Flutter应用长时间闲置/恢复时偶现启动页卡顿求助
Flutter启动页卡顿问题解决方案
针对你的应用在长时间闲置或从最近应用列表恢复时卡在启动页的问题,结合提供的代码,以下是问题根源分析和针对性修复方案:
问题根源
- 主线程阻塞:
main函数中同步等待Firebase.initializeApp(),长时间闲置后Firebase重建连接的耗时操作会阻塞UI主线程,导致启动页无法及时响应。 - Build方法执行异步逻辑:
SplashScreenPage作为无状态组件,在build方法中直接触发isUserAuthenticated()异步操作,违反Flutter构建原则,且可能因组件多次重建重复执行逻辑,引发状态紊乱。 - Auth校验逻辑不严谨:仅通过同步获取
currentUser判断登录状态,长时间闲置后用户会话可能已失效,但缓存的currentUser值会导致校验逻辑卡住或返回错误结果。 - 后台恢复状态异常:应用从后台恢复时,部分依赖注入实例可能被系统回收,导致登录校验无法正常执行。
修复方案
1. 异步初始化Firebase,避免阻塞主线程
将Firebase初始化移至启动页业务逻辑中,先启动App显示启动页,再在后台完成初始化,避免主线程被阻塞:
修改main.dart:
Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); configureInjections(); // 先启动App显示启动页,再后台初始化Firebase runApp(App()); Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ).then((_) { FirebaseMessaging.onBackgroundMessage(backgroundHandler); }); }
更新SplashScreenCubit,确保Firebase初始化完成后再执行校验:
@injectable class SplashScreenCubit extends Cubit<SplashScreenState> { final IsUserAuthenticatedUseCase _isUserAuthenticatedUseCase; SplashScreenCubit(this._isUserAuthenticatedUseCase) : super(Initial()); Future<void> isUserAuthenticated() async { print("Called isUserAuthenticated on the splash screen"); try { // 确保Firebase已初始化 await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); final isUserAuthenticated = await _isUserAuthenticatedUseCase.execute(NoParams()); emit(isUserAuthenticated ? Authenticated() : Unauthenticated()); } catch (e) { print("Splash auth error: $e"); emit(Unauthenticated()); } } }
2. 重构Splash页面为StatefulWidget,规范异步逻辑触发
将SplashScreenPage改为有状态组件,在initState中仅执行一次登录校验,避免在build方法中执行副作用操作:
class SplashScreenPage extends StatefulWidget { @override _SplashScreenPageState createState() => _SplashScreenPageState(); } class _SplashScreenPageState extends State<SplashScreenPage> { late SplashScreenCubit _cubit; @override void initState() { super.initState(); _cubit = getIt<SplashScreenCubit>(); // 仅在组件初始化时执行一次校验 _cubit.isUserAuthenticated(); } @override Widget build(BuildContext context) { return BlocProvider.value( value: _cubit, child: BlocConsumer<SplashScreenCubit, SplashScreenState>( listener: (context, state) { if (state is Authenticated) { PageNavigator.navigateAndRemoveHistory(context, HomePage()); } if (state is Unauthenticated || state is SplashError) { PageNavigator.navigateAndRemoveHistory(context, LogInPage()); } }, builder: (context, state) => SplashScreenContent(), ), ); } }
3. 优化Firebase Auth校验逻辑,确保会话有效性
强制刷新用户token,验证会话是否真实有效,避免缓存值导致的错误判断:
修改仓库的isAuthenticated()方法:
@override Future<bool> isAuthenticated() async { try { final currentUser = _firebaseAuth.currentUser; if (currentUser == null || !currentUser.emailVerified) { return false; } // 强制刷新token,确认会话未过期 await currentUser.getIdToken(true); return true; } catch (e) { print("Auth check error: $e"); return false; } }
4. 监听应用生命周期,处理后台恢复状态
在App组件中添加生命周期监听,当应用从后台恢复时,重新刷新Auth状态:
class App extends StatefulWidget { @override _AppState createState() => _AppState(); } class _AppState extends State<App> with WidgetsBindingObserver { @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void didChangeAppLifecycleState(AppLifecycleState state) { super.didChangeAppLifecycleState(state); if (state == AppLifecycleState.resumed) { // 应用恢复时刷新Auth状态 _refreshAuthState(); } } Future<void> _refreshAuthState() async { await FirebaseAuth.instance.currentUser?.reload(); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override Widget build(BuildContext context) => MultiBlocProvider( providers: [ BlocProvider(create: (context) => getIt<SplashScreenCubit>()), BlocProvider(create: (context) => getIt<HomeCubit>()), BlocProvider(create: (context) => getIt<LogInCubit>()), BlocProvider(create: (context) => getIt<SettingsCubit>()) ], child: OverlaySupport( child: MaterialApp( debugShowCheckedModeBanner: false, title: 'SAY', theme: appTheme, home: SplashScreenPage(), ), ), ); }
调试建议
- 在
isUserAuthenticated()和isAuthenticated()的关键节点添加日志,定位卡顿发生的具体环节。 - 使用Flutter DevTools的Performance面板分析启动时的主线程阻塞情况,找到耗时操作。
- 测试时可开启开发者选项的“不保留活动”功能,快速模拟应用长时间闲置后的恢复场景。
内容的提问来源于stack exchange,提问作者Zookey
相关产品推荐
相关产品推荐

