跳到主要內容

急切的初始化提供者程式

預設情況下,所有提供者程式都以延遲方式初始化。 這意味著提供者程式僅在首次使用時進行初始化。 這對於僅在應用程式的某些部分使用的提供者程式很有用。

不幸的是,由於 Dart 的工作方式(出於搖樹目的),沒有辦法將提供者程式標記為需要急切初始化。 但是,一種解決方案是在應用程式的根元件下強制讀取要急切初始化的提供者程式。

推薦的方法是簡單地 "watch" 位於 ProviderScope 下方的 Consumer 中的提供者程式:

void main() {
runApp(ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {

Widget build(BuildContext context) {
return const _EagerInitialization(
// TODO: Render your app here
child: MaterialApp(),
);
}
}

class _EagerInitialization extends ConsumerWidget {
const _EagerInitialization({required this.child});
final Widget child;


Widget build(BuildContext context, WidgetRef ref) {
// Eagerly initialize providers by watching them.
// By using "watch", the provider will stay alive and not be disposed.
ref.watch(myProvider);
return child;
}
}
備註

請考慮將初始化使用者放在 "MyApp" 或公共小部件中。 這使你的測試能夠使用相同的行為,方法是從你的主資料中刪除邏輯。

常見疑問

當提供者程式更改時,這不會重建我們的整個應用程式嗎?

不,事實並非如此。 在上面給出的示例中,負責急切初始化的消費者程式是一個單獨的小部件,它只返回一個 child .

關鍵部分是它返回一個 child ,而不是例項化 MaterialApp 自身。 這意味著,如果重新生成 _EagerInitializationchild 變數將不會更改。 當一個小部件沒有改變時,Flutter 不會重建它。

因此,除非另一個小部件也在監聽該提供者程式,否則只會 _EagerInitialization 重新構建。

使用此方法,如何處理載入和錯誤狀態?

您可以像往常一樣在 Consumer 中處理載入/錯誤狀態。 您可以 _EagerInitialization 檢查提供者程式是否處於 "loading" 狀態, 如果是則返回 CircularProgressIndicator 否則返回 child

class _EagerInitialization extends ConsumerWidget {
const _EagerInitialization({required this.child});
final Widget child;


Widget build(BuildContext context, WidgetRef ref) {
final result = ref.watch(myProvider);

// Handle error states and loading states
if (result.isLoading) {
return const CircularProgressIndicator();
} else if (result.hasError) {
return const Text('Oopsy!');
}

return child;
}
}

我已經處理了載入/錯誤狀態,但其他消費者程式仍然收到 AsyncValue!有沒有辦法不必處理每個小部件中的載入/錯誤狀態?

與其試圖讓你的提供者程式不公開一個 AsyncValue ,不如讓你的小部件使用 AsyncValue.requireValue
這將讀取資料,而無需進行模式匹配。如果一個錯誤溜走了,它會丟擲一個異常,並帶有明確的資訊。

// An eagerly initialized provider.

Future<String> example(Ref ref) async => 'Hello world';

class MyConsumer extends ConsumerWidget {

Widget build(BuildContext context, WidgetRef ref) {
final result = ref.watch(exampleProvider);

/// If the provider was correctly eagerly initialized, then we can
/// directly read the data with "requireValue".
return Text(result.requireValue);
}
}
備註

儘管有一些方法可以在這些情況下不公開載入/錯誤狀態(依賴於範圍),但通常不建議這樣做。
建立兩個提供者程式並使用覆蓋所增加的複雜性不值得麻煩。