Provider kinds and notifiers
The Riverpod 3 docs organise providers along two axes: can the state be modified and is the value synchronous, a future or a stream.
Read-only providers that just compute a value: Provider, FutureProvider, StreamProvider.
Notifiers, which expose methods to change state: NotifierProvider, AsyncNotifierProvider, StreamNotifierProvider.
The rule of thumb: if the UI calls a method that changes state, you need a notifier; if it only reads, Provider/FutureProvider is enough.
| Kind of value | Read-only | Mutable (notifier) |
|---|---|---|
| Synchronous | `Provider` | `NotifierProvider` (`Notifier<T>`) |
| `Future` | `FutureProvider` | `AsyncNotifierProvider` (`AsyncNotifier<T>`) |
| `Stream` | `StreamProvider` | `StreamNotifierProvider` (`StreamNotifier<T>`) |
// 1) Dəyişməyən obyekt: Provider.
final apiClientProvider = Provider<ApiClient>((ref) {
final client = ApiClient();
ref.onDispose(client.close); // təmizləmə nöqtəsi
return client;
});
// 2) Kompozisiya: ProxyProvider yerinə sadəcə ref.watch.
final todoRepositoryProvider = Provider<TodoRepository>((ref) {
return TodoRepository(ref.watch(apiClientProvider));
});
// 3) Async siyahı + dəyişdirmə metodları: AsyncNotifier.
final todosProvider =
AsyncNotifierProvider<TodosNotifier, List<Todo>>(TodosNotifier.new);
class TodosNotifier extends AsyncNotifier<List<Todo>> {
@override
Future<List<Todo>> build() async {
// build-in qaytardığı future AsyncValue-ya çevrilir:
// loading → data, xəta olarsa → error.
return ref.watch(todoRepositoryProvider).fetchAll();
}
Future<void> add(String title) async {
final repo = ref.read(todoRepositoryProvider);
// Optimistik yenilənmə: UI dərhal yeni siyahını görür.
state = AsyncData([...?state.value, Todo(title: title)]);
try {
await repo.create(title);
} catch (error, stackTrace) {
state = AsyncError(error, stackTrace);
return;
}
ref.invalidateSelf(); // serverdən təsdiqlənmiş vəziyyəti yenidən yükləyir
}
}Three levels of one feature: service → repository → notifier.
Legacy APIs in Riverpod 3. StateProvider, StateNotifierProvider and ChangeNotifierProvider were moved to a separate import — package:riverpod/legacy.dart — to discourage their use. They still work for backward compatibility, but new code uses Notifier/AsyncNotifier.
Also in 3.0, AutoDisposeNotifier and FamilyNotifier were merged into a single Notifier class, and Ref became one unified type (provider-specific types such as FutureProviderRef were removed) — which simplifies generated provider signatures.
The official migration doc puts the essence of moving from ChangeNotifier to AsyncNotifier this way: hand-maintained flags such as isLoading and hasError disappear because AsyncNotifier turns the future returned from build into an AsyncValue — "returning the future is enough". And notifyListeners() is no longer needed: assigning a new state is the notification.
📚 Sources and documentation
- Provider kinds (Riverpod 3)officialriverpod.dev
The official table of the six provider kinds and the @riverpod syntax.
- What's new in Riverpod 3officialriverpod.dev
The move of legacy providers, the unified Ref, and the merged Notifier classes.
- Migrating from ChangeNotifierofficialriverpod.dev
Replacing the isLoading/hasError flags with AsyncValue — before/after code.
- Migrating from StateNotifierofficialriverpod.dev