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

Flutter无状态启动页导航报错:访问已销毁Widget祖先不安全

问题:无状态启动页导航时触发已销毁Context报错

我实现了一个无状态的Splash Screen(启动页),用于检测用户登录状态:已登录则跳转主首页,未登录则跳转登录页。功能可正常运行,但出现以下报错:

E/flutter (21294): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Looking up a deactivated widget's ancestor is unsafe.
E/flutter (21294): At this point the state of the widget's element tree is no longer stable.
E/flutter (21294): To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
E/flutter (21294): #0      Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> (package:flutter/src/widgets/framework.dart:4241:9)
E/flutter (21294): #1      Element._debugCheckStateIsActiveForAncestorLookup (package:flutter/src/widgets/framework.dart:4255:6)
E/flutter (21294): #2      Element.findAncestorStateOfType (package:flutter/src/widgets/framework.dart:4322:12)
E/flutter (21294): #3      Navigator.of (package:flutter/src/widgets/navigator.dart:2549:40)
E/flutter (21294): #4      SplashScreen.build.<anonymous closure> (package:fbissalama/Screens/splashscreen.dart:20:18)
E/flutter (21294): #5      Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
E/flutter (21294): #6      _Timer._runTimers (dart:isolate-patch/timer_impl.dart:398:19)
E/flutter (21294): #7      _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:429:5)
E/flutter (21294): #8      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)

相关代码

main.dart

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider<Following>(
          create: (BuildContext context) => Following(),
        ),
        ChangeNotifierProvider<ProviderController>(
          create: (BuildContext context) => ProviderController(),
        ),
        ChangeNotifierProvider<Verifying>(
          create: (BuildContext context) => Verifying(),
        ),
        //TODO
        //Add AuthController Here...
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        title: "Bissalama",
        onGenerateRoute: onGenerate,
        initialRoute: AppRoutes.splashScreenPage,
        routes: {
          AppRoutes.loginPage: (context) => const SignInScreen(),
          AppRoutes.splashScreenPage: (context) => const SplashScreen(),
          AppRoutes.mainHomePage: (context) => const MainHome(),
          AppRoutes.addJourneyPage: (context) => const AddJourneyPage(),
          AppRoutes.currentJourneyPage: (context) => const CurrentJourneyPage(),
          AppRoutes.favoriteJourneyPage: (context) => const FavoritePage(),
          AppRoutes.settingsPage: (context) => const SettingsPage(),
          AppRoutes.aboutPage: (context) => const AboutPage(),
        },
      ),
    ),
  );
}

splashscreen.dart

class SplashScreen extends StatelessWidget {
  const SplashScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    Timer(const Duration(seconds: 1), () {
      final String? user = FirebaseAuth.instance.currentUser?.phoneNumber;
     final String? user1 = FirebaseAuth.instance.currentUser?.email;
     if (user1 != null || user != null) {
       Navigator.of(context).pushReplacementNamed('/home');
     } else {
       Navigator.of(context).pushReplacementNamed('/');
     }
    });
    return Scaffold(
      body: Container(
        color: Colors.blueAccent,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Center(
                child: Image.asset(
                  AppAssets.busIcon,
                  fit: BoxFit.fitHeight,
                ),
              ),
              const SizedBox(
                height: 20,
              ),
              Text(
                "Bissalama",
                style: GoogleFonts.lobster(
                  textStyle: const TextStyle(
                      color: Colors.white,
                      fontSize: 30,
                      fontWeight: FontWeight.bold),
                ),
              ),
              const SizedBox(
                height: 20,
              ),
              const SpinKitPouringHourGlassRefined(
                  color: Colors.white, size: 50.0),
            ],
          ),
      ),
    );
  }
}

问题原因

你推测的结论正确:Timer延迟执行时,SplashScreen组件可能已经被从组件树中移除(比如页面快速切换、系统资源回收),此时使用已销毁的context调用Navigator就会触发该错误。

无状态组件下的解决方案

方案1:调用导航前检查Context状态

通过ModalRoute.of(context) != null判断当前context是否仍关联活跃路由,避免使用已销毁的context:

@override
Widget build(BuildContext context) {
  Timer(const Duration(seconds: 1), () {
    final String? user = FirebaseAuth.instance.currentUser?.phoneNumber;
    final String? user1 = FirebaseAuth.instance.currentUser?.email;
    // 检查context是否仍处于活跃状态
    if (ModalRoute.of(context) != null) {
      if (user1 != null || user != null) {
        Navigator.of(context).pushReplacementNamed('/home');
      } else {
        Navigator.of(context).pushReplacementNamed('/');
      }
    }
  });
  // 原有UI代码不变
  return Scaffold(...);
}

方案2:使用PostFrameCallback替代Timer

WidgetsBinding.instance.addPostFrameCallback会在当前帧绘制完成后执行,结合Future.delayed模拟启动页展示时间,此时组件状态更稳定:

@override
Widget build(BuildContext context) {
  WidgetsBinding.instance.addPostFrameCallback((_) {
    Future.delayed(const Duration(seconds: 1), () {
      final String? user = FirebaseAuth.instance.currentUser?.phoneNumber;
      final String? user1 = FirebaseAuth.instance.currentUser?.email;
      if (ModalRoute.of(context) != null) {
        if (user1 != null || user != null) {
          Navigator.of(context).pushReplacementNamed('/home');
        } else {
          Navigator.of(context).pushReplacementNamed('/');
        }
      }
    });
  });
  // 原有UI代码不变
  return Scaffold(...);
}

方案3:改用StatefulWidget(可选)

如果后续需要更多生命周期逻辑,StatefulWidget可以直接通过mounted属性判断组件是否存活,代码更直观:

class SplashScreen extends StatefulWidget {
  const SplashScreen({Key? key}) : super(key: key);

  @override
  State<SplashScreen> createState() => _SplashScreenState();
}

class _SplashScreenState extends State<SplashScreen> {
  @override
  void initState() {
    super.initState();
    Future.delayed(const Duration(seconds: 1), () {
      final String? user = FirebaseAuth.instance.currentUser?.phoneNumber;
      final String? user1 = FirebaseAuth.instance.currentUser?.email;
      // 直接判断组件是否存活
      if (mounted) {
        if (user1 != null || user != null) {
          Navigator.of(context).pushReplacementNamed('/home');
        } else {
          Navigator.of(context).pushReplacementNamed('/');
        }
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    // 原有UI代码不变
    return Scaffold(...);
  }
}

内容的提问来源于stack exchange,提问作者saad tabban

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 14:55:23