Flutter Bloc库错误捕获异常:try-catch无法捕获Stream错误
Bloc中try-catch无法捕获Stream错误的解决方案
你的问题核心是:excursionss()返回的是Stream对象,而Stream的错误只会在订阅阶段触发,你当前的代码只是通过await获取了Stream实例,并没有实际订阅它,所以错误不会进入try-catch块。后续把Stream emit到State中,错误会在外部订阅时才出现,此时已经脱离了Bloc的错误捕获范围。
问题代码分析
excursionss()里的asyncMap和handleError都是为Stream添加错误处理逻辑,但这些逻辑要等到有人订阅Stream时才会执行。- Bloc的
_downloadExcursions方法中,await excursionRepository.excursionss()只是拿到了Stream对象,没有触发订阅,所以错误不会在这里抛出。
修复方案
方案1:仅需单次获取数据(放弃实时更新)
如果不需要监听Firestore数据的实时变化,只需要单次拉取,可以将Stream转为Future,等待第一个结果:
FutureOr<void> _downloadExcursions( DownloadDataEvent event, Emitter<DownloadDataState> emit) async { emit(OnDownloadDataState()); try { // 等待Stream的第一个数据,同时捕获错误 final excursions = await excursionRepository.excursionss().first; emit(DataFetchedState(excursions: excursions)); } catch (error) { emit(ErrorDownloadDataState(error)); } }
如果业务场景允许,更推荐直接修改excursionss()返回Future类型,逻辑更清晰:
Future<List<Excursion>> excursionss() async { final snapshot = await firebaseFirestore.collection('Excursions').get(); List<Excursion> excursions = []; for (var excursionSnapshot in snapshot.docs) { try { Excursion excursion = await excursionMapper.fromFirebaseAsync(excursionSnapshot.data()); excursions.add(excursion); } catch (e) { throw ExcursionRepositoryException(e); } } return excursions; }
方案2:需要实时监听数据更新(保留Stream特性)
如果要保留Firestore的实时更新能力,需要在Bloc内部订阅Stream,直接处理数据和错误:
class InitialDataBloc extends Bloc<DownloadDataEvent, DownloadDataState> { final ExcursionMapper excursionMapper; final FirebaseFirestore firebaseFirestore; final ExcursionRepository excursionRepository; StreamSubscription? _excursionSubscription; // 管理订阅实例 InitialDataBloc({ required this.firebaseFirestore, required this.excursionRepository, required this.excursionMapper, }) : super(OnDownloadDataState()) { on<DownloadDataEvent>(_downloadExcursions); } FutureOr<void> _downloadExcursions( DownloadDataEvent event, Emitter<DownloadDataState> emit) async { emit(OnDownloadDataState()); // 先取消之前的订阅,避免重复监听 await _excursionSubscription?.cancel(); // 订阅Stream,处理数据和错误 _excursionSubscription = excursionRepository.excursionss().listen( (excursions) { emit(DataFetchedState(excursions: excursions)); }, onError: (error) { emit(ErrorDownloadDataState(error)); }, ); } @override Future<void> close() { // Bloc销毁时取消订阅,防止内存泄漏 _excursionSubscription?.cancel(); return super.close(); } void downloadExcursion() => add(OnDownloadDataEvent()); }
同时可以简化excursionss()的错误处理,因为asyncMap里已经抛出了自定义异常,无需额外的handleError:
Stream<List<Excursion>> excursionss() { return firebaseFirestore .collection('Excursions') .snapshots() .asyncMap((snapshot) async { List<Excursion> excursions = []; for (var excursionSnapshot in snapshot.docs) { try { Excursion excursion = await excursionMapper.fromFirebaseAsync(excursionSnapshot.data()); excursions.add(excursion); } catch (e) { throw ExcursionRepositoryException(e); } } return excursions; }); }
内容的提问来源于stack exchange,提问作者Alessandro Scorrano
相关产品推荐
相关产品推荐

