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

Flutter Bloc同一Bloc内如何跨事件复用结果避免重复调用API

实现方案

核心原则是以Bloc当前持有的状态作为唯一可信数据源,不需要额外维护独立的缓存变量,直接从已发射的列表状态中取已有数据拼接即可,从根源上避免缓存和状态不一致的问题,符合Bloc状态管理的最佳实践。

具体实现逻辑

  • 处理ExpenseAdd事件时,首先判断当前Bloc的生效状态:如果状态为ExpenseFetched,说明列表已经加载完成,直接从状态中取出已有的列表数据
  • 拼接新增的开销条目时,必须生成新的列表对象,不要直接修改原状态持有的列表引用(Dart中List为引用类型,直接修改原列表无法触发Bloc的状态更新,也违反状态不可变原则)
  • 拼接完成后直接发射新的ExpenseFetched状态即可,不需要重复调用接口
  • 增加兜底逻辑:如果当前状态不是ExpenseFetched(比如还在加载中、未初始化),可以根据业务需求选择触发列表拉取、提示加载中等处理

修改后的完整代码

ExpenseBloc() : super(ExpenseInitial()) {
  on<ExpenseEvent>((ExpenseEvent event, Emitter<ExpenseState> emit) async {
    if (event is ExpenseFetchList) {
      emit(ExpenseFetching());
      List<Expense> e = dummyExpenseList; //await event.useCase.getAll();
      // TODO: remove Future.delayed
      await Future.delayed(const Duration(seconds: 5), () {
        emit(ExpenseFetched(list: e));
      });
      return;
    }
    if (event is ExpenseAdd) {
      // 从当前状态获取已加载的列表,无冗余缓存
      if (state is ExpenseFetched) {
        final currentState = state as ExpenseFetched;
        // 生成新列表,不修改原状态的引用
        final updatedList = List<Expense>.from(currentState.list)
          ..add(event.expense); // ExpenseAdd事件需通过expense字段传递新增条目
        emit(ExpenseFetched(list: updatedList));
      } else {
        // 兜底逻辑:列表未加载完成时的处理,可根据业务调整
        emit(ExpenseFetching());
        add(ExpenseFetchList());
      }
      return;
    }
  });
}

注意事项

  • 不要在Bloc中单独定义私有List字段存储列表数据做缓存:这种做法会导致状态和缓存分离,后续如果有删除、编辑、分页加载等操作,很容易漏更新缓存导致数据不一致
  • 所有对列表的修改操作(新增、删除、编辑)都要遵循「从当前状态取旧数据→生成新的列表副本→修改副本→发射新状态」的流程,严格保证状态不可变
  • 如果后续使用freezed等工具生成密封类状态,可以用state.maybeWhen/state.map的写法替代手动类型判断,代码会更简洁安全

内容的提问来源于stack exchange,提问作者Zain Arshad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 13:24:21