Google登录触发StateError:调用close后无法发射新状态
问题原因分析
这个异常的核心是Bloc已被关闭后仍尝试发射新状态。结合你的场景,大概率是以下流程导致的:
- Google登录成功后,代码先发射了
FormzSubmissionStatus.success状态 - 该状态触发页面跳转至主页,登录页面的Bloc随之被销毁(调用
close方法) - 但
createUserEntry是异步操作,此时还在后台执行,完成后它尝试发射状态时,Bloc已经关闭,因此抛出"Cannot emit new states after calling close"异常
代码排查方向
检查你的logInWithGoogle方法逻辑,是否存在以下情况:
- 登录成功后立即发射success状态并触发导航,未等待
createUserEntry执行完成 createUserEntry内部或执行完成后,尝试调用emit更新Bloc状态
解决方案
根据你的需求(为首次登录用户存储补充数据),可按以下两种方式调整:
方案1:等待数据写入完成后再发射success状态
修改logInWithGoogle流程,确保createUserEntry执行完成后,再发射success状态并触发导航:
Future<void> logInWithGoogle() async { try { emit(state.copyWith(status: FormzSubmissionStatus.inProgress)); // 执行Google登录 final user = await _authenticationRepository.logInWithGoogle(); // 检查是否为首次登录,写入补充数据 final isFirstLogin = await _checkIfFirstLogin(user.uid); if (isFirstLogin) { await _firestoreRepository.createUserEntry( userId: user.uid, firstName: user.displayName?.split(' ')[0] ?? '', lastName: user.displayName?.split(' ')[1] ?? '', // 其他补充字段 ); } // 所有操作完成后再发射success状态 emit(state.copyWith(status: FormzSubmissionStatus.success)); // 触发主页导航 _navigationRepository.navigateToHome(); } catch (_) { emit(state.copyWith(status: FormzSubmissionStatus.failure)); } }
这种方式能保证所有异步操作完成后再更新状态和导航,避免Bloc提前关闭。
方案2:将数据写入操作移至Bloc外部,不依赖当前登录Bloc的状态发射
如果不需要在登录Bloc中跟踪数据写入状态,可把createUserEntry放在导航之后,或交给主页Bloc处理:
- 登录成功后直接发射success状态并导航
- 主页初始化时,检查当前用户是否存在补充数据,不存在则执行写入操作
// 登录Bloc中的logInWithGoogle方法 Future<void> logInWithGoogle() async { try { emit(state.copyWith(status: FormzSubmissionStatus.inProgress)); final user = await _authenticationRepository.logInWithGoogle(); // 直接发射success并导航 emit(state.copyWith(status: FormzSubmissionStatus.success)); _navigationRepository.navigateToHome(); // 后台执行写入操作,不依赖当前Bloc状态 unawaited(_checkAndCreateUserEntry(user)); } catch (_) { emit(state.copyWith(status: FormzSubmissionStatus.failure)); } } // 单独的异步方法,不触发emit Future<void> _checkAndCreateUserEntry(User user) async { final isFirstLogin = await _checkIfFirstLogin(user.uid); if (isFirstLogin) { await _firestoreRepository.createUserEntry( userId: user.uid, firstName: user.displayName?.split(' ')[0] ?? '', lastName: user.displayName?.split(' ')[1] ?? '', ); } }
这里用unawaited让写入操作在后台执行,不阻塞登录流程,同时避免在已关闭的Bloc中发射状态。
额外注意点
- 确保
_checkIfFirstLogin方法能准确判断用户是否为首次登录(比如检查Firestore中是否存在该用户的文档) - 如果需要展示数据写入的加载状态,可在主页中单独处理,不要复用登录Bloc的状态
内容的提问来源于stack exchange,提问作者CuriousCat
相关产品推荐
相关产品推荐

