In Riverpod 3, which provider type fits a computed value, a one-off async load, a live stream, and state the UI changes?
answer
- read-only versus mutable
- sync value or AsyncValue
- Future once, Stream over time
- Notifier classes carry methods
- legacy import for the old ones
basics
~10 sProvider for a synchronous computed or injected value, FutureProvider for data loaded once, StreamProvider for values that keep arriving, and NotifierProvider or AsyncNotifierProvider when the UI must change the state through methods.
solid answer
~40 sRiverpod 3 splits providers by two questions: is the value synchronous or asynchronous, and can the app change it? `Provider` exposes a synchronous value computed from other providers or an injected object such as a repository; watching it gives `T`. `FutureProvider` runs an async function once (again when a watched dependency changes) and `StreamProvider` subscribes to a stream; both are watched as `AsyncValue<T>`. When the UI needs to change the state, use a class: `NotifierProvider` with a `Notifier<T>` whose `build()` returns the initial state and whose methods assign `state`, or `AsyncNotifierProvider` with an `AsyncNotifier<T>` when the initial state is loaded asynchronously. `StateProvider`, `StateNotifierProvider` and `ChangeNotifierProvider` still exist in Riverpod 3 but only under `legacy.dart`, marked as no longer recommended.
code
dart · 25 linesimport 'package:flutter_riverpod/flutter_riverpod.dart';
final chatRepositoryProvider = Provider<ChatRepository>((ref) => ChatRepository());
final profileProvider = FutureProvider<UserProfile>((ref) {
return ref.watch(chatRepositoryProvider).fetchProfile();
});
final unreadCountProvider = StreamProvider<int>((ref) {
return ref.watch(chatRepositoryProvider).watchUnreadCount();
});
enum NotificationFilter { all, mentionsOnly }
final notificationFilterProvider =
NotifierProvider<NotificationFilterNotifier, NotificationFilter>(
NotificationFilterNotifier.new,
);
class NotificationFilterNotifier extends Notifier<NotificationFilter> {
@override
NotificationFilter build() => NotificationFilter.all;
void showMentionsOnly() => state = NotificationFilter.mentionsOnly;
}go deeper
Recall the five current types and what watching each returns: T for Provider and NotifierProvider, AsyncValue for the three asynchronous ones.
Justify each choice by the two questions - synchronous or asynchronous, read-only or mutable - and explain why mutation lives in notifier classes.
Spot when a provider has outgrown its type, such as a FutureProvider that now needs edits, and keep legacy types out of new code.
Set team conventions for which provider types are allowed where, so state stays read-only by default and mutations go through named notifier methods.
## Providers are declarations In **Riverpod 3** (flutter_riverpod 3.4 at the time of writing), a provider is a global, immutable declaration of how to obtain a piece of state. The value itself lives in a `ProviderContainer` - in a Flutter app, the one created by `ProviderScope` at the root - and widgets read it through a `WidgetRef`. Choosing the right provider type is mostly answering two questions: 1. Is the value available **synchronously**, or does it come from a `Future` or a `Stream`? 2. Does anything outside the provider need to **change** it, or is it derived from other state? ## The five current types | Type | Declared with | `ref.watch` returns | Changed from outside? | |---|---|---|---| | `Provider` | a function returning `T` | `T` | no - recomputed from dependencies | | `FutureProvider` | a function returning `Future<T>` | `AsyncValue<T>` | no | | `StreamProvider` | a function returning `Stream<T>` | `AsyncValue<T>` | no | | `NotifierProvider` | a `Notifier<T>` class | `T` | yes, via the notifier's methods | | `AsyncNotifierProvider` | an `AsyncNotifier<T>` class | `AsyncValue<T>` | yes, via the notifier's methods | Riverpod also ships `StreamNotifierProvider`, the class-based counterpart of `StreamProvider`, for a stream that must also expose methods. ## Mapping a messaging app A messaging app needs several kinds of state, and each lands on a different type: - **The chat repository** - an object other providers use. `Provider<ChatRepository>`: synchronous, never changed by the UI. - **The display name shown in the header** - derived from the profile. `Provider<String>` that watches the profile provider; it recomputes when the profile changes and, since Riverpod 3.0, notifies listeners only when the new value is not `==` to the previous one. - **The user profile, loaded once** - `FutureProvider<UserProfile>`, or `AsyncNotifierProvider` as soon as the UI must edit it (rename, change avatar). - **The live unread-notifications count** - `StreamProvider<int>` over the repository's stream. - **A notification filter the user toggles** - `NotifierProvider<NotificationFilterNotifier, NotificationFilter>`: synchronous state with methods such as `showMentionsOnly()`. ## Why classes for mutable state The functional providers (`Provider`, `FutureProvider`, `StreamProvider`) have no public way to set their value; they only re-run. When a widget must change state, Riverpod puts the logic in a **notifier class**: - `build()` returns the initial state (and can watch other providers); - methods assign `state`, which notifies listeners; - widgets call those methods through `ref.read(provider.notifier)`. Keeping mutations inside the notifier means every change goes through named methods rather than arbitrary assignments from the UI. ## The legacy types Riverpod 3 moved `StateProvider`, `StateNotifierProvider` and (in flutter_riverpod) `ChangeNotifierProvider` to `package:flutter_riverpod/legacy.dart` (or `package:riverpod/legacy.dart`). They were **not removed**; the move signals that they are no longer recommended. New code uses `Notifier` for what `StateProvider` and `StateNotifierProvider` used to do. ## Quick decision list - Derived or injected, synchronous: `Provider`. - Fetched once, read-only: `FutureProvider`. - Pushed over time, read-only: `StreamProvider`. - Changed by the UI, synchronous: `NotifierProvider`. - Changed by the UI, loaded asynchronously: `AsyncNotifierProvider`.
- When would you move the profile from a FutureProvider to an AsyncNotifierProvider?As soon as the UI must change it - renaming the user or changing the avatar. A `FutureProvider` can only re-run its function; an `AsyncNotifier` keeps the same asynchronous loading in `build()` and adds methods that update `state` after saving.
- Why not use a StateProvider for the notification filter in new Riverpod 3 code?`StateProvider` lives in `legacy.dart` in Riverpod 3 and is no longer recommended. A small `Notifier` gives the same synchronous state with named methods, the same API as every other notifier, and no extra import.
saying these in an interview costs you the question
- Uses a FutureProvider and tries to assign its value from a button.
- Believes StateNotifierProvider was deleted in Riverpod 3.
- Expects ref.watch on a StreamProvider to return the raw int.
- Picks StateProvider as the default for new mutable state.
- Puts a repository in a NotifierProvider although nothing ever changes it.