website/docs/concepts2/auto_dispose.mdx
import { Link } from "/src/components/Link"; import { AutoSnippet } from "/src/components/CodeSnippet"; import onDisposeExample from "./auto_dispose/on_dispose_example"; import invalidateExample from "!!raw-loader!./auto_dispose/invalidate_example.dart"; import keepAlive from "./auto_dispose/keep_alive"; import cacheForExtension from "!!raw-loader!./auto_dispose/cache_for_extension.dart"; import cacheForUsage from "./auto_dispose/cache_for_usage"; import invalidateFamilyExample from './auto_dispose/invalidate_family_example'
In Riverpod, it is possible to tell the framework to automatically destroy resources associated with a provider when it is no longer used.
If you're using code-generation, this is enabled by default, and can be opted out in the annotation:
// Disable automatic disposal
@Riverpod(keepAlive: true)
String helloWorld(Ref ref) => 'Hello world!';
If you're not using code-generation, you can enable it by using isAutoDispose: true
when creating the provider:
final helloWorldProvider = Provider<String>(
// Opt-in to automatic disposal
isAutoDispose: true,
(ref) => 'Hello world!',
);
:::note
Enabling/disabling automatic disposal has no impact on whether or not
the state is destroyed when the provider is recomputed.
The state will always be destroyed when the provider is recomputed.
:::
:::caution When providers receive parameters, it is recommended to enable automatic disposal. That is because otherwise, one state per parameter combination will be created, which can lead to memory leaks. :::
When automatic disposal is enabled, Riverpod will track whether a provider has listeners or not. This happens by tracking Ref.watch/Ref.listen calls (and a few others).
When that counter reaches zero, the provider is considered "not used", and
Ref.onCancel
is triggered.
At that point, Riverpod waits for one frame (cf. await null). If, after that frame,
the provider is still not used, then the provider is destroyed and
Ref.onDispose
will be triggered.
In Riverpod, there are a few built-in ways for state to be destroyed:
ref.watch.
In that case, the previous state is disposed, and a new state is created.In both cases, you may want to execute some logic when that happens.
This can be achieved with ref.onDispose. This method enables
registering a listener for whenever the state is destroyed.
For example, you may want to use it to close any active StreamController:
<AutoSnippet {...onDisposeExample} />
:::caution
The callback of ref.onDispose must not trigger side-effects.
Modifying providers inside onDispose could lead to unexpected behavior.
:::
:::info There are other useful life-cycles such as:
ref.onCancel which is called when the last listener of a provider is removed.ref.onResume which is called when a new listener is added after onCancel was invoked.:::
:::info
You can call ref.onDispose as many times as you wish.
Feel free to call it once per disposable object in your provider. This practice
makes it easier to spot when we forget to dispose of something.
:::
ref.invalidateSometimes, you may want to force the destruction of a provider.
This can be done by using ref.invalidate, which can be called from another
provider or a widget.
Using ref.invalidate will destroy the current provider state.
There are then two possible outcomes:
:::info
It is possible for providers to invalidate themselves by using ref.invalidateSelf.
Although in this case, this will always result in a new state being created.
:::
:::tip When trying to invalidate a provider which receives parameters, it is possible to either invalidate one specific parameter combination, or all parameter combinations at once:
<AutoSnippet {...invalidateFamilyExample} /> :::
ref.keepAliveAs mentioned above, when automatic disposal is enabled, the state is destroyed when the provider has no listeners for a full frame.
But you may want to have more control over this behavior. For instance, you may want to keep the state of successful network requests, but not cache failed requests.
This can be achieved with ref.keepAlive, after enabling automatic disposal.
Using it, you can decide when the state stops being automatically disposed.
<AutoSnippet {...keepAlive} />
:::note If the provider is recomputed, automatic disposal will be re-enabled.
It is also possible to use the return value of ref.keepAlive to
revert to automatic disposal.
:::
Currently, Riverpod does not offer a built-in way to keep state alive
for a specific amount of time.
But implementing such a feature is easy and reusable with the tools we've seen so far.
By using a Timer + ref.keepAlive, we can keep the state alive for a specific amount of time.
To make this logic reusable, we could implement it in an extension method:
Then, we can use it like so:
<AutoSnippet {...cacheForUsage} />
This logic can be tweaked to fit your needs.
For example, you could use ref.onCancel/ref.onResume to destroy the state
only if a provider hasn't been listened to for a specific amount of time.