Sparround

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 valueRead-onlyMutable (notifier)
Synchronous`Provider``NotifierProvider` (`Notifier<T>`)
`Future``FutureProvider``AsyncNotifierProvider` (`AsyncNotifier<T>`)
`Stream``StreamProvider``StreamNotifierProvider` (`StreamNotifier<T>`)
dart
// 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