Riverpod 2 with code generation is the state management approach most new Flutter projects reach for in 2026. It's compile-safe, testable, and decoupled from the widget tree. Here's the modern workflow.
A Generated Provider
part 'counter.g.dart';
@riverpod
class Counter extends _$Counter {
@override
int build() => 0;
void increment() => state++;
}Async Data With AsyncValue
AsyncNotifier models loading, data, and error as a single value you pattern-match in the UI — no manual flags:
@riverpod
Future<List<User>> users(UsersRef ref) async {
final res = await http.get(Uri.parse('/api/users'));
return parseUsers(res.body);
}
// In the widget
ref.watch(usersProvider).when(
data: (users) => UserList(users),
loading: () => const Spinner(),
error: (e, _) => ErrorView(e),
);Why Code Generation
- No more choosing the right provider type by hand — the generator infers it.
- Provider arguments become type-safe function parameters.
- Refactors are caught at compile time, not in production.
Run the Watcher
Keep dart run build_runner watch -d running during development so generated files stay in sync as you edit providers.
