跳到主要内容

急切的初始化提供者程序

默认情况下,所有提供者程序都以延迟方式初始化。 这意味着提供者程序仅在首次使用时进行初始化。 这对于仅在应用程序的某些部分使用的提供者程序很有用。

不幸的是,由于 Dart 的工作方式(出于摇树目的),没有办法将提供者程序标记为需要急切初始化。 但是,一种解决方案是在应用程序的根组件下强制读取要急切初始化的提供者程序。

推荐的方法是简单地 "watch" 位于 ProviderScope 下方的 Consumer 中的提供者程序:

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

class MyApp extends StatelessWidget {

Widget build(BuildContext context) {
return const _EagerInitialization(
// TODO: 在这里渲染你的 APP
child: MaterialApp(),
);
}
}

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


Widget build(BuildContext context, WidgetRef ref) {
// 通过观察提供者程序,来初始化提供者程序。
// 通过使用 "watch",提供者程序将保持存活,不会被处置。
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);

// 处理错误状态和加载状态
if (result.isLoading) {
return const CircularProgressIndicator();
} else if (result.hasError) {
return const Text('Oopsy!');
}

return child;
}
}

我已经处理了加载/错误状态,但其他消费者程序仍然收到 AsyncValue!有没有办法不必处理每个小部件中的加载/错误状态?

与其试图让你的提供者程序不公开一个 AsyncValue ,不如让你的小部件使用 AsyncValue.requireValue
这将读取数据,而无需进行模式匹配。如果一个错误溜走了,它会抛出一个异常,并带有明确的信息。

// 一个急切需要初始化的 provider

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

class MyConsumer extends ConsumerWidget {

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

/// 如果提供者程序被正确的急切初始化了,
/// 那么我们可以使用 "requireValue" 直接读取数据。
return Text(result.requireValue);
}
}
备注

尽管有一些方法可以在这些情况下不公开加载/错误状态(依赖于范围),但通常不建议这样做。
创建两个提供者程序并使用覆盖所增加的复杂性不值得麻烦。