Flutter Bloc中copyWith函数失效,如何保留原有状态?
问题原因与解决方案
你的问题核心是每次调用copyWith时都从全新的空AddressLoaded实例开始,而非基于Bloc当前已有的状态,导致之前存储的addresses、defaultAddress等数据被直接重置。
具体问题点
- 在
_getCurrentLocation和_getGeoAddress方法中,你使用const AddressLoaded().copyWith(...)创建新状态,这会生成一个所有字段都是默认空值的实例,完全丢弃了之前的状态数据。 - 未处理当前状态不是
AddressLoaded的边界情况(比如初始状态AddressInitial、加载状态GetGeoAddressLoading),直接调用copyWith会导致状态丢失或异常。
修正方案
1. 基于当前状态进行更新
在需要更新状态时,先获取Bloc当前的AddressLoaded状态(如果不是则使用默认空实例),再调用copyWith修改指定字段:
修改_getCurrentLocation方法:
void _getCurrentLocation(GetCurrentLocation event, Emitter<AddressState> emit) async { if (event.currentLocation == null) { logger('this is crashing currentlocation'); return; } // 获取当前状态,非AddressLoaded则用默认实例兜底 final currentLoadedState = state is AddressLoaded ? state as AddressLoaded : const AddressLoaded(); // 基于已有状态更新指定字段 emit(currentLoadedState.copyWith(currentLocation: event.currentLocation)); logger(state); }
2. 修复_getGeoAddress的状态更新逻辑
同样基于当前状态更新,同时优化错误处理,避免错误后清空原有状态:
void _getGeoAddress(GetGeoAddress event, Emitter<AddressState> emit) async { if (state is GetGeoAddressLoading) return; // 提前保存当前的Loaded状态,避免加载过程中丢失数据 final prevLoadedState = state is AddressLoaded ? state as AddressLoaded : const AddressLoaded(); emit(GetGeoAddressLoading()); try { Map<String, dynamic> geoAddress = await AddressServices.getGeoAddress(event.currentLocation); // 基于保存的状态更新geoAddress字段 emit(prevLoadedState.copyWith(geoAddress: geoAddress)); } catch (e) { logger(e.toString()); // 错误时先抛出错误状态,再恢复之前的Loaded状态 emit(GetGeoAddressError(e.toString())); emit(prevLoadedState); } }
3. 统一其他事件的状态更新逻辑
对于AddAddress、UpdateAddress等需要修改AddressLoaded的事件,都要遵循“基于当前状态更新”的逻辑:
void _addAddress(AddAddress event, Emitter<AddressState> emit) async { try { await AddressServices.addAddress(event.address); final currentLoadedState = state is AddressLoaded ? state as AddressLoaded : const AddressLoaded(); // 生成新的地址列表,避免直接修改原列表 final newAddresses = [...currentLoadedState.addresses, event.address]; // 重新计算默认地址 final newDefaultAddress = newAddresses.firstWhereOrNull((addr) => addr.addressDefault == true); emit(currentLoadedState.copyWith(addresses: newAddresses, defaultAddress: newDefaultAddress)); } catch (e) { emit(AddressError(e.toString())); } }
额外优化
可以给AddressState添加扩展方法,简化状态转换逻辑:
extension AddressStateExt on AddressState { AddressLoaded toLoaded() { return this is AddressLoaded ? this as AddressLoaded : const AddressLoaded(); } }
之后在事件处理中可以直接写state.toLoaded().copyWith(...),代码更简洁。
内容的提问来源于stack exchange,提问作者Nikhil
相关产品推荐
相关产品推荐

