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

Flutter问题:使用Go Router时热重载会返回根路由

问题解决:Flutter Provider + GoRouter 热重载返回根路由/启动页卡住

问题根源

  • 在build方法内创建AppRouter:每次热重载都会重建GoRouter实例,重置到initialLocation,直接导致跳回根路由。
  • 在build方法外创建AppRouter:GoRouter未监听AuthProvider的状态变化,即使AuthProvider的变量更新,重定向逻辑也不会触发,导致卡在启动页。

修复方案

步骤1:改造AppRouter,绑定AuthProvider并开启状态监听

class AppRouter {
  final AuthProvider authProvider;
  GoRouter get router => _goRouter;

  // 接收外部传入的AuthProvider
  AppRouter(this.authProvider);

  late final GoRouter _goRouter = GoRouter(
    // 监听AuthProvider的状态变化,自动触发重定向逻辑
    refreshListenable: authProvider,
    initialLocation: '/',
    routes: <GoRoute>[
      GoRoute(
        name: RouteNames.patientHomeScreen,
        path: '/',
        builder: (context, state) => const PatientHomeScreen(),
      ),
      GoRoute(
        name: RouteNames.splashScreen,
        path: '/splash',
        builder: (context, state) => const SplashScreen(),
      ),
      // 其他路由配置...
    ],
    redirect: (context, state) {
      // 直接使用传入的authProvider,无需重复从context获取
      final isAuth = authProvider.authenticated;
      final isVerified = authProvider.isVerified;
      final isOnboard = authProvider.isOnBoarding;
      final isInitialize = authProvider.initialized;

      final loginLocation = state.namedLocation(RouteNames.loginScreen);
      final otpLocation = state.namedLocation(RouteNames.otpScreen);
      final splashScreenLocation = state.namedLocation(RouteNames.splashScreen);
      final onBoardScreenLocation = state.namedLocation(RouteNames.onBoardScreen);

      final isGoingToLogin = state.matchedLocation == loginLocation;
      final isGoingToInit = state.matchedLocation == splashScreenLocation;
      final isGoingToOnboard = state.matchedLocation == onBoardScreenLocation;
      final isGoingToOtp = state.matchedLocation == otpLocation;

      if (!isInitialize && !isGoingToInit) {
        return splashScreenLocation;
      } else if (isInitialize && !isOnboard && !isGoingToOnboard) {
        return onBoardScreenLocation;
      } else if (isInitialize && isOnboard && !isAuth && !isGoingToLogin) {
        return loginLocation;
      } else if (isInitialize && isOnboard && isAuth && !isVerified && !isGoingToOtp) {
        return otpLocation;
      } else {
        return null;
      }
    },
  );
}

步骤2:修改MyApp,合理初始化AppRouter

将MyApp改为StatefulWidget,在依赖加载完成后初始化AppRouter,避免重复创建:

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late AppRouter appRouter;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 确保能获取到已初始化的AuthProvider后再创建AppRouter
    final authProvider = Provider.of<AuthProvider>(context, listen: false);
    appRouter = AppRouter(authProvider);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Awa',
      theme: TAppTheme.lightTheme,
      darkTheme: TAppTheme.darkTheme,
      // 使用routerConfig简化配置,替代分开设置三个路由相关参数
      routerConfig: appRouter.router,
    );
  }
}

核心优化点

  1. 状态监听:通过refreshListenable绑定AuthProvider,确保状态变化时自动触发重定向,解决启动页卡住问题。
  2. 避免重复实例化:在didChangeDependencies中初始化AppRouter,避免热重载时重建GoRouter导致路由重置。
  3. 简化配置:使用routerConfig替代拆分的路由参数,代码更简洁易维护。

内容的提问来源于stack exchange,提问作者saidu Bundu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 04:47:22