Appearance
Kapitel 4: Flutter Kernkonzepte
4.1 Widget (Komponente)
Was ist ein Widget?
In Flutter ist alles ein Widget!
- UI-Elemente (Text, Button, Image) sind Widgets
- Layout-Elemente (Row, Column, Container) sind Widgets
- Gesamte Seiten sind Widgets
Widget-Baum (Widget Tree):
MyApp (Widget)
└── MaterialApp (Widget)
└── Scaffold (Widget)
├── AppBar (Widget)
└── Body (Widget)
├── Text (Widget)
└── Button (Widget)Widget-Klassifizierung
1. StatelessWidget (Zustandslose Komponente)
Eigenschaften:
- ✅ Unveränderlich (immutable)
- ✅ Kein interner Zustand
- ✅ Wird nur neu gebaut, wenn externe Daten sich ändern
- ✅ Gut für statische Inhalte
dart
class MeinText extends StatelessWidget {
final String text; // Final = kann nicht geändert werden
const MeinText(this.text, {super.key}); // Konstruktor
@override
Widget build(BuildContext context) {
return Text(
text,
style: TextStyle(fontSize: 20),
);
}
}
// Verwendung:
MeinText('Hallo Welt') // Zeigt Text an, ändert sich nieAnwendungsfälle für StatelessWidget:
- Texte, Bilder, Icons
- Statische Menüs
- About-Seiten
- Header/Footer
2. StatefulWidget (Zustandsbehaftete Komponente)
Eigenschaften:
- ✅ Hat einen veränderlichen Zustand (State)
- ✅ UI aktualisiert sich bei Zustandsänderung (durch
setState()) - ✅ Gut für interaktive Inhalte
dart
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _zaehler = 0; // Zustandsvariable
void _erhoehen() {
setState(() { // WICHTIG: setState() aktualisiert UI
_zaehler++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('Zähler: $_zaehler'),
ElevatedButton(
onPressed: _erhoehen,
child: Text('Erhöhen'),
),
],
);
}
}
// Verwendung:
Counter() // Zeigt Zähler an, der sich ändertAnwendungsfälle für StatefulWidget:
- Formulareingaben
- Animations-Steuerung
- Zähler, Timer
- API-Datenabruf
Unterschied: Stateless vs Stateful
| Merkmal | StatelessWidget | StatefulWidget |
|---|---|---|
| Zustand | Kein Zustand | Hat Zustand (State) |
| Veränderlichkeit | Unveränderlich | Veränderlich |
| Performance | Besser (weniger Overhead) | Schlechter (Rebuilding) |
| Komplexität | Einfacher | Komplexer |
| Anwendung | Statische Inhalte | Interaktive Inhalte |
Entscheidungshilfe:
Brauche ich interaktive Elemente?
├── NEIN → StatelessWidget
└── JA → StatefulWidget
├── Einfache Interaktion → StatefulWidget
└── Komplexe Zustandsverwaltung → Provider/Bloc (später)4.2 BuildContext (Kontext)
Was ist BuildContext?
BuildContext ist der Standort eines Widgets im Widget-Baum.
dart
Widget build(BuildContext context) { // ← Das ist der BuildContext!
return Container();
}Wofür wird BuildContext verwendet?
- Auf übergeordnete Widgets zugreifen
dart
// Theme abrufen (von übergeordnetem MaterialApp-Widget)
final farbe = Theme.of(context).primaryColor;
// MediaQuery abrufen (Bildschirmgröße)
final breite = MediaQuery.of(context).size.width;- Routing (Seitennavigation)
dart
// Zu einer neuen Seite navigieren
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => NeueSeite()),
);- Snackbar anzeigen
dart
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Hallo!')),
);Wichtig für Anfänger:
- ✅
contextist immer verfügbar in derbuild()-Methode - ✅
contextwird verwendet, um auf übergeordnete Widgets zuzugreifen - ❌ Nicht in asynchronen Operationen nach
awaitverwenden (Gefahr von "Unmounted Widget" Fehler)
dart
// RICHTIG: Context vor await verwenden
Future<void> ladeDaten() async {
final navigator = Navigator.of(context); // Context speichern
await Future.delayed(Duration(seconds: 2));
navigator.push(...); // Navigator verwenden
}
// FALSCH: Context nach await verwenden (Widget könnte bereits zerstört sein)
Future<void> ladeDaten() async {
await Future.delayed(Duration(seconds: 2));
Navigator.of(context).push(...); // FEHLER: Context könnte ungültig sein!
}4.3 Hot Reload & Hot Restart
Hot Reload (r)
Was es tut:
- ✅ Aktualisiert UI sofort
- ✅ Behält App-Zustand bei (Variablen, Daten)
- ✅ Funktioniert nur im Debug-Modus
- ✅ Sehr schnell (Millisekunden)
Wann verwenden:
- UI-Änderungen (Farben, Texte, Layout)
- Widget-Struktur ändern
- Styling anpassen
Einschränkungen:
- ❌ Keine Änderungen an
main()oderinitState() - ❌ Keine statische Variablen-Änderungen
- ❌ Keine Änderungen an Enums oder Typen
dart
// Beispiel: Hot Reload funktioniert
// Ändern Sie dies und speichern Sie (r drücken):
Text('Hallo') → Text('Welt')
// BEISPIEL: Hot Reload funktioniert NICHT (Hot Restart R benötigt)
int zaehler = 0; // Statische Variable
// Ändern zu: int zaehler = 100; → Hot Reload reicht nicht!Hot Restart (R)
Was es tut:
- ✅ Startet App neu
- ✅ Setzt App-Zustand zurück
- ✅ Etwas langsamer als Hot Reload
Wann verwenden:
- Änderungen an
main() - Änderungen an
initState() - Statische Variablen ändern
- Enums oder Typen ändern
Full Restart
Was es tut:
- ✅ Stoppt und startet App komplett neu
- ✅ Erforderlich bei Änderungen an nativen Dateien
Wann verwenden:
- Änderungen an
AndroidManifest.xmloderInfo.plist - Neue Plugins hinzufügen (
pubspec.yamlÄnderungen) - Native Code-Änderungen
4.4 Flutter-Rendering (vereinfacht)
3 Bäume in Flutter
Flutter verwendet drei Bäume für das Rendering:
1. Widget Tree (Widget-Baum)
└── Beschreibung der UI (Konfiguration)
"Ich möchte einen blauen Button mit Text 'Klick'"
2. Element Tree (Element-Baum)
└── Verbindung zwischen Widget und Render-Baum
Verwaltet die Lebensdauer von Widgets
3. Render Tree (Render-Baum)
└── Tatsächliches Rendering (Größe, Position, Malen)
Berechnet: Wo? Wie groß? Welche Farbe?Vereinfachter Ablauf:
1. Sie schreiben Widgets (Widget Tree)
↓
2. Flutter erstellt Elemente (Element Tree)
↓
3. Flutter berechnet Layout & Malen (Render Tree)
↓
4. Benutzer sieht UI auf dem BildschirmWarum das wichtig ist:
- ✅ Durch Hot Reload: Nur betroffene Widgets werden neu gebaut
- ✅ Durch
setState(): Nur dieser Teilbaum wird neu gerendert - ✅ Performance: Flutter ist schnell, weil es die Bäume intelligent verwaltet
Vereinfachtes Beispiel:
dart
// Widget Tree (Ihre Code)
Container(
color: Colors.blue,
child: Text('Hallo'),
)
// Element Tree (Flutter intern)
ContainerElement
└── TextElement
// Render Tree (Flutter intern)
RenderPositionedBox (Layout)
└── RenderParagraph (Text rendern)4.5 Praxisbeispiel: Widgets erstellen & Hot Reload testen
Schritt 1: StatelessWidget erstellen
dart
import 'package:flutter/material.dart';
class Begruessung extends StatelessWidget {
final String name;
const Begruessung(this.name, {super.key});
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue.shade100,
borderRadius: BorderRadius.circular(8),
),
child: Text(
'Hallo, $name!',
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
);
}
}Schritt 2: StatefulWidget erstellen
dart
class ZaehlerWidget extends StatefulWidget {
const ZaehlerWidget({super.key});
@override
State<ZaehlerWidget> createState() => _ZaehlerWidgetState();
}
class _ZaehlerWidgetState extends State<ZaehlerWidget> {
int _zaehler = 0;
void _erhoehen() {
setState(() {
_zaehler++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Text('Zähler: $_zaehler', style: const TextStyle(fontSize: 32)),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _erhoehen,
child: const Text('Erhöhen'),
),
],
);
}
}Schritt 3: In main.dart verwenden
dart
// In Ihrer main.dart, in der build()-Methode:
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Begruessung('Max'), // StatelessWidget
const SizedBox(height: 40),
ZaehlerWidget(), // StatefulWidget
],
),
),Schritt 4: Hot Reload testen
- App ausführen (
flutter run) - Ändern Sie
Colors.blue.shade100zuColors.green.shade100 - Speichern Sie die Datei (
Ctrl + S) - Beobachten Sie: Farbe ändert sich sofort!
Schritt 5: StatefulWidget testen
- Klicken Sie auf "Erhöhen" → Zähler erhöht sich
- Ändern Sie
Text('Zähler: $_zaehler')zuText('Wert: $_zaehler') - Hot Reload (r) → Text ändert sich, aber Zählerwert bleibt erhalten!
- Hot Restart (R) → Zähler wird auf 0 zurückgesetzt
Zusammenfassung
In diesem Kapitel haben Sie:
- ✅ Verstanden, dass alles in Flutter ein Widget ist
- ✅ Den Unterschied zwischen StatelessWidget und StatefulWidget gelernt
- ✅ BuildContext verstanden (Kontext im Widget-Baum)
- ✅ Hot Reload & Hot Restart meisterhaft eingesetzt
- ✅ Das Flutter-Rendering (3 Bäume) vereinfacht verstanden
- ✅ Praxisbeispiele erstellt und Hot Reload getestet
Nächstes Kapitel: Wir werden Flutter-Basis-Widgets lernen (Text, Image, Button, TextField).
Übungsaufgaben:
- Erstellen Sie ein
StatelessWidget, das ein Profilbild (CircleAvatar) anzeigt - Erstellen Sie ein
StatefulWidget, das eine To-Do-Liste mit Hinzufügen-Button implementiert - Testen Sie Hot Reload: Ändern Sie Farben, Texte und beobachten Sie die sofortige Aktualisierung
- Testen Sie Hot Restart: Ändern Sie eine Initialisierungsvariable und beobachten Sie den Unterschied
Häufige Fehler:
- ❌
setState()vergessen → UI aktualisiert sich nicht - ❌ BuildContext nach
awaitverwenden → "Unmounted Widget" Fehler - ❌ Zustand in
StatelessWidgetspeichern wollen → Verwenden SieStatefulWidget! - ❌ Hot Reload erwarten, wenn
main()geändert wurde → Verwenden Sie Hot Restart
