Flutter State-Management
Die Zustandsverwaltung ist ein Kernkonzept in der Flutter-Entwicklung.
In diesem Abschnitt werden die in Flutter integrierten Ansätze zur Zustandsverwaltung vorgestellt, darunter setState, Provider und ChangeNotifier.
Was ist Zustandsverwaltung?
State-Management bezeichnet den Mechanismus zur Verwaltung von Datenänderungen und UI-Aktualisierungen in einer Anwendung. Wenn sich Daten ändern, muss die UI entsprechend aktualisiert werden; genau dieses Problem löst State-Management.
| State-Typen | Beschreibung | Beispiele |
|---|---|---|
| Lokaler State | State innerhalb eines einzelnen Widgets | Text im Eingabefeld, Animationsfortschritt |
| Geteilter State | State, der über mehrere Widgets hinweg geteilt werden muss | Benutzer-Login-Informationen, Themes |
| Angewendeter Status | State, auf den die gesamte Anwendung zugreifen muss | Inhalt des Warenkorbs, Benutzerkonfiguration |
Lokaler State - setState
Für einfachen lokalen State reicht die Verwendung von StatefulWidget und setState aus.
Beispiel: Zähler-Beispiel
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
// Lokaler Zustand
int _count = 0;
// Zähler erhöhen
void _increment() {
setState(() {
_count++;
});
}
// Zähler verringern
void _decrement() {
setState(() {
_count--;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(Anzahl: $_count, style: const TextStyle(fontSize: 32)),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(onPressed: _decrement, child: const Text('-')),
const SizedBox(width: 20),
ElevatedButton(onPressed: _increment, child: const Text('+')),
],
),
],
);
}
}
Provider-Zustandsverwaltung
Provider ist eine der offiziell von Flutter empfohlenen Lösungen zur Zustandsverwaltung und eignet sich für die Verwaltung von Zuständen, die über Komponenten hinweg geteilt werden.
Provider installieren
Füge die Abhängigkeit in pubspec.yaml hinzu:
dependencies: provider: ^6.0.0
ChangeNotifier-Muster
Beispiel: Provider + ChangeNotifier
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
// Benachrichtige Listener, dass sich der Zustand geändert hat
notifyListeners();
}
void decrement() {
_count--;
notifyListeners();
}
}
// 2. Daten oben in der App bereitstellen
void main() {
runApp(
// CounterModel für untergeordnete Widgets bereitstellen
ChangeNotifierProvider(
create: (context) => CounterModel(),
child: const MyApp(),
),
);
}
// 3. Daten im Widget verwenden
class CounterDisplay extends StatelessWidget {
const CounterDisplay({super.key});
@override
Widget build(BuildContext context) {
// CounterModel lesen
final counter = context.watch<CounterModel>();
return Column(
children: [
Text('Anzahl: ${counter.count}', style: const TextStyle(fontSize: 32)),
ElevatedButton(
onPressed: () => counter.increment(),
child: const Text('Erhöhen'),
),
],
);
}
}
Multi-Provider-Komposition
Beispiel: Verwendung mehrerer Provider
runApp(
MultiProvider(
providers: [
// Provider für Benutzerinformationen
ChangeNotifierProvider(create: (_) => UserModel()),
// Theme-Provider
ChangeNotifierProvider(create: (_) => ThemeModel()),
// Warenkorb-Provider
ChangeNotifierProvider(create: (_) => CartModel()),
],
child: const MyApp(),
),
);
}
// Verwende context.read, wenn du nicht auf Änderungen hören musst.
class SomeWidget extends StatelessWidget {
const SomeWidget({super.key});
@override
Widget build(BuildContext context) {
// Lesen, aber nicht lauschen
final user = context.read<UserModel>();
return ElevatedButton(
onPressed: () => user.logout(),
child: const Text('Abmelden'),
);
}
}
Zustand nach oben heben
Wenn mehrere untergeordnete Komponenten Zustand gemeinsam nutzen müssen, kann der Zustand zur nächstgelegenen gemeinsamen Elternkomponente nach oben gehoben werden.
Beispiel: Zustand nach oben heben
class ParentWidget extends StatefulWidget {
const ParentWidget({super.key});
@override
State<ParentWidget> createState() => _ParentWidgetState();
}
class _ParentWidgetState extends State<ParentWidget> {
// Gemeinsamer Zustand
bool _isEnabled = false;
// Callback-Methode
void _toggleEnabled() {
setState(() {
_isEnabled = !_isEnabled;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
// Kindkomponente A
ChildWidgetA(
isEnabled: _isEnabled,
),
// Kindkomponente B
ChildWidgetB(
isEnabled: _isEnabled,
onToggle: _toggleEnabled,
),
],
);
}
}
// Kindkomponente A (zeigt nur den Zustand an)
class ChildWidgetA extends StatelessWidget {
final bool isEnabled;
const ChildWidgetA({super.key, required this.isEnabled});
@override
Widget build(BuildContext context) {
return Text('Status: ${isEnabled ? "Aktiviert" : "Deaktiviert"}');
}
}
// Kindkomponente B (steuert den Zustand)
class ChildWidgetB extends StatelessWidget {
final bool isEnabled;
final VoidCallback onToggle;
const ChildWidgetB({
super.key,
required this.isEnabled,
required this.onToggle,
});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onToggle,
child: Text(isEnabled ? 'Schließen' : 'Aktivieren'),
);
}
}
Weitere ErweiterungenDie Wahl des State-Managements hängt vom Umfang der Anwendung ab: Einfache Apps verwenden setState, mittelgroße Apps verwenden Provider, und komplexe Apps können Lösungen wie Riverpod, BLoC oder GetX in Betracht ziehen.