如何结合Bloc与Freezed包处理双API请求?求最优方案
解决方案分析
针对你的问题,有两种可行方案,具体选择取决于业务耦合度和未来扩展需求:
方案一:修复现有单Bloc的问题(不拆分)
当前报错的核心原因有两个:
- Bloc默认顺序处理事件,同时触发
fetchObjects和fetchBalance时,第二个请求会等待第一个完成,若其中一个请求异常可能阻塞另一个; - 状态设计为互斥类型,无法同时表示“对象加载中+余额加载中”这类并发状态,导致状态更新冲突。
修复步骤:
开启事件并发处理
将事件注册改为分开处理,并启用concurrent: true,允许两个请求同时执行:ObjectsBloc({required this.objectsUseCase, required this.balanceUseCase}) : super(const ObjectsState.initial()) { on<_FetchObjects>(_fetchObjects, concurrent: true); on<_FetchBalance>(_fetchBalance, concurrent: true); }重构状态为组合式结构
使用AsyncValue(flutter_bloc内置工具)统一管理每个数据的加载状态,让两个请求的状态独立更新:import 'package:bloc/bloc.dart'; class ObjectsState with _$ObjectsState { const factory ObjectsState({ required AsyncValue<ObjectsListResponse> objects, required AsyncValue<BalanceResponse> balance, }) = _ObjectsState; factory ObjectsState.initial() => ObjectsState( objects: const AsyncValue.loading(), balance: const AsyncValue.loading(), ); }更新事件处理逻辑
在各自的请求方法中,只更新对应字段的状态:Future<void> _fetchObjects(Emitter<ObjectsState> emit) async { try { final objects = await objectsUseCase.execute(); emit(state.copyWith(objects: AsyncValue.data(objects))); } catch (e) { emit(state.copyWith(objects: AsyncValue.error(e.toString()))); } } Future<void> _fetchBalance(Emitter<ObjectsState> emit) async { try { final balance = await balanceUseCase.execute(); emit(state.copyWith(balance: AsyncValue.data(balance))); } catch (e) { emit(state.copyWith(balance: AsyncValue.error(e.toString()))); } }
这种方案适合对象和余额业务高度耦合(比如必须同时展示、关联操作)的场景,避免多Bloc的管理成本。
方案二:拆分独立Bloc(推荐未来扩展场景)
如果未来会新增更多独立请求,或者对象、余额的业务逻辑逐渐复杂,建议遵循单一职责原则,拆分为ObjectsBloc和BalanceBloc:
示例代码:
ObjectsBloc相关代码:
// ObjectsEvent class ObjectsEvent with _$ObjectsEvent { const factory ObjectsEvent.fetchObjects() = _FetchObjects; } // ObjectsState class ObjectsState with _$ObjectsState { const factory ObjectsState.initial() = _Initial; const factory ObjectsState.loading() = _Loading; const factory ObjectsState.loaded(ObjectsListResponse objects) = _Loaded; const factory ObjectsState.error(String message) = _Error; } // ObjectsBloc class ObjectsBloc extends Bloc<ObjectsEvent, ObjectsState> { ObjectsBloc({required this.objectsUseCase}) : super(const ObjectsState.initial()) { on<_FetchObjects>(_fetchObjects); } final ObjectsUseCase objectsUseCase; Future<void> _fetchObjects(Emitter<ObjectsState> emit) async { emit(const ObjectsState.loading()); try { final objects = await objectsUseCase.execute(); emit(ObjectsState.loaded(objects)); } catch (e) { emit(ObjectsState.error(e.toString())); } } }
BalanceBloc相关代码:
// BalanceEvent class BalanceEvent with _$BalanceEvent { const factory BalanceEvent.fetchBalance() = _FetchBalance; } // BalanceState class BalanceState with _$BalanceState { const factory BalanceState.initial() = _Initial; const factory BalanceState.loading() = _Loading; const factory BalanceState.loaded(BalanceResponse balance) = _Loaded; const factory BalanceState.error(String message) = _Error; } // BalanceBloc class BalanceBloc extends Bloc<BalanceEvent, BalanceState> { BalanceBloc({required this.balanceUseCase}) : super(const BalanceState.initial()) { on<_FetchBalance>(_fetchBalance); } final BalanceUseCase balanceUseCase; Future<void> _fetchBalance(Emitter<BalanceState> emit) async { emit(const BalanceState.loading()); try { final balance = await balanceUseCase.execute(); emit(BalanceState.loaded(balance)); } catch (e) { emit(BalanceState.error(e.toString())); } } }
优势:
- 每个Bloc只负责单一业务,代码更清晰,易于维护和测试;
- 未来新增请求时,直接新增对应Bloc即可,不会导致单个Bloc臃肿;
- 单独调用某个请求时,直接触发对应Bloc的事件,逻辑更直观。
选择建议
- 如果当前两个请求业务耦合度极高,且未来扩展有限,选方案一;
- 如果未来会新增多个独立请求,或两个业务逻辑有各自的扩展方向,选方案二(长期来看更易维护)。
内容的提问来源于stack exchange,提问作者Nicole Kozmanko
相关产品推荐
相关产品推荐

