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-TypenBeschreibungBeispiele
Lokaler StateState innerhalb eines einzelnen WidgetsText im Eingabefeld, Animationsfortschritt
Geteilter StateState, der über mehrere Widgets hinweg geteilt werden mussBenutzer-Login-Informationen, Themes
Angewendeter StatusState, auf den die gesamte Anwendung zugreifen mussInhalt des Warenkorbs, Benutzerkonfiguration

Lokaler State - setState

Für einfachen lokalen State reicht die Verwendung von StatefulWidget und setState aus.

Beispiel: Zähler-Beispiel

class CounterWidget extends StatefulWidget {
  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

// 1. Datenmodell definieren (von ChangeNotifier erben)
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

void main() {
  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

// Elternkomponente verwaltet den Zustand
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'),
    );
  }
}

Die 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.

Weitere Erweiterungen