Ref Lifecycle
Ref exists only for the lifetime of its associated provider or widget, managing dependencies, lifecycle events, and interactions with Riverpod during that period.
What is the Ref Lifecycle?
Every provider and consumer receives its own Ref object.
The Ref is created when the provider (or consumer) is initialized and is destroyed when that provider (or consumer) is disposed.
During its lifetime, Ref is responsible for:
- Reading other providers
- Watching dependencies
- Listening to state changes
- Invalidating providers
- Registering lifecycle callbacks
- Managing provider relationships
Once the provider is disposed, its Ref is also destroyed.
Why does it exist?
Riverpod needs a way to manage communication between providers.
Instead of allowing providers to directly reference each other, Riverpod gives each provider its own Ref.
This provides several benefits:
- Automatic dependency tracking
- Safe lifecycle management
- Automatic cleanup
- Controlled access to providers
- Better performance through selective recomputation
Without Ref, Riverpod couldn't determine:
- Which providers depend on each other
- When to rebuild providers
- When providers should be disposed
- How to manage caching
Lifecycle Stages
A Ref typically goes through the following stages.
Provider Created
│
▼
Ref Created
│
▼
build() Executes
│
▼
Provider Active
│
▼
Dependencies Change
│
▼
Provider Rebuilds
│
▼
Last Listener Removed (.autoDispose)
│
▼
onCancel()
│
▼
Listener Returns?
│ │
Yes No
│ │
▼ ▼
onResume() onDispose()
│
▼
Ref Destroyed
Stage 1 — Ref Creation
When Riverpod creates a provider, it also creates a corresponding Ref.
final counterProvider = Provider((ref) {
return 0;
});
Explanation:
- A new
Refinstance is created. - The provider can now interact with Riverpod.
Stage 2 — Dependency Registration
As the provider executes, it may watch other providers.
final greetingProvider = Provider((ref) {
final user = ref.watch(userProvider);
return 'Hello ${user.name}';
});
Explanation:
greetingProviderdepends onuserProvider.- Riverpod records this dependency.
Stage 3 — Provider Rebuild
If a watched dependency changes, Riverpod rebuilds the provider.
userProvider changes
│
▼
greetingProvider rebuilds
│
▼
Same Ref instance continues
The provider gets a new computed value while continuing its lifecycle.
Stage 4 — Lifecycle Events
For .autoDispose providers, Ref exposes lifecycle callbacks.
ref.onCancel(() {
print('No listeners');
});
ref.onResume(() {
print('Listener returned');
});
ref.onDispose(() {
print('Provider disposed');
});
Explanation:
onCancel()runs when the last listener is removed.onResume()runs if a listener returns.onDispose()runs before the provider is destroyed.
Stage 5 — Disposal
Eventually, the provider is destroyed.
Provider Disposed
│
▼
onDispose()
│
▼
Ref Destroyed
After disposal:
- The provider no longer exists.
- The
Refbecomes invalid. - Lifecycle callbacks cannot be registered.
- Dependencies are removed.
Ref Lifecycle in Different Provider Types
| Provider Type | Ref Lifetime |
|---|---|
Provider |
Until the provider is disposed |
Notifier |
Until the notifier is disposed |
FutureProvider |
While the provider exists |
StreamProvider |
While the provider exists |
.autoDispose providers |
Until automatically disposed |
ConsumerWidget |
During each widget build |
ConsumerStatefulWidget |
While the widget exists |
Lifecycle and Dependency Graph
userProvider
│
▼
profileProvider
│
▼
dashboardProvider
If userProvider changes:
userProvider
│
▼
profileProvider rebuilds
│
▼
dashboardProvider rebuilds
Ref is responsible for maintaining this dependency graph.
Lifecycle APIs
Throughout its lifetime, a Ref can use different APIs.
| API | Purpose |
|---|---|
watch() |
Register dependencies |
read() |
Read once |
listen() |
Observe changes |
invalidate() |
Clear provider state |
refresh() |
Recreate provider immediately |
keepAlive() |
Prevent auto-disposal |
onCancel() |
Last listener removed |
onResume() |
Listener returns |
onDispose() |
Final cleanup |
Best Practices
- Keep providers focused on a single responsibility.
- Register lifecycle callbacks immediately after creating resources.
- Prefer
watch()for reactive dependencies. - Use
read()only for one-time access. - Clean up external resources with
onDispose(). - Use
.autoDisposefor short-lived providers. - Avoid performing heavy work inside lifecycle callbacks.
Common Mistakes
1. Using Ref After Disposal
❌ Wrong
ref.onDispose(() {
ref.read(userProvider);
});
Why it's wrong:
- The provider is being destroyed.
- Accessing other providers during disposal can lead to unexpected behavior.
✔ Correct
Use onDispose() only for cleanup.
2. Confusing Widget Lifecycle with Ref Lifecycle
❌ Wrong
Assuming:
Widget disposed
│
▼
Provider disposed
Why it's wrong:
- Providers can outlive widgets.
- Multiple widgets may share the same provider.
✔ Correct
Treat provider and widget lifecycles as separate concepts.
3. Forgetting AutoDispose Behavior
❌ Wrong
Expecting an .autoDispose provider to remain alive forever.
Why it's wrong:
- It is disposed when no listeners remain unless kept alive.
✔ Correct
Use:
ref.keepAlive();
when caching is required.
4. Using read() Instead of watch()
❌ Wrong
final user = ref.read(userProvider);
When reactive updates are expected.
Why it's wrong:
- The provider won't rebuild.
✔ Correct
final user = ref.watch(userProvider);
Ref Lifecycle vs Widget Lifecycle
| Feature | Ref Lifecycle | Widget Lifecycle |
|---|---|---|
| Managed by | Riverpod | Flutter |
| Starts when | Provider is created | Widget is inserted |
| Ends when | Provider is disposed | Widget is removed |
| Tracks dependencies | ✅ | ❌ |
| Registers provider callbacks | ✅ | ❌ |
| Handles UI rendering | ❌ | ✅ |
Related APIs
RefWidgetRefref.watch()ref.read()ref.listen()ref.onDispose()ref.onCancel()ref.onResume().autoDispose
Summary
The Ref lifecycle begins when a provider or consumer is created and ends when it is disposed. During this time, Ref manages provider dependencies, lifecycle callbacks, state invalidation, and communication with Riverpod. Understanding this lifecycle is essential for writing efficient providers, preventing resource leaks, and taking full advantage of Riverpod's automatic dependency tracking and lifecycle management.