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

基于GoRouter实现随登录状态动态切换的嵌套路由底部导航

动态切换认证状态下的StatefulShellRoute分支方案

针对你的需求,这里提供一个兼顾深度链接支持和避免不必要请求的实现方案,核心是单GoRouter配置+动态生成StatefulShellBranch+路由守卫控制访问:

核心思路

用同一个GoRouter实例,通过监听认证状态动态生成对应状态的导航分支,同时用redirect逻辑确保未登录用户无法访问私有页面;把私有页面的初始化(比如网络请求)放在页面内部的initState中,而非路由配置阶段,这样未登录时这些页面不会被加载,自然不会触发多余请求。

具体实现

1. 抽离不同状态的导航分支配置

先把登录/未登录的分支单独定义,方便动态切换:

// 未登录时的公共导航分支
final List<StatefulShellBranch> _publicBranches = [
  StatefulShellBranch(
    routes: [
      GoRoute(
        path: '/home',
        builder: (context, state) => const PublicHomePage(),
      ),
    ],
  ),
  StatefulShellBranch(
    routes: [
      GoRoute(
        path: '/login',
        builder: (context, state) => const LoginPage(),
      ),
    ],
  ),
];

// 登录后的私有导航分支
final List<StatefulShellBranch> _privateBranches = [
  StatefulShellBranch(
    routes: [
      GoRoute(
        path: '/dashboard',
        builder: (context, state) => const DashboardPage(),
        // 嵌套子路由示例
        routes: [
          GoRoute(
            path: 'details/:id',
            builder: (context, state) => DetailsPage(id: state.pathParameters['id']!),
          ),
        ],
      ),
    ],
  ),
  StatefulShellBranch(
    routes: [
      GoRoute(
        path: '/profile',
        builder: (context, state) => const ProfilePage(),
      ),
    ],
  ),
];

2. 监听认证状态动态生成路由

用ChangeNotifier管理认证状态,让GoRouter通过refreshListenable监听状态变化,自动更新分支和底部导航:

// 管理认证状态的类
class AuthState extends ChangeNotifier {
  bool _isLoggedIn = false;
  bool get isLoggedIn => _isLoggedIn;

  void login() {
    _isLoggedIn = true;
    notifyListeners(); // 通知GoRouter刷新配置
  }

  void logout() {
    _isLoggedIn = false;
    notifyListeners();
  }
}

// 全局实例化认证状态
final authState = AuthState();

// 配置GoRouter
final GoRouter router = GoRouter(
  refreshListenable: authState,
  // 重定向逻辑:处理未登录访问私有页面、已登录访问登录页的情况
  redirect: (context, state) {
    final isLoggedIn = authState.isLoggedIn;
    final targetPath = state.subloc;

    // 未登录时访问私有路由,跳转到登录页
    if (!isLoggedIn && (targetPath.startsWith('/dashboard') || targetPath.startsWith('/profile'))) {
      return '/login';
    }
    // 已登录时访问登录页,跳转到仪表盘
    if (isLoggedIn && targetPath == '/login') {
      return '/dashboard';
    }
    return null; // 无需重定向
  },
  routes: [
    StatefulShellRoute.indexedStack(
      builder: (context, state, navigationShell) {
        // 动态渲染底部导航栏
        return Scaffold(
          body: navigationShell,
          bottomNavigationBar: BottomNavigationBar(
            currentIndex: navigationShell.currentIndex,
            onTap: (index) {
              navigationShell.goBranch(
                index,
                initialLocation: index == navigationShell.currentIndex,
              );
            },
            items: authState.isLoggedIn
                ? const [
                    BottomNavigationBarItem(icon: Icon(Icons.dashboard), label: '仪表盘'),
                    BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'),
                  ]
                : const [
                    BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
                    BottomNavigationBarItem(icon: Icon(Icons.login), label: '登录'),
                  ],
          ),
        );
      },
      // 根据认证状态动态选择分支
      branches: authState.isLoggedIn ? _privateBranches : _publicBranches,
    ),
    // 其他公共路由(比如重置密码)
    GoRoute(
      path: '/reset-password',
      builder: (context, state) => const ResetPasswordPage(),
    ),
  ],
);

3. 页面内部延迟初始化请求

把私有页面的网络请求等初始化逻辑放在initState中,只有当用户实际进入页面(且已通过路由守卫验证)时才执行:

class DashboardPage extends StatefulWidget {
  const DashboardPage({super.key});

  @override
  State<DashboardPage> createState() => _DashboardPageState();
}

class _DashboardPageState extends State<DashboardPage> {
  @override
  void initState() {
    super.initState();
    // 仅当页面加载时才发起请求
    _loadDashboardData();
  }

  Future<void> _loadDashboardData() async {
    // 这里写获取仪表盘数据的逻辑
  }

  @override
  Widget build(BuildContext context) {
    return const Center(child: Text('仪表盘'));
  }
}

方案优势

  • 完美支持深度链接:所有路由在同一GoRouter配置中,重定向逻辑可以直接处理深度链接的跳转
  • 避免不必要请求:私有页面的初始化逻辑在页面内部,未登录时这些页面不会被实例化,自然不会触发网络请求
  • 动态切换无感知:认证状态变化时,GoRouter自动刷新分支和底部导航,无需手动切换整个路由配置
  • 状态安全:通过redirect逻辑确保未登录用户无法访问私有页面,即使直接输入URL也会被重定向

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 07:17:07