You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google登录触发StateError:调用close后无法发射新状态

问题原因分析

这个异常的核心是Bloc已被关闭后仍尝试发射新状态。结合你的场景,大概率是以下流程导致的:

  1. Google登录成功后,代码先发射了FormzSubmissionStatus.success状态
  2. 该状态触发页面跳转至主页,登录页面的Bloc随之被销毁(调用close方法)
  3. 但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处理:

  1. 登录成功后直接发射success状态并导航
  2. 主页初始化时,检查当前用户是否存在补充数据,不存在则执行写入操作
// 登录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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 18:42:43