Managing State in Flutter
Contents
- Core Concepts
- Architecture and Data Flow
- Workflow: Selecting a State Management Approach
- Workflow: Implementing MVVM with Provider
- Examples
Core Concepts
Flutter's UI is declarative; it is built to reflect the current state of the app (UI = f(state)). When state changes, trigger a rebuild of the UI that depends on that state.
Distinguish between two primary types of state:
- Ephemeral State (Local State): State contained neatly within a single widget (e.g., current page in a
PageView, current selected tab, animation progress). Manage this using aStatefulWidgetandsetState(). - App State (Shared State): State shared across multiple parts of the app and maintained between user sessions (e.g., user preferences, login info, shopping cart contents). Manage this using advanced approaches like
InheritedWidget, theproviderpackage, or Riverpod.
Architecture and Data Flow
Implement Unidirectional Data Flow (UDF) for scalable app state management.
- Unidirectional Data Flow (UDF): Enforce a strict flow where state flows down from the data layer, through the logic layer, to the UI layer. Events from user interactions flow up from the UI layer, to the logic layer, to the data layer.
- Single Source of Truth (SSOT): Ensure data changes always happen in the data layer (Repositories/Providers). The SSOT class must be the only class capable of modifying its respective data.
- Model (Data Layer): Handle low-level tasks like HTTP requests, data caching, and system resources using Repository classes.
- ViewModel / Provider (Logic Layer): Manage the UI state. Convert app data from the Model into UI State. Call
notifyListeners()(Provider) or update state (Riverpod) to trigger UI rebuilds when data changes. - View (UI Layer): Display the state provided by the ViewModel/Provider. Keep views lean; they should contain minimal logic (only routing, animations, or simple UI conditionals).
Workflow: Selecting a State Management Approach
If managing Ephemeral State (single widget scope):
- Subclass
StatefulWidgetandState. - Store mutable state as private fields within the
Stateclass. - Mutate state exclusively inside a
setState()callback.
- Subclass
If managing App State (shared across widgets):
- Implement providers (Riverpod or Provider package).
- Use
ChangeNotifieror Riverpod'sNotifier/AsyncNotifierto emit state updates. - Consume state in widgets using
Consumer/ConsumerWidgetorref.watch().
Workflow: Implementing State with Riverpod
- Define the data model (Freezed classes or plain Dart).
- Create the provider (
@riverpodannotation for code generation, or manual). - Inject
ProviderScopeat the top of the widget tree. - Consume state in widgets using
ConsumerWidgetandref.watch(). - Trigger mutations using
ref.read(provider.notifier).method().
Examples
Ephemeral State Implementation (setState)
class EphemeralCounter extends StatefulWidget {
const EphemeralCounter({super.key});
@override
State<EphemeralCounter> createState() => _EphemeralCounterState();
}
class _EphemeralCounterState extends State<EphemeralCounter> {
int _counter = 0;
void _increment() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _increment,
child: Text('Count: $_counter'),
);
}
}
App State Implementation (Riverpod)
// Provider
@riverpod
class CartNotifier extends _$CartNotifier {
@override
List<String> build() => [];
void addItem(String item) {
state = [...state, item];
}
void removeItem(String item) {
state = state.where((i) => i != item).toList();
}
}
// View
class CartScreen extends ConsumerWidget {
const CartScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final items = ref.watch(cartNotifierProvider);
return Scaffold(
body: ListView.builder(
itemCount: items.length,
itemBuilder: (_, index) => Text(items[index]),
),
floatingActionButton: FloatingActionButton(
onPressed: () => ref.read(cartNotifierProvider.notifier).addItem('New Item'),
child: const Icon(Icons.add),
),
);
}
}
Source: openplaybooks-dev/converge — distributed by TomeVault.