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

如何在GoRouter中实现多场景下的路由模块复用?

GoRouter 复用路由与循环导航问题的解决方案

针对模块多位置复用的问题

你可以把每个模块的路由结构封装成可复用的路由生成函数,通过传入不同前缀生成唯一路由名称,同时完全复用页面组件和子路由逻辑:

  • 先定义模块的基础路由配置函数,比如Schedule模块:
List<GoRoute> buildScheduleRoutes(String prefix) {
  return [
    GoRoute(
      name: '$prefix-schedule',
      path: '$prefix/schedule',
      builder: (context, state) => const SchedulePage(),
      routes: [
        // 复用子路由
        GoRoute(
          name: '$prefix-schedule-detail',
          path: 'detail/:id',
          builder: (context, state) => ScheduleDetailPage(id: state.pathParameters['id']!),
        ),
      ],
    ),
  ];
}
  • 然后在BottomNavigationBar和More菜单的路由中分别调用该函数,传入不同前缀:
final router = GoRouter(
  routes: [
    // BottomNavigationBar 路由组
    ShellRoute(
      builder: (context, state, child) => BottomNavPage(child: child),
      routes: [
        ...buildScheduleRoutes('bottom'),
        ...buildSalesOrderRoutes('bottom'),
        // 其他底部模块
      ],
    ),
    // More菜单路由组
    GoRoute(
      path: '/more',
      builder: (context, state) => const MorePage(),
      routes: [
        ...buildScheduleRoutes('more'),
        ...buildSalesOrderRoutes('more'),
        // 其他More模块
      ],
    ),
  ],
);

这样既保证了路由名称唯一,又完全复用了模块的页面和子路由结构。导航时直接使用生成的唯一路由名即可,比如context.pushNamed('bottom-schedule-detail', params: {'id': '123'})或context.pushNamed('more-schedule-detail', params: {'id': '123'})。

如果不想区分前缀,也可以直接用相对路由导航,但需要确保当前上下文的路由层级正确,不过前缀生成唯一路由名的方式更清晰,也更贴合GoRouter的声明式设计。

针对循环依赖路由的问题

不需要重复定义两个完全对称的路由,你可以通过以下两种方式解决:

1. 动态路由+通用页面组件

定义一个通用嵌套路由,通过路径参数区分当前模块类型和关联模块类型:

GoRoute(
  path: '/:moduleType/:moduleId/:relatedType/:relatedId',
  builder: (context, state) {
    final moduleType = state.pathParameters['moduleType']!;
    final moduleId = state.pathParameters['moduleId']!;
    final relatedType = state.pathParameters['relatedType']!;
    final relatedId = state.pathParameters['relatedId']!;

    // 根据类型渲染对应页面,复用组件逻辑
    if (moduleType == 'case') {
      return CasePage(id: moduleId, relatedSalesOrderId: relatedId);
    } else if (moduleType == 'sales-order') {
      return SalesOrderPage(id: moduleId, relatedCaseId: relatedId);
    }
    // 兜底页面
    return const NotFoundPage();
  },
)

然后在页面中加入循环检测逻辑:比如在CasePage跳转到SalesOrder时,检查当前栈的上一个路由是否是对应的SalesOrder->Case,若是则阻止跳转,或替换当前路由而非push。

2. 用redirect阻止循环跳转

如果一定要保留两个对称路由结构,可以复用页面组件,同时通过GoRouter的redirect函数阻止无限循环:

final router = GoRouter(
  redirect: (context, state) {
    final currentPath = state.fullPath;
    final previousPath = GoRouterState.of(context).previousFullPath;

    // 检测循环跳转:case/:id/sales-order/:sid <-> sales-order/:sid/case/:id
    if (currentPath != null && previousPath != null) {
      final currentSegments = currentPath.split('/');
      final previousSegments = previousPath.split('/');
      if (currentSegments.length == 5 && previousSegments.length == 5) {
        final isLoop = 
          currentSegments[1] == previousSegments[3] && 
          currentSegments[2] == previousSegments[4] && 
          currentSegments[3] == previousSegments[1] && 
          currentSegments[4] == previousSegments[2];
        if (isLoop) {
          // 阻止跳转,返回当前路径
          return previousPath;
        }
      }
    }
    return null;
  },
  routes: [
    GoRoute(
      path: '/case/:id/sales-order/:sid',
      builder: (context, state) => SalesOrderPage(
        id: state.pathParameters['sid']!,
        relatedCaseId: state.pathParameters['id']!,
      ),
    ),
    GoRoute(
      path: '/sales-order/:sid/case/:id',
      builder: (context, state) => CasePage(
        id: state.pathParameters['id']!,
        relatedSalesOrderId: state.pathParameters['sid']!,
      ),
    ),
  ],
);

这里两个路由复用了页面组件,仅路径参数顺序不同,同时通过redirect检测循环跳转,避免无限push。另外也可以写一个生成对称路由的函数,传入模块类型对自动生成路由,减少重复代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 23:42:40