如何在Riverpod中获取Stream Provider的连接状态?
用Riverpod实现模块逐个加载并显示完成提示
一、当前写法的小问题
你当前代码里循环渲染模块时用了modules[i],逻辑上应该改用value[i](或直接遍历value)。因为value是Stream当前输出的已加载模块列表,这样更符合数据流逻辑,能避免原始列表和流输出不一致时出现错误。
二、判断Stream是否完成的两种方案
方案1:自定义状态类,包含加载状态和完成标记
这种方法直接在Stream输出中包含完成状态,逻辑紧凑,适合单一组件使用场景。
1. 定义状态类
class ModuleLoadState { final List<String> loadedModules; final bool isDone; ModuleLoadState(this.loadedModules, this.isDone); }
2. 修改StreamProvider
@riverpod Stream<ModuleLoadState> getModules(Ref ref, List<String> modules) async* { final showingModules = <String>[]; for (var module in modules) { await Future.delayed(const Duration(milliseconds: 1500)); showingModules.add(module); // 每次加载一个模块后,输出未完成状态 yield ModuleLoadState(List.unmodifiable(showingModules), false); } // 所有模块加载完成后,输出标记为完成的状态 yield ModuleLoadState(List.unmodifiable(showingModules), true); }
3. 渲染Widget
Consumer( builder: (context, ref, child) { final moduleState = ref.watch(getModulesProvider(modules)); return switch (moduleState) { AsyncData(:final value) => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 遍历已加载模块 for (final module in value.loadedModules) ...[ Row( children: [ Container( width: 8, height: 8, decoration: ShapeDecoration( shape: const CircleBorder(), color: primary, ), ), const SizedBox(width: 4), LeftToRightTextFade(text: module), ], ), const SizedBox(height: 8), ], // 流完成后显示提示 if (value.isDone) ...[ const SizedBox(height: 16), FutureBuilder( future: Future.delayed(const Duration(milliseconds: 500)), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { return Text( "All modules loaded...", style: body(context).copyWith(color: primary), ); } return const SizedBox.shrink(); }, ), ] ], ), AsyncError(:final error) => Text('加载出错: $error'), AsyncLoading() => const SizedBox.shrink(), }; }, ),
方案2:用单独的StateProvider标记完成状态
这种方法将完成状态与数据流分离,适合需要在多个组件中复用完成状态的场景。
1. 创建状态Provider和StreamProvider
// 标记所有模块是否加载完成的状态Provider @riverpod StateProvider<bool> modulesLoadCompleted(Ref ref) => StateProvider((ref) => false); @riverpod Stream<List<String>> getModules(Ref ref, List<String> modules) async* { final showingModules = <String>[]; final completedNotifier = ref.read(modulesLoadCompletedProvider.notifier); try { for (var module in modules) { await Future.delayed(const Duration(milliseconds: 1500)); showingModules.add(module); yield List.unmodifiable(showingModules); } } finally { // 流完成时更新状态 completedNotifier.state = true; } }
2. 渲染Widget
Consumer( builder: (context, ref, child) { final loadedModules = ref.watch(getModulesProvider(modules)); final isLoadCompleted = ref.watch(modulesLoadCompletedProvider); return switch (loadedModules) { AsyncData(:final value) => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ for (final module in value) ...[ Row( children: [ Container( width: 8, height: 8, decoration: ShapeDecoration( shape: const CircleBorder(), color: primary, ), ), const SizedBox(width: 4), LeftToRightTextFade(text: module), ], ), const SizedBox(height: 8), ], if (isLoadCompleted) ...[ const SizedBox(height: 16), FutureBuilder( future: Future.delayed(const Duration(milliseconds: 500)), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { return Text( "All modules loaded...", style: body(context).copyWith(color: primary), ); } return const SizedBox.shrink(); }, ), ] ], ), AsyncError(:final error) => Text('加载出错: $error'), AsyncLoading() => const SizedBox.shrink(), }; }, ),
三、注意事项
- 用
List.unmodifiable包装输出列表,避免外部修改导致状态不一致。 - 若需要支持重新加载模块,可在Provider中添加
ref.onDispose或监听依赖变化重置状态。
内容的提问来源于stack exchange,提问作者Sireen Ibnu Kabeer
相关产品推荐
相关产品推荐

