Flutter State
State
State management is about ownership and lifecycle. Choose patterns by state category, not by library popularity.
State Categories
UI State
Short-lived values local to one widget tree branch (tab index, text focus, toggle visibility).
Feature state
Domain state shared across screens within a feature (cart contents, selected account, draft form flows).
Server state
Remote data requiring loading/error/cache semantics and invalidation strategy.
Layers
Layering
presentation (widgets, controllers)
-> application (use cases)
-> domain (entities, policies)
-> data (api, local cache, adapters)
Tool Choice
- Start simple with ValueNotifier/ChangeNotifier for small feature scope.
- Use Riverpod/Bloc when state graph and async orchestration become complex.
- Standardize one primary approach per project to reduce cognitive switching.
Async State
State Model
sealed class UsersState {}
class UsersLoading extends UsersState {}
class UsersReady extends UsersState {
UsersReady(this.items);
final List<User> items;
}
class UsersFailure extends UsersState {
UsersFailure(this.message);
final String message;
}
Recovery
Expose explicit retry actions in UI and record failure reasons for observability.
Injection
Inject Services
class UsersController {
UsersController(this.api);
final UsersApi api;
}
This allows deterministic unit tests and easier substitution in integration environments.
Engineering Notes
Keep a state ownership map per feature: who owns it, who reads it, and how it is invalidated.
Common Mistakes
- Globalizing local state too early.
- Using one mega-controller for unrelated domains.
- Ignoring async error states and showing empty UI with no diagnostics.
Practice Scope
Implement one feature with explicit state classes, async lifecycle handling, and unit tests for controller logic.
Back to roadmap: Flutter Roadmap