Riverpod — Providers
Instructions
Providers are the central feature of Riverpod: memoized functions that cache their result and let multiple widgets (or other providers) access the same value. They are declared as top-level final variables. State lives in a ProviderContainer (or ProviderScope in Flutter), not in the provider itself.
Provider variants
| Synchronous | Future | Stream | |
|---|---|---|---|
| Unmodifiable | Provider | FutureProvider | StreamProvider |
| Modifiable | NotifierProvider | AsyncNotifierProvider | StreamNotifierProvider |
- Sync vs Future vs Stream: Match the return type of your function (
int,Future<T>,Stream<T>). - Unmodifiable: Widgets only read the value. Use for computed or fetched data.
- Modifiable: Expose a Notifier with a
build()method and custom methods; widgets callref.read(provider.notifier).someMethod(). Use when the UI must update state.
Pick the type based on what you want to return; the provider type follows naturally. FutureProvider / AsyncNotifierProvider are the most common.
Creating a provider (unmodifiable / functional)
Manual:
final userProvider = FutureProvider<User>((ref) async {
final response = await http.get('https://api.example.com/user/123');
return User.fromJson(response.body);
});
Code generation: Annotate a top-level function with @riverpod. First parameter must be Ref ref. The generated provider name is functionNameProvider.
@riverpod
Future<User> user(Ref ref) async {
final response = await http.get('https://api.example.com/user/123');
return User.fromJson(response.body);
}
Modifiers: .autoDispose (or @Riverpod(keepAlive: true) to disable) and .family for parameters. See riverpod-auto-dispose and riverpod-family.
Creating a provider (modifiable / Notifier)
Manual: Use a NotifierProvider (or AsyncNotifierProvider / StreamNotifierProvider) with a Notifier class that extends Notifier<T>, AsyncNotifier<T>, or StreamNotifier<T>. Override build(); do not put logic in the constructor (ref/state are not available yet). Public methods are called via ref.read(provider.notifier).method().
final counterProvider = NotifierProvider<CounterNotifier, int>(CounterNotifier.new);
class CounterNotifier extends Notifier<int> {
@override
int build() => 0;
void increment() => state++;
}
Code generation: Annotate a class that extends _$ClassName. Override build(); add custom methods. Same rule: no logic in the constructor.
@riverpod
class CounterNotifier extends _$CounterNotifier {
@override
int build() => 0;
void increment() => state++;
}
Using providers
- Flutter: Wrap the app in
ProviderScope. Use Consumer, ConsumerWidget, or ConsumerStatefulWidget to get arefand callref.watch(provider)orref.read(provider). - Dart only: Create a
ProviderContainer(), thencontainer.read(provider)orcontainer.listen(...). Callcontainer.dispose()when done. - Inside another provider: Use the
Refpassed to the provider function (orthis.refin a Notifier) toref.watch/ref.readother providers.
Example in a widget:
return Consumer(
builder: (context, ref, _) {
final helloWorld = ref.watch(helloWorldProvider);
return Text(helloWorld);
},
);
You can declare multiple providers that return the same type; they are independent. For more detail on Ref, containers, modifiers, and notifier rules, see reference.md.