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

如何结合NavigationRail实现Flutter GoRouter的嵌套路由与页面切换

如何结合NavigationRail实现Flutter GoRouter的嵌套路由与页面切换

看了你贴的代码,其实已经把核心框架搭得差不多了,只需要几个关键调整,就能实现点击NavigationRail时路由同步变化(比如/landing/home)同时页面切换的效果,咱们一步步来改:

一、给ShellRoute绑定基础路径

你现在的ShellRoute没有指定path,导致子路由的路径是/home而不是/landing/home。先给ShellRoute加上/landing前缀,让所有子路由自动继承这个路径:

修改GoRouter里的ShellRoute部分:

ShellRoute(
  navigatorKey: _landingNavigatorKey,
  path: Routes.landing, // 新增这一行,指定父路由路径为/landing
  builder: (context, state, child) => const LandingScreen(child: child),
  routes: [
    // 子路由保持不变,现在它们的实际路径会变成/landing/home、/landing/live等
    GoRoute(
      path: Routes.home,
      name: Routes.home,
      builder: (_, __) => const HomeScreen(),
    ),
    // 其他子路由(live、tvGuide等)同理...
  ],
),

同时要确保你的Routes类里定义了这个路径:

class Routes {
  // ...其他已有定义
  static const landing = '/landing';
}

二、建立NavigationRail索引与路由的映射

在LandingScreen的状态类里,新增一个列表,把每个RailItem对应的路由名称按顺序存起来,和你的NavigationRailDestination顺序完全对应:

class _LandingScreenState extends ConsumerState<LandingScreen> {
  // 新增:索引对应的路由名称列表,顺序要和Rail的destinations完全一致
  final List<String> _routeNames = [
    Routes.home,
    Routes.live,
    Routes.tvGuide,
    Routes.weekly,
    Routes.recordings,
    Routes.settings,
  ];

  // 其他原有代码(_iconWidth、_railItemPadding等)...
}

三、修改Rail点击逻辑:路由跳转+状态同步

原来的onDestinationSelected只更新了状态索引,没触发路由跳转,所以路径不会变。现在改成点击时同时跳转路由并更新状态:

onDestinationSelected: (selectedIndex) {
  // 1. 更新状态管理的选中索引
  ref.read(landingIndexProvider.notifier).updateIndex(selectedIndex);
  // 2. 跳转到对应的路由
  context.goNamed(_routeNames[selectedIndex]);
},

四、同步路由变化到Rail选中状态

为了避免用户直接输入URL(或其他方式跳转)时,Rail的选中索引和路由不一致,需要在build方法里监听当前路由,自动同步索引:

@override
Widget build(BuildContext context) {
  // 新增:监听当前路由,同步更新Rail选中索引
  final currentRouteName = GoRouterState.of(context).name;
  final currentIndex = _routeNames.indexOf(currentRouteName);
  if (currentIndex != -1 && currentIndex != ref.read(landingIndexProvider)) {
    ref.read(landingIndexProvider.notifier).updateIndex(currentIndex);
  }

  // 原有代码:获取当前索引、profile名称等
  final index = ref.watch(landingIndexProvider);
  final currentProfileName = ref.watch(currentProfileStateProvider);
  ref.watch(asyncProfilesProvider(currentProfileName));
  
  // ...后续Scaffold构建代码不变
}

五、修正ProfileSelecting的跳转逻辑

原来的redirect直接返回Routes.landing,但/landing是ShellRoute,需要跳转到它的默认子路由(比如/landing/home)。修改ProfileSelecting的GoRoute:

GoRoute(
  path: Routes.profileSelecting,
  name: Routes.profileSelecting,
  redirect: (_, state) async {
    final stbAccount = await ref.read(asyncAccountProvider.future);
    final isMultipleProfiles = stbAccount.data.profiles!.length > 1;
    if (isMultipleProfiles) {
      // 有多个profile时,停留在当前选择页面
      return null;
    }
    // 无多个profile时,直接跳转到landing的home页面
    return '/${Routes.landing}/${Routes.home}';
  },
  builder: (_, __) => const ProfileSelectionScreen(),
),

最后检查Routes类的子路由定义

确保子路由的path是相对路径(不带/),因为ShellRoute已经有了/landing前缀:

class Routes {
  // ...其他定义
  static const home = 'home';
  static const live = 'live';
  static const tvGuide = 'tv-guide';
  static const weekly = 'weekly';
  static const recordings = 'recordings';
  static const settings = 'settings';
}

这样调整后,点击NavigationRail的各个选项时,路由会自动变成/landing/home、/landing/live等,页面也会同步切换对应的子页面,同时Rail的选中状态和路由始终保持一致。

备注:内容来源于stack exchange,提问作者TeamlyDigital

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 14:58:01