我正在尝试将“已解决”的 Riverpod StreamProvider 对象注入下面的树中,以删除一些不必要的异步调用。如果我对文档的解释是正确的,嵌套的 ProviderScope 应该对此有所帮助,但我收到运行时异常。
我的用例:我需要访问小部件树中特定于用户的specs 对象。整个应用程序的其余部分都需要来自该对象的一些数据,包括作为任何数据库操作的参数。 specs 对象来自 firebase,并与 StreamProvider 异步检索。
一旦在 HomePage 小部件内执行,我知道规范对象必须被加载并且有效,所以我不想再次获取它作为需要处理加载和错误情况的流提供者。在将规范提供程序输入到其他组合提供程序的情况下尤其如此,因为额外的负载和错误情况会增加许多不必要的复杂性。
// Called at the root of the tree to retieve some firestore object
final specsStreamProvider = StreamProvider<Specs?>((ref) {
return ref.read(baseDatabaseProvider).currentSpecs();
});
// Called further down to provide the object that was retrieved
final specsProvider = Provider<Specs>((ref) {
throw UnimplementedError('should have been overwritten');
});
// An example of how content will be retrieved from firestore at HomePage widget and below.
// Having to use specsStreamProvider here quickly turns into a mess.
final recordStreamProvider = StreamProvider.autoDispose<List<Record>>((ref) {
final specs = ref.read<Specs>(specsProvider);
final database = ref.read(contentDatabaseProvider(specs.current!));
return database.recordsStream();
});
class SetupWidget extends ConsumerWidget {
const SetupWidget({Key? key, required this.setupBuilder, required this.homeBuilder}) : super(key: key);
final WidgetBuilder setupBuilder;
final WidgetBuilder homeBuilder;
@override
Widget build(BuildContext context, ScopedReader watch) {
final specsAsyncValue = watch(specsStreamProvider);
return specsAsyncValue.when(
data: (specs) => _data(context, specs),
loading: () => const Scaffold(/.../),
error: (e, __) => Scaffold(/.../),
));
}
Widget _data(BuildContext context, Specs? specs) {
if (specs != null) {
return ProviderScope(
// The plan here is to introduce the resolved specs into the tree below
overrides: [specsProvider.overrideWithValue(specs)],
child: homeBuilder(context),
);
}
return setupBuilder(context);
}
}
根据 Riverpod API,嵌套的 ProviderScope 是覆盖小部件树部分提供者的有效工具。不幸的是,就我而言,我收到运行时错误“不支持的操作:无法覆盖非根 ProviderContainer/ProviderScope 上的提供程序”
我还尝试将 specsProvider 设为 ScopedProvider,但组合后的 recordStreamProvider 无法编译。 ('错误:无法将参数类型 'ScopedProvider' 分配给参数类型 'RootProvider
答案 0 :(得分:0)
我认为我想通了。我将 specsProvider 设置为在父级中设置的 ScopedProvider 并将 recordStreamProvider(仅在子级中调用的那个)更改为不直接依赖于作用域提供者。
但是,如果我在这里所做的事情是可以接受的并且没有反模式,我仍然很乐意听取一位 Riverpod 专家的意见。
父设置范围提供程序:
final specsStreamProvider = StreamProvider<Specs?>((ref) {
return ref.read(baseDatabaseProvider).currentSpecs();
});
// Called further down to provide the object that was retrieved
// This MUST be a ScopedProvider
final specsProvider = ScopedProvider<Specs>((ref) {
throw UnimplementedError('should have been overwritten');
});
class SetupWidget extends ConsumerWidget {/* as before */}
使用范围提供程序的儿童
// no dependency on specsProvider here
final recordStreamProvider = StreamProvider.family.autoDispose<List<Record>, String>((ref, storeId) {
final database = ref.read(contentDatabaseProvider(storeId));
return database.recordsStream();
});
class HomePage extends ConsumerWidget {
@override
Widget build(BuildContext context, ScopedReader watch) {
final specs = watch(specsProvider);
final recordsAsyncValue = watch(recordsStreamProvider(specs.storeId!));
return recordsAsyncValue.when(
data: (records) => /* build a list */
loading: () => /* show a progress indicator */,
error: (e, __) => /* show an alert dialog */,
));
}
}