JHHK

欢迎来到我的个人网站
行者常至 为者常成

go_router

目录

介绍

一、为什么需要 GoRouter?

在 Flutter 中,页面导航最初通常通过 Navigator.push() 和 Navigator.pop() 实现。

Navigator.push(
    context,
    MaterialPageRoute(
        builder: (_) => const DetailPage(),
    ),
);

这种方式适合简单应用,但随着项目规模增大,路由配置分散、参数传递、登录拦截、深度链接以及多 Tab 导航等问题会逐渐凸显。

go_router 是 Flutter 生态中常用的声明式路由解决方案。它基于 Flutter 的 Router API,提供统一的路由配置、URL 匹配、页面导航和重定向能力。

使用 go_router,我们可以将页面导航关系集中定义成一棵路由树。

一个比较合理的架构:

                GoRouter
                   │
          ┌────────┴────────┐
          │                 │
        Shell              Login
          │
   ┌──────┼──────┐
   │      │      │
  Home  Message  Mine
   │
 Detail

二、主要方法

方法 主要用途 常见场景
go() 导航到目标路由状态,清空当前栈 Tab 切换、登录成功
push() 打开新的导航页面 列表进入详情
pop() 返回上一导航页面 关闭详情页
replace() 替换栈顶页面 表单提交成功后切换成功页面
页面无权限时切换权限页面
扫码页面扫码成功后进入新页面
canPop() 检查当前导航上下文能否返回 自定义返回逻辑

三、路由参数传递

GoRouter 常用的参数传递方式包括 Path 参数、Query 参数和 extra。

1、Path 参数

适用于标识页面核心资源的参数。

路由配置:
GoRoute(
    path: '/product/:id',
    builder: (context, state) {
    final id = state.pathParameters['id']!;
        return ProductDetailPage(id: id);
    },
)

跳转:    
context.push('/product/100');

页面接收到:   
id = 100

2、Query 参数

适用于筛选条件、来源标识等附加参数。

路由配置:
GoRoute(
    path: '/product',
    builder: (context, state) {
    final category = state.uri.queryParameters['category'];
    
        return ProductPage(category: category);
    },
)

跳转:
context.push('/product?category=book');

页面读取:
state.uri.queryParameters['category']
结果为 book。

3、extra 参数

当需要传递对象或不适合放进 URL 的临时数据时,可以使用 extra。

路由配置:
GoRoute(
    path: '/product/detail',
    builder: (context, state) {
    final product = state.extra as Product;
    
        return ProductDetailPage(product: product);
    },
)

context.push(
    '/product/detail',
    extra: product,
);

extra 的优点是传递对象方便,但它不是可直接编码到 URL 中的参数。
深度链接、进程重启或状态恢复场景下,不应假定这个对象始终存在。
对于必须支持直接访问的详情页,建议优先使用 Path 参数获取 ID,再通过 Repository 或状态管理层加载数据。

四、其它能力

initialLocation: '/',
redirect: (context, state) {
  return null;
},
errorBuilder: (context, state) => const ErrorPage(),

最佳实践

一、go()失效问题

MaterialApp.router(
    // routeInformationProvider 必须传入:
    // go_router 6.x 的 go() 依赖它通知 Router 触发导航,
    // 缺失时 go() 会静默失效(push/pushReplacement 走 delegate 不受影响)
    routeInformationProvider:router.routeInformationProvider,
    routeInformationParser:router.routeInformationParser,
    routerDelegate:router.routerDelegate,
)

二、错误页无法返回上一级页面问题


  /// 从错误页返回上一页面
  /// ------------------------------------------------------------
  /// 注意这里不能用 pop:错误页出现时 Navigator 只剩它一个页面,canPop 为 false,
  /// pop 不会有效果。恢复方式:go 回到最近一次导航前记录的位置;
  /// 未记录过(如启动 url 本身非法)时兜底回首页
  static void backFromError(BuildContext? context) {
    final String target = _lastLocation ?? '/home';
    if (context != null) {
      // 直接调 GoRouter.go 而不走本类 go():
      // 避免把错误页的坏 location 误记录进 _lastLocation
      GoRouter.of(context).go(target);
    } else {
      NavigationChannelHandler().go("xyapp://home");
    }
  }
  
  
// go_router 6.5.9:错误页会替换 Navigator 的全部 pages,
// 栈内只剩错误页、无法 pop,需用 backFromError 恢复到导航前的页面
RouterHandler.backFromError(context);

如果路由树是按父子路由配置好的,go(“xyapp://demo/set”)方法,会重建demo页面 -> set页面。


行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.