autoDispose, keepAlive and family
Riverpod's two most misunderstood modifiers concern the lifetime and the parameters of state.
autoDispose — clearing the state once no listener uses the provider. Per the docs, Riverpod tracks a listener count; when it reaches zero the provider is considered "not used". Then ref.onCancel fires immediately, Riverpod waits one frame, and if the provider is still unused ref.onDispose runs and the state is destroyed. For providers written with code generation this is enabled by default; you disable it with @Riverpod(keepAlive: true). For hand-written providers the 3.x docs show the isAutoDispose: true parameter.
family — the modifier that lets you pass arguments to a provider. Riverpod keeps separate state per argument, so the argument must be stable under ==/hashCode (value types, records, immutable models).
// Kod generasiyası ilə: parametrlər funksiyanın arqumentləridir,
// autoDispose default olaraq aktivdir.
@riverpod
Future<Product> product(Ref ref, String id) {
return ref.watch(catalogRepositoryProvider).fetch(id);
}
// İstifadə: hər id ayrı provider instansiyasıdır.
final product = ref.watch(productProvider('sku-42'));
// Kod generasiyası olmadan: family modifikatoru, bir pozisional arqument.
final productProviderManual = FutureProvider.family<Product, String>(
(ref, id) => ref.watch(catalogRepositoryProvider).fetch(id),
);
// keepAlive: uğurlu nəticəni cache-də saxlamaq, xətanı saxlamamaq.
@riverpod
Future<Config> config(Ref ref) async {
final result = await ref.watch(apiClientProvider).loadConfig();
ref.keepAlive(); // yalnız uğurlu hala çatdıqda cache-i qoruyur
return result;
}family plus autoDispose: a separate cache per id, cleared when unused.
| Situation | Recommendation | Reason |
|---|---|---|
| The provider takes parameters (a family) | Keep autoDispose enabled | The docs' recommendation: otherwise state accumulates for every argument combination |
| Configuration or auth needed app-wide | `@Riverpod(keepAlive: true)` | Reloading on every navigation makes no sense |
| A heavy request's result, but only when it succeeds | autoDispose plus a conditional `ref.keepAlive()` | Errors are not cached while successful results are |
| A screen's temporary state (a filter, a selection) | autoDispose — or not a provider at all | The DO/DON'T page advises against providers for ephemeral state |
ref.keepAlive() returns a link: calling link.close() restores the auto-dispose behaviour. So the cache can be kept conditionally rather than forever — for instance a Timer calling link.close() after five minutes to declare the data stale. That detail is a strong argument in an interview discussion about caching strategy.
Riverpod 3 adds one more mechanism: pause/resume. A subscription can be controlled manually with subscription.pause() and subscription.resume(), and per the docs subscriptions are paused automatically for widgets that are not visible. The practical effect: a StreamProvider on a background screen no longer triggers pointless rebuilds.
📚 Sources and documentation
- autoDisposeofficialriverpod.dev
The listener counter, the one-frame wait, ref.keepAlive() and the link mechanism are here.
- familyofficialriverpod.dev
Passing parameters and keeping separate state per argument.
- Riverpod 3: pause/resume and other changesofficialriverpod.dev
- Provider kinds and modifiersofficialriverpod.dev