Flutter(Riverpod)中Future为何在AsyncValue.guard()处理前就已完成?
Riverpod AsyncValue.guard抛出"Future already completed"异常问题解析
问题背景
我有一个Service类用于操作Repository类将数据写入数据库:
Service类
class ListsService { ListsService(this.ref); final Ref ref; Future<void> addList(ListModel list) async { final repo = ref.read(ListsRepositoryProvider); final listsCount = await repo.countAll(); /// 返回Future<ListModel> await repo.add( list.copyWith( isFavorite: listsCount == 0 ? true : false, ), ); } // ... 其他方法 } @riverpod ListsService listsService(listsServiceRef ref) { return ListsService(ref); }
Controller类(调用Service)
@riverpod class ListsController extends _$ListsController { @override FutureOr<void> build() {} ListsService get listsService => ref.read(listsServiceProvider); Future<void> addList(ListModel list) async { state = const AsyncLoading(); /// 此处抛出状态错误(Future已完成) state = await AsyncValue.guard<void>(() => listsService.addList(list)); } }
当Controller中的addList方法调用state = await AsyncValue.guard(() => listsService.addList(list));时,抛出异常:StateError (Bad state: Future already completed)。请问该Future可能在传入AsyncValue.guard()回调之前就已完成的原因是什么?
更新:
将问题简化后,异常仍会抛出:
Future<void> addList(ListModel list) async { state = const AsyncLoading(); // 此处抛出异常:StateError (Bad state: Future already completed) state = await AsyncValue.guard<ListModel>(() async => Future.delayed(Duration(seconds: 2), () => ListModel(title: 'Test'))); }
原因与解决方案
这个异常的核心原因是你在同一个异步方法中连续两次修改AsyncNotifier的state,违反了Riverpod的状态更新规则:
- 执行
state = const AsyncLoading();时,已经触发一次状态更新,AsyncNotifier内部关联的Completer被标记为完成。 - 后续用
await AsyncValue.guard(...)获取结果再赋值给state时,内部的Completer已经处于完成状态,无法再次接收新的状态更新,因此抛出Future already completed错误。
正确解决方式
直接移除手动设置的AsyncLoading,让AsyncValue.guard自动管理整个状态流程(它会自动生成Loading、Success、Error三种状态):
Future<void> addList(ListModel list) async { state = await AsyncValue.guard<void>(() => listsService.addList(list)); }
进阶场景(需额外前置逻辑)
如果必须在加载前执行一些额外操作,可通过then链式调用避免重复赋值冲突:
Future<void> addList(ListModel list) async { state = const AsyncLoading(); // 用then处理结果,避免await后直接赋值触发状态冲突 await AsyncValue.guard<void>(() => listsService.addList(list)).then((newState) { state = newState; }); }
本质上,AsyncNotifier的state是FutureOr类型,每次赋值都会绑定一个新的Completer,同一个异步方法中连续赋值会导致前一个Completer已完成,无法复用,从而触发异常。AsyncValue.guard本身就封装了完整的状态流转逻辑,无需手动设置Loading状态。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

