Bild von Einfache App-Entwicklung dank Cross-Platform Mobile Development mit Flutter

15. Mai 2025

Einfache App-Entwicklung dank Cross-Platform Mobile Development mit Flutter

Suchen Sie nach einer effizienten Lösung für plattformübergreifende App-Entwicklung? Mit Flutter kann eine leistungsstarke ChatGPT-Chat-App erstellt werden. Profitieren Sie von einer verkürzten Entwicklungszeit und einer einzigen Codebasis für alle Plattformen.

In einem Workshop erhielten wir spannende Einblicke in die Welt der plattformübergreifenden Entwicklung von Anwendungen.  Geleitet wurde der Workshop von Judith Gull, Senior Softwareentwicklerin bei  Comerge . Der Fokus lag darauf, praxisorientiert den Umgang mit Flutter zu erlernen, um eine kleine ChatGPT-Chat-App zu entwickeln. 

Ausgangslage

Wenn wir eine Applikation für verschiedene Plattformen entwickeln, stehen uns mehrere Ansätze zur Verfügung:

1. Native Entwicklung

Dabei wird eine separate Codebase für jede Plattform erstellt (z. B. Swift für iOS und Kotlin/Java für Android). Dieser Ansatz bietet hervorragende Performance und Zugriff auf alle plattformspezifischen APIs sowie Funktionen. Allerdings ist er zeitaufwändig und kostspielig, da jede Plattform separat entwickelt und gewartet werden muss.

2. Cross-Plattform Entwicklung

Hier wird eine einzige Codebase genutzt, die für alle Plattformen funktioniert. Es gibt verschiedene Ansätze, die sich wie folgt unterscheiden:

Hybrid (Web View)

Eine Applikation, die aus HTML, CSS und JavaScript besteht, wird in einen nativen Container verpackt. Dieser Container ermöglicht den Zugriff auf plattformspezifische Funktionen. Updates können unabhängig vom jeweiligen App-Store durchgeführt werden. Die Performance leidet jedoch häufig, da die Anwendung letztlich über eine Web View (im Grunde ein eingebetteter Browser) läuft.

Ein Beispiel hierfür ist Apache Cordova .

Native Komponenten

Eine Applikation nutzt eine JavaScript-Brücke, um native UI-Elemente zu rendern. Dadurch werden die Vorteile von plattformunabhängigem Code und nativen Elementen kombiniert. Die Performance ist im Vergleich zu hybriden Ansätzen besser, jedoch nicht ganz auf dem Niveau einer echten nativen Entwicklung.

Ein Beispiel für diesen Ansatz ist React Native .

Kompilierung in nativen Code

Der Code der Applikation wird in echten nativen Code kompiliert, was nahezu die gleiche Performance wie bei einer nativen Entwicklung ermöglicht.

Ein bekanntes Beispiel hierfür ist Flutter .

Flutter

Im Workshop haben wir uns Flutter genauer angesehen. Flutter ist ein von Google entwickeltes Open-Source-UI-Framework, das Dart als Programmiersprache nutzt. Es basiert auf einer eigenen Engine, die direkt Pixel auf dem Zielgerät rendern kann. Der grundlegende Baustein in Flutter ist das Widget (vergleichbar mit einer React-Komponente).

Flutter ist in drei Ebenen strukturiert:

  1. Framework-Ebene: Entwickler verwenden diese Ebene, um die Benutzeroberfläche (UI) und Logik ihrer Anwendungen zu definieren.
  2. Flutter-Engine-Ebene: Die Engine kümmert sich um Low-Level-Aufgaben wie die Darstellung von Grafiken und Animationen sowie den Umgang mit der Hardware.
  3. Plattformspezifische Embedded-Ebene: Diese Ebene kommuniziert mit dem Betriebssystem, um Zugriff auf Eingabegeräte, Rendering-Bereiche und andere plattformspezifische Funktionalitäten zu ermöglichen.

Widgets

Ein Widget beschreibt deklarativ einen Teil der Benutzeroberfläche (UI). Widgets können entweder visuell sein, wie z. B. das Text-Widget, das Text rendert, oder funktional, wie das Padding-Widget, das einen Abstand hinzufügt. In Flutter ist alles ein Widget.

Typischerweise besteht ein Widget aus einer Vielzahl kleinerer, single-purpose Widgets (Widgets mit einem einzigen Zweck). Im Gegensatz zu herkömmlichen Webframeworks ist das Styling, wie z. B. Padding, Farbe usw., ebenfalls ein Widget. Es ist kein CSS notwendig, da alles direkt durch Widgets gesteuert wird.

Ein wichtiger Punkt ist, dass Widgets in Flutter unveränderlich sind. Das bedeutet: Bei Änderungen in der Benutzeroberfläche werden neue Widgets erstellt und instanziiert, anstatt bestehende Widgets zu modifizieren.

Unterschiede zwischen Widget-Typen:

Stateless Widgets: Diese Widgets haben keinen veränderbaren Zustand (State). Beispiele hierfür sind Widgets wie Text oder Image.

Stateful Widgets: Diese Widgets besitzen einen veränderbaren Zustand (mutable State), wie bspw. eine Checkbox, ein TextField oder interaktive Komponenten.

Wenn sich der State eines Stateful Widgets ändert, wird nur das betroffene Widget neu aufgebaut, nicht die gesamte Applikation.

Ein Widget für eine Chatnachrichtenblase kann man wie folgt implementieren:

import 'package:flutter/material.dart';
import 'package:flutter_training_demo/bloc/chat_message.dart';
class ChatBubble extends StatelessWidget {
  const ChatBubble({super.key, required this.content});
  final ChatMessage content;
  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment:
          content.isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
      children: [
        Container(
          margin: const EdgeInsets.all(4),
          decoration: BoxDecoration(
            color: content.isUser ? Colors.blue[100] : Colors.grey[300],
            borderRadius: BorderRadius.circular(20),
          ),
          padding: const EdgeInsets.all(8),
          child: Text(content.text),
        ),
      ],
    );
  }
}

Welches Chatnachrichten wie folgt rendert:

Wie im Code zu sehen ist, handelt es sich bei der ChatBubble um ein Stateless Widget, das den Inhalt über seinen Konstruktor erhält.

Der Rendering-Prozess findet in der build()-Funktion statt. Die Nachrichten werden innerhalb des Widgets als Spalte (Column) dargestellt, wobei zwischen gesendeten und empfangenen Nachrichten unterschieden wird. Gesendete Nachrichten werden rechts, empfangene links platziert.

Für jede Chat-Bubble erstellen wir einen Container, der mit einem Margin von 4px und einem Padding von 8px versehen ist. Der Hintergrund des Containers wird farblich unterschiedlich gestaltet – blau für gesendete und grau für empfangene Nachrichten. Anschließend wird der Inhalt der Chat-Nachricht innerhalb des Containers dargestellt.

Eine Chat-Applikation benötigt ebenfalls ein Eingabefeld, um neue Nachrichten zu verfassen. Diese Aufgabe wird von einem Stateful Widget übernommen, da sich der Inhalt des Eingabefeldes während der Benutzung ändert.

import 'package:flutter/material.dart';
class MessageInput extends StatefulWidget {
  const MessageInput({super.key, required this.onAddMessage});
  final void Function(String) onAddMessage;
  @override
  State<MessageInput> createState() => _MessageInputState();
}
class _MessageInputState extends State<MessageInput> {
  final TextEditingController _controller = TextEditingController();
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Padding(
      // to avoid textfield being hidden by keyboard
      padding: EdgeInsets.only(
        bottom: MediaQuery.of(context).viewInsets.bottom,
      ),
      child: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Row(
          children: [
            Expanded(
              child: TextField(
                controller: _controller,
                decoration: const InputDecoration(hintText: 'Type something'),
                onChanged: (_) {
                  setState(() {}); // Trigger rebuild to update IconButton state
                },
              ),
            ),
            IconButton(
              icon: Icon(
                Icons.send,
                color:
                    _controller.text.isEmpty ? Colors.grey : Colors.deepPurple,
              ),
              onPressed:
                  _controller.text.isEmpty
                      ? null // disabled button
                      : () {
                        widget.onAddMessage(_controller.text);
                        _controller.clear();
                      },
            ),
          ],
        ),
      ),
    );
  }
}

Hier benötigen wir zwei Klassen: sowohl die Klasse MessageInput, welche die createState() Funktion überschreibt, als auch die private Klasse _MessageInputState, welche sich um den Rendering- und State-Prozess kümmert.

Die Klasse _MessageInputState besitzt einen TextEditingController, um den Benutzereingaben aus dem Textfeld zu erhalten und zu verwalten.

Mit diesen beiden Klassen wird das Eingabefeld für eine Chat-Applikation erstellt. Die übergeordnete Stateful-Klasse MessageInput kümmert sich um die Verwaltung des States durch die createState() Methode, während sich die private Klasse _MessageInputState um das Rendering und die Interaktion mit Benutzereingaben kümmert. Wenn der User die Nachricht sendet wird das onAddMessage Callback im MessageInput ausgelöst.

Layout

«Constraints go down. Sizes go up. Parent sets the position.» ist das Layout-Mantra in Flutter.

Ein Widget erhält vier Constraints von seinem Parent: die minimale und maximale Breite sowie die minimale und maximale Höhe. Das Widget entscheidet anschliessend, wie groß es sein möchte, und sendet diese Entscheidung zurück an den Parent. Der Parent wiederum berücksichtigt die Größe des Widgets und sendet entsprechende Constraints an das nächste Child. Schliesslich positioniert der Parent alle Children.

Die Applikation wird dadurch im Render Tree Depth-First aufgebaut: Das Root-Widget gibt die Bildschirmgröße als Constraint vor, und die Kinder passen sich entsprechend an. Durch diese Constraints ist es schwierig, einen unbeabsichtigten Overflow zu erzeugen, da sich die Kinder in den meisten Fällen an die Constraints halten.

Ein Kind-Widget weiss auch nicht, wo es positioniert wird – dies wird ausschliesslich vom Parent entschieden.

Mein Teilnehmer-Fazit 

Flutter hat sich in diesem Workshop als leistungsstarkes und vielseitiges Framework für die plattformübergreifende Entwicklung erwiesen. Besonders interessant fand ich, dass kein CSS benötigt wird und alles mithilfe von Widgets gesteuert werden kann. Das Layout-System macht nach einer kurzen Lernphase ebenfalls grossen Spass und bietet viel Flexibilität. Für alle, die sich für die Gestaltung von Benutzeroberflächen interessieren und einmal abseits der gängigen JavaScript-Frameworks entwickeln möchten, kann ich Flutter wärmstens empfehlen.

Cudos entwickelt Web- und Mobile-Apps

Wir entwickeln massgeschneiderte Web- und plattformübergreifende Mobile-Apps, die durch eine hohe Performance und ein ansprechendes Design überzeugen.  Unsere Lösungen für Web, iOS und Android sind optimal auf Ihre Anforderungen abgestimmt. Erfahren Sie mehr dazu auf unserer Angebotsseite .


Schliessen
Stamp Icon-Print Icon-Clear
S
M
L
XL
XXL