Flutter Bloc emit调用报错:事件处理完成后调用emit问题求助
解决Bloc中emit调用时机错误问题
问题场景
实现聊天功能(未使用WebSocket,后端运行正常),编写的ChatBloc代码如下:
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:moms/features/chat/domain/entities/message_entity.dart'; import 'package:moms/features/chat/domain/usecases/send_message.dart'; import 'package:moms/features/chat/domain/usecases/get_messages.dart'; import 'package:moms/features/chat/domain/usecases/get_or_create_thread_usecase.dart'; part 'chat_event.dart'; part 'chat_state.dart'; class ChatBloc extends Bloc<ChatEvent, ChatState> { final GetOrCreateThread getOrCreateThread; final GetMessages getMessages; final SendMessage sendMessage; ChatBloc({ required this.getOrCreateThread, required this.getMessages, required this.sendMessage, }) : super(ChatInitial()) { on<LoadChatThreadEvent>(_onLoadChatThreadEvent); on<LoadMessagesEvent>(_onLoadMessagesEvent); on<SendMessageEvent>(_onSendMessageEvent); } Future<void> _onLoadChatThreadEvent( LoadChatThreadEvent event, Emitter<ChatState> emit) async { emit(ChatLoading()); final threadResult = await getOrCreateThread(event.gigId, event.bidId); threadResult.fold( (failure) => emit(ChatError('Failed to load chat thread: ${failure.message}')), (threadId) async { await _loadMessagesForThread(threadId, emit); }, ); } Future<void> _onLoadMessagesEvent( LoadMessagesEvent event, Emitter<ChatState> emit) async { emit(ChatLoading()); await _loadMessagesForThread(event.threadId, emit); } Future<void> _onSendMessageEvent( SendMessageEvent event, Emitter<ChatState> emit) async { emit(ChatLoading()); final sendResult = await sendMessage(event.threadId, event.content, event.senderId); sendResult.fold( (failure) => emit(ChatError('Failed to send message: ${failure.message}')), (message) async { await _loadMessagesForThread(event.threadId, emit); }, ); } Future<void> _loadMessagesForThread( int threadId, Emitter<ChatState> emit) async { final messagesResult = await getMessages(threadId); messagesResult.fold( (failure) => emit(ChatError('Failed to load messages: ${failure.message}')), (messages) => emit(ChatLoaded(threadId: threadId, messages: messages)), ); } }
运行后反复出现如下报错:
E/flutter (18794): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: 'package:bloc/src/emitter.dart': Failed assertion: line 114 pos 7: '!_isCompleted': E/flutter (18794): emit was called after an event handler completed normally. E/flutter (18794): This is usually due to an unawaited future in an event handler. E/flutter (18794): Please make sure to await all asynchronous operations with event handlers E/flutter (18794): and use emit.isDone after asynchronous operations before calling emit() to E/flutter (18794): ensure the event handler has not completed. E/flutter (18794): E/flutter (18794): **BAD** E/flutter (18794): on<Event>((event, emit) { E/flutter (18794): future.whenComplete(() => emit(...)); E/flutter (18794): }); E/flutter (18794): E/flutter (18794): **GOOD** E/flutter (18794): on<Event>((event, emit) async { E/flutter (18794): await future.whenComplete(() => emit(...)); E/flutter (18794): }); E/flutter (18794): E/flutter (18794): #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:50:61) E/flutter (18794): #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:40:5) E/flutter (18794): #2 _Emitter.call (package:bloc/src/emitter.dart:114:7) E/flutter (18794): #3 ChatBloc._loadMessagesForThread.<anonymous closure> (package:project/features/chat/presentation/bloc/chat_bloc.dart:66:25) E/flutter (18794): #4 Right.fold (package:dartz/src/either.dart:200:64) E/flutter (18794): #5 ChatBloc._loadMessagesForThread (package:project/features/chat/presentation/bloc/chat_bloc.dart:63:20) E/flutter (18794): <asynchronous suspension> E/flutter (18794): #6 ChatBloc._onLoadChatThreadEvent.<anonymous closure> (package:project/features/chat/presentation/bloc/chat_bloc.dart:34:9) E/flutter (18794): <asynchronous suspension>
错误原因
问题出在fold方法的使用上:fold是同步方法,当你在Right分支传入一个async匿名函数时,该函数会返回一个Future,但fold不会等待这个Future完成。这会导致事件处理器(比如_onLoadChatThreadEvent)在fold执行完毕后就提前结束,而后续_loadMessagesForThread中的emit调用会在事件处理器已经完成后执行,触发Bloc的断言检查。
修复方案
将fold替换为显式的Left/Right类型判断,这样可以直接await异步操作,确保事件处理器等待所有异步逻辑完成后再结束:
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:moms/features/chat/domain/entities/message_entity.dart'; import 'package:moms/features/chat/domain/usecases/send_message.dart'; import 'package:moms/features/chat/domain/usecases/get_messages.dart'; import 'package:moms/features/chat/domain/usecases/get_or_create_thread_usecase.dart'; import 'package:dartz/dartz.dart'; part 'chat_event.dart'; part 'chat_state.dart'; class ChatBloc extends Bloc<ChatEvent, ChatState> { final GetOrCreateThread getOrCreateThread; final GetMessages getMessages; final SendMessage sendMessage; ChatBloc({ required this.getOrCreateThread, required this.getMessages, required this.sendMessage, }) : super(ChatInitial()) { on<LoadChatThreadEvent>(_onLoadChatThreadEvent); on<LoadMessagesEvent>(_onLoadMessagesEvent); on<SendMessageEvent>(_onSendMessageEvent); } Future<void> _onLoadChatThreadEvent( LoadChatThreadEvent event, Emitter<ChatState> emit) async { emit(ChatLoading()); final threadResult = await getOrCreateThread(event.gigId, event.bidId); if (threadResult is Left) { final failure = threadResult.value; emit(ChatError('Failed to load chat thread: ${failure.message}')); } else { final threadId = (threadResult as Right).value; await _loadMessagesForThread(threadId, emit); } } Future<void> _onLoadMessagesEvent( LoadMessagesEvent event, Emitter<ChatState> emit) async { emit(ChatLoading()); await _loadMessagesForThread(event.threadId, emit); } Future<void> _onSendMessageEvent( SendMessageEvent event, Emitter<ChatState> emit) async { emit(ChatLoading()); final sendResult = await sendMessage(event.threadId, event.content, event.senderId); if (sendResult is Left) { final failure = sendResult.value; emit(ChatError('Failed to send message: ${failure.message}')); } else { await _loadMessagesForThread(event.threadId, emit); } } Future<void> _loadMessagesForThread( int threadId, Emitter<ChatState> emit) async { final messagesResult = await getMessages(threadId); if (messagesResult is Left) { final failure = messagesResult.value; emit(ChatError('Failed to load messages: ${failure.message}')); } else { final messages = (messagesResult as Right).value; emit(ChatLoaded(threadId: threadId, messages: messages)); } } }
额外说明
如果坚持使用fold,可以将分支函数改为返回Future,并通过await整个fold的结果,但这种写法可读性较差:
// 以_onLoadChatThreadEvent为例 await threadResult.fold( (failure) async { emit(ChatError('Failed to load chat thread: ${failure.message}')); }, (threadId) async { await _loadMessagesForThread(threadId, emit); }, );
显式的类型判断代码更直观,更适合新手维护。
内容的提问来源于stack exchange,提问作者Nishant
相关产品推荐
相关产品推荐

