AsyncNotifier and FutureProvider Data Pipelines
Model loading, error, and data states cleanly using AsyncNotifier and AsyncValue patterns.
The Three States of Async Data
Every screen that loads data from the network must answer three questions: Is it loading? Did it fail? What is the data?
Riverpod 2.0 models these three states with a single sealed type called AsyncValue<T>. Instead of juggling separate isLoading, error, and data fields by hand, you receive one value that is always in exactly one of three shapes:
AsyncLoading<T>— the future is still runningAsyncError<T>— it threw, carrying the error and stack traceAsyncData<T>— it completed successfully, carrying the value
Both FutureProvider and AsyncNotifier expose their state as an AsyncValue, which is why the UI code for either looks identical.
FutureProvider: the simplest pipeline
FutureProvider is the lightest way to run an async computation and expose its result. You give it an async callback; Riverpod runs it, caches the result, and rebuilds dependents as the state moves from loading to data or error.
Use it when the data is read-only and has no side-effect methods — for example, fetching a user profile once and displaying it.
final userProfileProvider = FutureProvider<UserProfile>((ref) async {
final repo = ref.watch(profileRepositoryProvider);
return repo.fetchProfile();
});
// In a ConsumerWidget
Widget build(BuildContext context, WidgetRef ref) {
final profile = ref.watch(userProfileProvider);
return profile.when(
loading: () => const CircularProgressIndicator(),
error: (err, st) => Text('Failed: $err'),
data: (p) => Text('Hello, ${p.name}'),
);
}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