Flutter登录状态重启时出现已卸载Widget状态上下文错误求助
登录状态判断与页面跳转错误分析及解决
问题背景
预期逻辑:
- 保持登录状态重启应用,用户直接进入
BottomNav页面 - 退出登录后重启,用户先看到
Splash启动页
添加Splash后,已登录状态下重启应用出现以下错误:
已发生异常。FlutterError(此Widget已被卸载,因此State不再拥有上下文(应视为失效)。请考虑在"dispose"期间取消所有活跃任务,或使用"mounted" getter判断State是否仍处于活跃状态。)
相关代码
main.dart
import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:recipe_collector/Authen/InfoPage.dart'; import 'package:recipe_collector/Authen/email_Login.dart'; import 'package:recipe_collector/splash_screen.dart'; import 'All Recipes/viewRecipe.dart'; import 'BottomNavigationBar.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:recipe_collector/globalVariable.dart' as global; import 'Profile/Profile.dart'; import 'globalVariable.dart'; Future main() async { runApp(MyApp()); } class MyApp extends StatelessWidget { MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return FutureBuilder<FirebaseUser>( future: FirebaseAuth.instance.currentUser(), builder: (context, snapshot) { if (snapshot.hasData) { FirebaseUser? user = snapshot.data; if (user != null) { getCurrentUser(); } return MaterialApp( debugShowCheckedModeBanner: false, title: 'Recipes Collector ', initialRoute: "/", home: BottomNav(), ); } else { return Splash(); } }); } }
Splash.dart
import 'dart:async'; import 'dart:ui'; import 'package:google_fonts/google_fonts.dart'; import 'package:splashscreen/splashscreen.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:liquid_swipe/liquid_swipe.dart'; import 'Authen/InfoPage.dart'; class Splash extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: SplashScreen( seconds: 3, navigateAfterSeconds: MyliquidSwipe(), styleTextUnderTheLoader: TextStyle(), title: Text.rich( textAlign: TextAlign.center, TextSpan( children: [ TextSpan( text: 'Access Your Recipes Collection', style: TextStyle( fontSize: 18, color: Color.fromARGB( 255, 230, 200, 160, ), letterSpacing: 1, )), TextSpan( text: '\nAnytime & Anywhere', style: TextStyle( letterSpacing: 2, fontSize: 23, fontWeight: FontWeight.w600, height: 1.5, color: Color.fromARGB(255, 230, 200, 160), )), TextSpan( text: '\nJust at Your Fingertip!', style: TextStyle( letterSpacing: 1, fontSize: 18, color: Color.fromARGB(255, 230, 200, 160))) ], ), ), image: Image.asset("assets/Logo.png"), photoSize: 150.0, imageBackground: AssetImage("assets/IntroPage/Create Your Own Recipe.png"), loaderColor: Color.fromARGB(255, 230, 200, 160), ), ); } }
错误成因
- 多MaterialApp实例冲突:
MyApp根据登录状态返回不同的MaterialApp,已登录时返回的MaterialApp会替换掉Splash中的MaterialApp,但Splash内部的SplashScreen仍在执行3秒后的导航任务,试图在已被卸载的上下文里跳转,导致错误。 - 异步判断的短暂状态切换:
FutureBuilder获取用户状态是异步操作,在结果返回前会短暂进入else分支渲染Splash,之后切换到已登录页面,此时Splash的导航任务未被取消,继续执行引发上下文失效。 - build方法内执行异步操作:在
MyApp的build方法中直接调用getCurrentUser(),build方法是同步执行的,异步操作可能在Widget未稳定完成渲染时执行,引发上下文相关问题。
解决方法
1. 全局仅保留一个MaterialApp
将MaterialApp提升到FutureBuilder外层,确保整个应用只有一个导航上下文,避免多实例冲突:
import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; // 其他导入保持不变 Future main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // 必须初始化Firebase runApp(MyApp()); } class MyApp extends StatelessWidget { MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Recipes Collector ', home: FutureBuilder<User?>( // 用authStateChanges().first替代currentUser(),状态监听更可靠 future: FirebaseAuth.instance.authStateChanges().first, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { // 显示过渡加载页,避免短暂闪显Splash return Scaffold(body: Center(child: CircularProgressIndicator(color: Color.fromARGB(255, 230, 200, 160)))); } if (snapshot.hasData && snapshot.data != null) { // 用Future.microtask延迟执行异步操作,避免在build中直接调用 Future.microtask(() => getCurrentUser()); return BottomNav(); } else { return Splash(); } }, ), ); } }
2. 移除Splash中的MaterialApp
因为外层已经有全局的MaterialApp,Splash只需返回SplashScreen即可:
import 'package:flutter/material.dart'; import 'package:splashscreen/splashscreen.dart'; import 'Authen/InfoPage.dart'; class Splash extends StatelessWidget { @override Widget build(BuildContext context) { return SplashScreen( seconds: 3, navigateAfterSeconds: MyliquidSwipe(), styleTextUnderTheLoader: TextStyle(), title: Text.rich( textAlign: TextAlign.center, TextSpan( children: [ TextSpan( text: 'Access Your Recipes Collection', style: TextStyle( fontSize: 18, color: Color.fromARGB(255, 230, 200, 160), letterSpacing: 1, )), TextSpan( text: '\nAnytime & Anywhere', style: TextStyle( letterSpacing: 2, fontSize: 23, fontWeight: FontWeight.w600, height: 1.5, color: Color.fromARGB(255, 230, 200, 160), )), TextSpan( text: '\nJust at Your Fingertip!', style: TextStyle( letterSpacing: 1, fontSize: 18, color: Color.fromARGB(255, 230, 200, 160))) ], ), ), image: Image.asset("assets/Logo.png"), photoSize: 150.0, imageBackground: AssetImage("assets/IntroPage/Create Your Own Recipe.png"), loaderColor: Color.fromARGB(255, 230, 200, 160), ); } }
3. 优化异步操作执行时机
如果getCurrentUser()内部包含异步操作,确保在执行时判断上下文是否活跃(如果是在StatefulWidget中调用),或者用Future.microtask延迟执行,避免干扰Widget渲染流程。
额外注意事项
- 确保Firebase初始化完成:在
main方法中必须调用WidgetsFlutterBinding.ensureInitialized()和await Firebase.initializeApp(),否则Firebase相关操作会报错。 - Firebase版本兼容:新版本Firebase Auth中
currentUser()已被authStateChanges()替代,FirebaseUser类也改为User,需根据实际版本调整代码。
内容的提问来源于stack exchange,提问作者user19402977
相关产品推荐
相关产品推荐

