急切的初始化提供者程序
默认情况下,所有提供者程序都以延迟方式初始化。 这意味着提供者程序仅在首次使用时进行初始化。 这对于仅在应用程序的某些部分使用的提供者程序很有用。
不幸的是,由于 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
自身。
这意味着,如果重新生成 _EagerInitialization
,child
变量将不会更改。
当一个小部件没有改变时,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);
}
}
尽管有一些方法可以在这些情况下不公开加载/错误状态(依赖于范围),但通常不建议这样做。
创建两个提供者程序并使用覆盖所增加的复杂性不值得麻烦。