Ein Tutorial zur Programmiersprache Dart umfasst oft auch deren Einsatz in der App-Entwicklung mit Flutter. Flutter ist ein UI-Toolkit von Google, mit dem sich plattformübergreifende Anwendungen mit nur einer Codebasis entwickeln lassen. Durch die Kombination mit Dart bietet Flutter eine performante Umgebung, um reaktionsschnelle und optisch ansprechende Apps zu gestalten. Wir betrachten die Grundlagen des Open-Source-Frameworks und zeigen, wie Flutter mit Dart zusammenarbeitet, welche Werkzeuge benötigt werden und wie eine App strukturiert wird.
Flutter ist ein Open-Source-Framework, das 2017 von Google veröffentlicht wurde. Es bietet eine einheitliche Umgebung zur Entwicklung von nativ kompilierten Anwendungen für Android, iOS, Web, Windows, macOS und Linux. Wichtige Merkmale von Flutter sind:
plattformübergreifende Entwicklung: ein einziger Code für verschiedene Plattformen
schnelle Entwicklung mit Hot Reload: Änderungen am Code werden sofort in der App reflektiert
moderne, flexible User-Interface-Gestaltung: dank eines widgetbasierten Systems
hohe Performance: verwendet eine eigene Rendering-Engine für native Geschwindigkeit
In diesem vierten Teil der Artikelserie über die Programmiersprache Dart beschäftigen wir uns mit dem Cross-Platform-Framework Flutter. Die Programmierung der Apps erfolgt in Dart.
Die Architektur (Abbildung 1) von Flutter ist so konzipiert, dass sie eine hohe Performance und eine konsistente Benutzererfahrung auf verschiedenen Plattformen ermöglicht. Im Zentrum steht die Dart-Engine, die den Flutter-Code in maschinennahe Sprache übersetzt. Dies geschieht durch Ahead-of-Time(AOT)-Kompilierung für Produktionsanwendungen, was eine nahezu native Ausführungsgeschwindigkeit ermöglicht. Während der Entwicklung nutzt Flutter hingegen die Just-in-Time(JIT)-Kompilierung, die in Kombination mit Hot Reload schnelle Änderungen am Code erlaubt, ohne die gesamte Anwendung neu starten zu müssen. Ein weiteres zentrales Element ist die Skia-Rendering-Engine, die die Benutzeroberfläche direkt auf den Bildschirm zeichnet, anstatt plattformspezifische User-Interface-Komponenten zu verwenden. Dies unterscheidet Flutter von Frameworks wie React Native oder .NET MAUI, die sich auf native User-Interface-Elemente stützen. Skia sorgt dafür, dass Animationen flüssig dargestellt werden und ermöglicht eine einheitliche Optik und Performance, unabhängig vom zugrunde liegenden Betriebssystem.
Das Dart-Framework bildet die oberste Schicht der Architektur und enthält eine umfangreiche Sammlung vorgefertigter Widgets für die Gestaltung moderner Benutzeroberflächen. Diese Widgets sind vollständig anpassbar und orientieren sich entweder an Material Design (Google) oder Cupertino (Apple), sodass eine native Nutzererfahrung auf Android- und iOS-Geräten gewährleistet wird. Zudem ist das Framework so strukturiert, dass Entwickler eigene Widgets einfach erstellen und wiederverwenden können, was die Wartung und Skalierbarkeit von Apps erleichtert.
Abbildung 1: Flutter Architektur-Layers [1]
Welche Rolle spielt Dart bzw. welche Vorteile bringt der Einsatz dieser Programmiersprache bei der App-Entwicklung? Dart wird nativ kompiliert, wodurch Flutter-Apps oft schneller sind als andere Cross-Platform-Technologien. Durch eine asynchrone Programmierung bietet Dart Futures, Streams und async/await, um reaktive Anwendungen zu ermöglichen. Es wird die objektorientierte Entwicklung mit Klassen, Vererbung, Interfaces und Mixins unterstützt, was eine saubere Code-Struktur ermöglicht. Die Effizienz der Entwicklung gilt insgesamt durch das bereitgestellte Tooling als gut und ist für produktionsreife Apps geeignet.
Um mit Flutter zu entwickeln, muss die Entwicklungsumgebung entsprechend eingerichtet werden. Dies umfasst die Installation des Flutter SDK, das bereits das Dart SDK enthält, sowie die Einrichtung notwendiger Entwicklungswerkzeuge und Emulatoren. Flutter unterstützt die Betriebssysteme Windows, macOS, Linux und Web-Applikationen, sodass eine plattformübergreifende Entwicklung problemlos möglich ist. Zusätzlich stellt Flutter verschiedene CLI-Tools bereit, die Entwicklern helfen, die Installation zu überprüfen, Abhängigkeiten zu verwalten und Projekte effizient zu steuern.
Die Installation von Flutter unterscheidet sich je nach Betriebssystem, folgt jedoch einem ähnlichen Ablauf. Das Flutter SDK kann direkt von der offiziellen Webseite [2] heruntergeladen werden. Wir erläutern die Installation für Windows und macOS. Die Dokumentation beschreibt darüber hinaus das Vorgehen für Linux-Distributionen und Chrome OS.
Windows:
Download: Das Flutter-SDK als ZIP-Datei von der Website herunterladen.
Entpacken: Das SDK an einem Speicherort wie C:\flutter ablegen.
Umgebungsvariablen setzen: Den Pfad C:\flutter\bin zur Umgebungsvariable Path hinzufügen.
Abhängigkeiten installieren: Android Studio oder Visual Studio Code für die Entwicklung nutzen.
Installation überprüfen: Den Befehl flutter doctor im Terminal ausführen und ggf. auf der Basis der ausgewiesenen Meldungen Installationen und Konfigurationen anpassen.
macOS:
Installation über Homebrew:
brew install --cask flutter
Manuelle Installation: Alternativ kann das SDK als ZIP-Datei heruntergeladen und in /Users/<benutzername>/flutter entpackt werden.
Umgebungsvariablen setzen: Den Pfad zur flutter/bin-Ordner der Datei .zshrc oder .bash_profile hinzufügen.
Xcode installieren: Das kann über den App Store geschehen.
Abhängigkeiten prüfen:
flutter doctor
Nach der Installation stehen verschiedene Werkzeuge zur Verfügung, die für die Entwicklung mit Flutter und Dart benötigt werden. Das Flutter SDK enthält alles, was für die Entwicklung von Flutter-Apps benötigt wird, darunter die Flutter-Bibliotheken, Tools zur Paketverwaltung und eine eigene Rendering-Engine. Das Dart SDK ist direkt im Flutter SDK enthalten und ermöglicht die Entwicklung in der Sprache Dart. Es enthält einen Compiler, die Standardbibliotheken und Entwicklungswerkzeuge. Flutter bietet leistungsfähige Command Line Interface(CLI)-Tools, um Projekte zu verwalten. Einige wichtige Befehle:
flutter doctor: Überprüft die Installation und zeigt fehlende Komponenten an. Sie führen auf der Kommandozeile lediglich folgenden Befehl aus:
flutter doctor
flutter create <projektname>: Erstellt ein neues Flutter-Projekt. Ein Beispiel:
flutter create my_app
flutter run: Startet die Anwendung in einem Emulator oder auf einem verbundenen Gerät
flutter run
flutter pub get: Lädt Abhängigkeiten für ein Flutter-Projekt:
flutter pub get
flutter build: Erstellt eine lauffähige APK- oder iOS-App für die Veröffentlichung.
flutter build
Nach der Installation können wir direkt mit dem Erstellen eines ersten Flutter-Projekts beginnen. Das kann über die Kommandozeile erfolgen:
flutter create my_first_app
cd my_first_app
Um das Projekt im Android-Emulator oder iOS-Simulator auszuführen, muss dieser installiert und konfiguriert werden, dann kann man die App von der Kommandozeile aus starten:
flutter run
Alternativ kann man die App auch aus der Entwicklungsumgebung Android Studio bzw. Visual Studio Code heraus beispielsweise als Web-Anwendung im aktuellen Browser ausführen (Abbildung 2).
Abbildung 2: Hello World App (Webbrowser)
Untersuchen wir im Folgenden die Projektstruktur einer Flutter-App. Nach dem Erstellen eines Flutter-Projekts enthält der Projektordner verschiedene Verzeichnisse und Dateien:
my_first_app/: Hauptprojektordner
android/: Native Android-Projektstruktur
ios/: Native iOS-Projektstruktur
lib/: Hauptverzeichnis für Dart-Code
main.dart: Einstiegspunkt der App
test/: Unit-Tests für die App
assets/: Bilder, Schriftarten etc.
pubspec.yaml: Konfigurationsdatei für Abhängigkeiten und Ressourcen
README.md: Dokumentation zur App.
Der Einstiegspunkt einer Flutter-App ist die main()-Funktion, die in der Datei lib/main.dart definiert ist. Diese Funktion dient als Ausgangspunkt für die Ausführung der Anwendung. Innerhalb der main()-Funktion wird in der Regel die runApp()-Funktion aufgerufen, die das Haupt-Widget der Anwendung in den Widget-Baum einfügt und die Ausführung der Flutter-App startet. Ein Beispiel für eine minimale Flutter-App sieht wie folgt aus:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Flutter App')),
body: Center(child: Text('Hallo, Flutter!')),
),
);
}
}
Sehen wir uns diese Zeilen des Quellcodes Schritt für Schritt an. Der Code definiert eine einfache Flutter-App mit einer App-Leiste (AppBar) und einem zentrierten Text („Hallo, Flutter!“). Die Anwendung basiert auf Material Design, dem User-Interface-Framework von Google.
Import der Flutter-Bibliothek:
import 'package:flutter/material.dart';
Das importiert die Material-Design-Bibliothek von Flutter.
Diese Bibliothek enthält eine Vielzahl vorgefertigter Widgets für User-Interface-Elemente wie Buttons, Texte, Layouts und vieles mehr.
Alle Flutter-Apps basieren auf Widgets aus diesem oder einem anderen User-Interface-Framework (Cupertino für iOS-Designs).
Die main-Funktion – Einstiegspunkt der App:
void main() {
runApp(MyApp());
}
Dies ist der Einstiegspunkt jeder Dart- und Flutter-Anwendung.
Die main-Funktion wird als Erstes ausgeführt, wenn die App startet.
Die Funktion runApp() startet die Anwendung und übergibt das Haupt-Widget (MyApp).
MyApp ist das Haupt-Widget der App, das die gesamte Benutzeroberfläche definiert.
Das Haupt-Widget MyApp (eine StatelessWidget-Klasse):
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Flutter App')),
body: Center(child: Text('Hallo, Flutter!')),
),
);
}
}
MyApp ist ein Stateless Widget (unveränderliches Widget).
Es erbt von StatelessWidget, was bedeutet, dass es keinen internen Zustand hat.
@override: Überschreibt die Methode build(), welche das User Interface des Widgets definiert.
Das build-Methoden-Widget:
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Flutter App')),
body: Center(child: Text('Hallo, Flutter!')),
),
);
}
Diese Methode wird von Flutter aufgerufen, um das User Interface-Layout der App zu erstellen.
Sie gibt ein Widget zurück, das die Struktur der App definiert.
Das MaterialApp-Widget:
return MaterialApp(
home: Scaffold(
MaterialApp: Dies ist das Haupt-Widget für jede Flutter-App, die das Material-Design-Framework verwendet.
Es stellt grundlegende User-Interface-Funktionen bereit, wie Navigation, Themes und Lokalisierung.
Das home-Attribut gibt das Haupt-Widget der App an (hier Scaffold).
Das Scaffold-Widget – Das Grundgerüst der App
Scaffold(
appBar: AppBar(title: Text('Flutter App')),
body: Center(child: Text('Hallo, Flutter!')),
),
Scaffold: Stellt das Grundgerüst der App bereit; enthält typische Layout-Elemente wie die App-Leiste (AppBar) und den Body-Bereich.
appBar: AppBar(title: Text('Flutter App')): Erstellt eine App-Leiste mit dem Titel Flutter App. AppBar ist ein vordefiniertes Widget, das eine Titelleiste im Material-Design-Stil anzeigt.
body: Center(child: Text('Hallo, Flutter!')): Definiert den Hauptinhalt der App; Center ist ein Layout-Widget, das den enthaltenen Inhalt in der Mitte des Bildschirms platziert. Text('Hallo, Flutter!') zeigt eine einfache Textnachricht in der Mitte des Bildschirms an.
Dieser Code erstellt eine minimale Flutter-App mit folgenden Elementen: MaterialApp (als Container für die gesamte Anwendung); Scaffold (als Grundstruktur der Benutzeroberfläche); AppBar (für die obere Titelleiste mit Flutter App) und einen zentrierten Text (Text('Hallo, Flutter!')) als Hauptinhalt der App. Im nächsten Schritt erweitern wir dieses Hello-World-Beispiel. Wir ergänzen einen Button im Screen. Dazu modifizieren wir den Quellcode wie folgt:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Flutter App')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Hallo, Flutter!'),
SizedBox(height: 20), // Abstand zwischen den Widgets
ElevatedButton(
onPressed: () {
print('Button geklickt!');
},
child: Text('Klick mich!'),
),
],
),
),
),
);
}
}
Was wurde geändert?
Column-Widget wurde hinzugefügt, um Text und Button untereinander anzuordnen.
ElevatedButton wurde hinzugefügt, um Interaktion zu ermöglichen.
print('Button geklickt!') gibt beim Klicken eine Nachricht in der Konsole aus.
Dieser Code definiert eine einfache Flutter-App mit einer Titelleiste und einem Text. Die Struktur basiert auf Material...