Flutter集成Firebase登录登出时遇组件卸载及Build期setState调用错误
问题:Flutter Firebase 登录/登出时的状态管理错误
出现的错误
- 登出时触发:
This widget has been unmounted, so the State no longer has a context (and should be considered defunct).
- 上述错误后登录触发:
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
相关产品推荐
相关产品推荐

