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

Flutter集成Firebase登录登出时遇组件卸载及Build期setState调用错误

问题:Flutter Firebase 登录/登出时的状态管理错误

出现的错误

  1. 登出时触发:

This widget has been unmounted, so the State no longer has a context (and should be considered defunct).

  1. 上述错误后登录触发:

setState() or markNeedsBuild() called during build.

提供的代码

登出代码

Future<void> signOut() async {
    if (googleSignIn.currentUser != null){
      await googleSignIn.disconnect();
      await FirebaseAuth.instance.signOut();}
    else{
      await FirebaseAuth.instance.signOut();
  }
  
    WidgetsBinding.instance.addPostFrameCallback((_) {
      Navigator.pushAndRemoveUntil(
        context,
        MaterialPageRoute(
          builder: (BuildContext context) => const AuthPage(),
        ),
            (route) => false,
      );
    });

  }

登录代码

Future googleLogin() async{
     timer1 = Timer(const Duration(milliseconds: 1500), (){
       Navigator.of(context, rootNavigator: true).pop();
     });
     showDialog(context: context,
       barrierDismissible: false,
       builder: (context) =>   Center(
         child: SizedBox(
           width: 150, height: 150,
           child: LiquidCircularProgressIndicator(
             backgroundColor: const Color(0xFF5B84B1),
             valueColor: const AlwaysStoppedAnimation(Colors.white),
           ),
         ),
       ),
     ).then((value) {
       timer1!.cancel();
     });
     try {
       final googleUser =
       await GoogleSignIn(scopes: ['profile', 'email']).signIn();
       if (googleUser == null) return;
       _user = googleUser;

       final googleAuth = await googleUser.authentication;

       final credential = GoogleAuthProvider.credential(
         accessToken: googleAuth.accessToken,
         idToken: googleAuth.idToken,
       );

       UserCredential userCredential = await _auth.signInWithCredential(
           credential);

       if (userCredential.additionalUserInfo!.isNewUser == true) {
         if (!mounted) return;

         Navigator.pushAndRemoveUntil(
           context,
           MaterialPageRoute(
             builder: (BuildContext context) => const ProfilePage(),
           ),
               (route) => false,
         );
       }
       else {
         if (!mounted) return;

         Navigator.pushAndRemoveUntil(
           context,
           MaterialPageRoute(
             builder: (BuildContext context) => const HomeScreen(),
           ),
               (route) => false,
         );
       }
     } on PlatformException catch (e) {
       Utils.showSnackBar(e.toString());
     }

 }

解决方案

1. 修复登出代码的问题

核心问题是异步操作完成后未检查widget是否仍处于挂载状态,直接使用失效context导航。修改如下:

Future<void> signOut() async {
  // 统一执行登出操作
  if (googleSignIn.currentUser != null) {
    await googleSignIn.disconnect();
  }
  await FirebaseAuth.instance.signOut();

  // 先确认widget仍挂载,再执行导航
  if (!mounted) return;
  
  WidgetsBinding.instance.addPostFrameCallback((_) {
    Navigator.pushAndRemoveUntil(
      context,
      MaterialPageRoute(builder: (_) => const AuthPage()),
      (route) => false,
    );
  });
}

2. 修复登录代码的问题

问题出在定时器回调可能在widget卸载后执行,以及对话框关闭后的回调未处理状态。修改并补充资源清理逻辑:

Future googleLogin() async {
  timer1 = Timer(const Duration(milliseconds: 1500), () {
    // 先检查widget状态再关闭对话框
    if (mounted) {
      Navigator.of(context, rootNavigator: true).pop();
    }
  });

  await showDialog(
    context: context,
    barrierDismissible: false,
    builder: (context) => const Center(
      child: SizedBox(
        width: 150, height: 150,
        child: LiquidCircularProgressIndicator(
          backgroundColor: Color(0xFF5B84B1),
          valueColor: AlwaysStoppedAnimation(Colors.white),
        ),
      ),
    ),
  );
  
  // 对话框关闭后立即取消定时器
  timer1?.cancel();

  try {
    final googleUser = await GoogleSignIn(scopes: ['profile', 'email']).signIn();
    if (googleUser == null) return;
    _user = googleUser;

    final googleAuth = await googleUser.authentication;
    final credential = GoogleAuthProvider.credential(
      accessToken: googleAuth.accessToken,
      idToken: googleAuth.idToken,
    );

    final userCredential = await _auth.signInWithCredential(credential);

    if (!mounted) return;

    // 简化页面导航逻辑
    final targetPage = userCredential.additionalUserInfo!.isNewUser 
        ? const ProfilePage() 
        : const HomeScreen();

    Navigator.pushAndRemoveUntil(
      context,
      MaterialPageRoute(builder: (_) => targetPage),
      (route) => false,
    );
  } on PlatformException catch (e) {
    // 确保widget挂载时再显示错误提示
    if (mounted) {
      Utils.showSnackBar(e.toString());
    }
  }
}

// 在State类中添加资源清理逻辑
@override
void dispose() {
  timer1?.cancel();
  super.dispose();
}

额外提示

  • 所有涉及context或UI更新的操作前,必须检查mounted状态
  • 定时器、流订阅等异步资源务必在dispose方法中清理,避免内存泄漏和错误回调

内容的提问来源于stack exchange,提问作者taha khamis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 19:48:17