Provider Scoping, Overrides, and ProviderObserver
Scope providers per feature, override them in tests, and observe state changes for debugging.
Why Scoping, Overrides, and Observation Matter
Riverpod providers are global declarations, but their values don't have to be. Three powerful tools let you control and inspect provider state:
- Scoping — give a provider a different value for one part of the widget tree (e.g. per feature, per item in a list).
- Overrides — replace a provider's implementation, most often in tests to inject fakes.
- ProviderObserver — a hook that fires on every provider add/update/dispose, perfect for logging and debugging.
In this lesson you'll learn to scope providers per feature, override them in tests, and observe state changes. These are the techniques that make a large Flutter app testable and debuggable.
The ProviderScope at the Root
Every Riverpod app is wrapped in a single ProviderScope at the root. This widget creates the ProviderContainer that stores all provider state.
The overrides parameter on ProviderScope is the entry point for both scoping and testing. By default it's empty and every provider uses its declared body.
void main() {
runApp(
const ProviderScope(
// No overrides yet — every provider uses its default body.
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(home: HomeScreen());
}
}All lessons in this course
- From Provider to Riverpod: Migrating Legacy State
- Code Generation with riverpod_generator and @riverpod
- AsyncNotifier and FutureProvider Data Pipelines
- Provider Scoping, Overrides, and ProviderObserver