You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的状态更新规则:

  1. 执行state = const AsyncLoading();时,已经触发一次状态更新,AsyncNotifier内部关联的Completer被标记为完成。
  2. 后续用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 14:37:53