目录
介绍
一、为什么需要 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页面。
行者常至,为者常成!