Fortgeschrittenes Flutter
In diesem Abschnitt werden einige fortgeschrittene Flutter-Konzepte vorgestellt, die Ihnen helfen sollen, Ihre Entwicklungsfähigkeiten zu verbessern.
Rendering-Prinzipien
Flutter verwendet die Skia-Rendering-Engine (seit 3.0 wird schrittweise auf Impeller umgestellt), um die UI direkt zu zeichnen, ohne native Komponenten zu verwenden.
Widget-Baum und Element-Baum
- Widget-Baum: Konfigurationsinformationen, die die UI beschreiben (unveränderlich)
- Element-Baum: Instanziiertes Objekt von Widget (veränderlich)
- RenderObject-Baum: Verantwortlich für das tatsächliche Rendering
Das Verständnis der Rendering-Prinzipien hilft, die UI-Performance zu optimieren und unnötige Neuaufbauten zu vermeiden.
InheritedWidget - Datenweitergabe
InheritedWidget ermöglicht es, Daten im Widget-Baum nach unten weiterzugeben, und untergeordnete Widgets können auf Daten von Vorfahren zugreifen.
Beispiel: Benutzerdefiniertes InheritedWidget
class MyData extends InheritedWidget {
final int value;
const MyData({
super.key,
required this.value,
required super.child,
});
// Komfortmethode: das nächstgelegene MyData abrufen
static MyData of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<MyData>()!;
}
// Bestimmen, ob das untergeordnete Widget neu aufgebaut werden muss
@override
bool updateShouldNotify(MyData oldWidget) {
return value != oldWidget.value;
}
}
// Verwendung
class ParentWidget extends StatelessWidget {
const ParentWidget({super.key});
@override
Widget build(BuildContext context) {
return MyData(
value: 42,
child: const ChildWidget(),
);
}
}
class ChildWidget extends StatelessWidget {
const ChildWidget({super.key});
@override
Widget build(BuildContext context) {
// MyData des Vorfahren abrufen
final myData = MyData.of(context);
return Text('Wert: ${myData.value}');
}
}
RepaintBoundary - Lokale Neuzeichnung
Die Verwendung von RepaintBoundary kann den Neuzeichnungsbereich einschränken und die Leistung verbessern.
Beispiel: Verwendung von RepaintBoundary
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: Container(
// Das Zeichnen in diesem Bereich wirkt sich nicht auf das Äußere aus
color: Colors.red,
child: const Text(Unabhängiger Bereich),
),
);
}
}
Tiefes Verständnis von Keys
Keys helfen Flutter, Widgets zu identifizieren und neue Widgets von vorhandenen Widgets zu unterscheiden.
ValueKey vs ObjectKey vs UniqueKey
| Type | Beschreibung |
|---|---|
| ValueKey | Werte als Keys verwenden (wie IDs, Zahlen, Strings) |
| ObjectKey | Objektverweise als Keys verwenden |
| UniqueKey | Bei jeder Erstellung, Zerstörung und Neuerstellung einen eindeutigen Key erzeugen |
CustomPainter - Benutzerdefiniertes Zeichnen
Mit CustomPainter kann vollständig benutzerdefiniertes Grafikzeichnen erreicht werden.
Beispiel: Verwendung von CustomPainter
const CustomPainterExample({super.key});
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: MyCirclePainter(),
size: const Size(200, 200),
);
}
}
class MyCirclePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// Einen Kreis zeichnen
final paint = Paint()
..color = Colors.blue
..style = PaintingStyle.fill;
final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2 - 10;
canvas.drawCircle(center, radius, paint);
// Einen Rahmen zeichnen
final borderPaint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 2;
canvas.drawCircle(center, radius, borderPaint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return false;
}
}
Stream und Future
Beispiel: Stream-Verwendung
Stream<int> countStream(int max) async* {
for (int i = 1; i <= max; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i; // Wert senden
}
}
// StreamBuilder verwenden
class StreamExample extends StatelessWidget {
const StreamExample({super.key});
@override
Widget build(BuildContext context) {
return StreamBuilder<int>(
stream: countStream(10),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(Anzahl: ${snapshot.data});
} else if (snapshot.hasError) {
return Text(Error: ${snapshot.error});
}
return const CircularProgressIndicator();
},
);
}
}
Zusammenfassung
Dieses einführende Flutter-Tutorial behandelt die folgenden Themen:
- Flutter-Installation und Umgebungskonfiguration
- Widget-Grundlagen (StatelessWidget und StatefulWidget)
- Layout-System (Row, Column, Stack)
- Verarbeitung von Benutzereingaben
- State-Management (setState, Provider)
- Netzwerkanfragen und Datenspeicherung
- Navigation und Routing
- Testen und Veröffentlichung
Für weiterführendes Lernen folgen Sie:
- BLoC-Muster
- Riverpod-Zustandsverwaltung
- GetX-Routing und Zustandsverwaltung
- Flutter-Leistungsoptimierung
- Flutter-Web- und Desktop-Entwicklung