mirror of
https://github.com/rrousselGit/riverpod.git
synced 2025-08-15 02:06:53 +08:00

We have way too many duplicates of the same code snippets. This makes migrating code to newer versions difficult, as lots of clones need to be updated. Let's remove duplicates by enabling translation of comments inside snippets
65 lines
2.8 KiB
Plaintext
65 lines
2.8 KiB
Plaintext
---
|
|
title: Eager initialization of providers
|
|
version: 2
|
|
---
|
|
|
|
import { Link } from "/src/components/Link";
|
|
import { AutoSnippet } from "/src/components/CodeSnippet";
|
|
import consumerExample from "!!raw-loader!./eager_initialization/consumer_example.dart";
|
|
import asyncConsumerExample from "!!raw-loader!./eager_initialization/async_consumer_example.dart";
|
|
import requireValue from "./eager_initialization/require_value";
|
|
|
|
All providers are initialized lazily by default. This means that the provider is only
|
|
initialized when it is first used. This is useful for providers that are only
|
|
used in certain parts of the application.
|
|
|
|
Unfortunately, there is no way to flag a provider as needing to be eagerly initialized due
|
|
to how Dart works (for tree shaking purposes). One solution, however, is to forcibly
|
|
read the providers you want to eagerly initialize at the root of your application.
|
|
|
|
The recommended approach is to simply "watch" a provider in a Consumer placed right under your `ProviderScope`:
|
|
|
|
<AutoSnippet raw={consumerExample} />
|
|
|
|
:::note
|
|
Consider putting the initialization consumer in your "MyApp" or in a public widget.
|
|
This enables your tests to use the same behavior, by removing logic from your main.
|
|
:::
|
|
|
|
### FAQ
|
|
|
|
#### Won't this rebuild our entire application when the provider changes?
|
|
|
|
No, this is not the case.
|
|
In the sample given above, the consumer responsible for eagerly initializing
|
|
is a separate widget, which does nothing but return a `child`.
|
|
|
|
The key part is that it returns a `child`, rather than instantiating `MaterialApp` itself.
|
|
This means that if `_EagerInitialization` ever rebuilds, the `child` variable
|
|
will not have changed. And when a widget doesn't change, Flutter doesn't rebuild it.
|
|
|
|
As such, only `_EagerInitialization` will rebuild, unless another widget is also listening to that provider.
|
|
|
|
#### Using this approach, how can I handle loading and error states?
|
|
|
|
You can handle loading/error states as you normally would in a `Consumer`.
|
|
Your `_EagerInitialization` could check if a provider is in a "loading" state,
|
|
and if so, return a `CircularProgressIndicator` instead of the `child`:
|
|
|
|
<AutoSnippet raw={asyncConsumerExample} />
|
|
|
|
#### I've handled loading/error states, but other Consumers still receive an AsyncValue! Is there a way to not have to handle loading/error states in every widget?
|
|
|
|
Rather than trying to have your provider _not_ expose an `AsyncValue`, you can
|
|
instead have your widgets use `AsyncValue.requireValue`.
|
|
This will read the data without having to do pattern matching. And in case a bug slips through,
|
|
it will throw an exception with a clear message.
|
|
|
|
<AutoSnippet {...requireValue} />
|
|
|
|
:::note
|
|
Although there are ways to not expose the loading/error states in those cases (relying on scoping),
|
|
it is generally discouraged to do so.
|
|
The added complexity of making two providers and using overrides is not worth the trouble.
|
|
:::
|