What state management options do you know in Flutter, and when would you use each one?
Show answer — try answering out loud first
The most common are: setState for local state, Provider for simple shared state, Riverpod as an evolution of Provider that's safer and more testable, and Bloc/Cubit for business logic based on well-defined events and states. There's no single "best" one: it depends on the size of the project and the complexity of the state.
"State" is any data that changes and affects the UI. The key question is who needs that data:
setState: a widget's local state (a counter, a toggle). Simple and enough for what lives in a single widget.Provider: exposes objects to a subtree and notifies changes (usesChangeNotifier+notifyListeners()). Good for shared state in small and medium apps. Built on top ofInheritedWidget.Riverpod: similar to Provider but without depending onBuildContextto read, with better type safety and easier to test. Very popular in new projects.Bloc/Cubit: separates the logic from the UI through events that come in and states that go out. Scales well in large apps and teams, at the cost of more code.
Rule: start simple (setState) and level up only when shared state and complexity justify it.
import 'package:flutter/material.dart';
// ChangeNotifier pattern (the foundation of Provider)
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners(); // notifies the widgets that are listening
}
}
// In a real project, with the provider package, you'd use it like this:
//
// ChangeNotifierProvider(
// create: (_) => CounterModel(),
// child: MyApp(),
// );
//
// And inside a widget:
// final model = context.watch<CounterModel>();
// Text('${model.count}');
// onPressed: () => context.read<CounterModel>().increment();
Cramming the whole app into a single giant global state or, the other way around, using a heavy library like Bloc for a two-screen app. It's also common to call notifyListeners() too much (rebuilding the UI unnecessarily) or to modify the state without notifying (the UI doesn't react). Choose the tool based on the real complexity, not on hype.
"For local state I use
setState. When state is shared across several widgets, I useProvider, which relies onChangeNotifierandnotifyListenersand under the hood usesInheritedWidget. In new projects I likeRiverpodbecause it's more type-safe, doesn't depend oncontextto read, and is easier to test. And for large apps with complex business logic,BlocorCubit, which separate the UI from the logic with events and states. My rule of thumb is to start simple and level up only when the complexity calls for it."
In the ChangeNotifier pattern, what happens if you change a value but forget to call notifyListeners()?
See answer
The value changes in memory, but the widgets that are listening don't find out and don't rebuild: the UI stays showing the old data. notifyListeners() is what triggers the update for all the listeners. It's conceptually the equivalent of forgetting setState.