go_router
欢迎来到 go_router !
MaterialApp.router
指定的 Flutter Router API 需要 RouterDelegate
类和 RouteInformationParser
类的实现。
这两个实现本身意味着第三种类型的定义,该类型保有驱动创建 Navigator
的应用状态。 可以阅读 Medium 上一篇关于这些需求的优秀博客(需要借助一些工具访问)。这种责任分离允许 Flutter 开发者来实现多种路由和导航策略,包括深度链接和动态链接,但是代价是复杂度。
go_router 包的目的是使用声明式路由来降低复杂度,无论面向的是哪个目标平台(移动端、Web、桌面端)。在 Android、iOS 和 Web 上处理深度链接和动态链接,以及多个其它与导航关联的场景,同时仍然(有望)提供一个简单易用的开发者体验。
开始
go_router | Flutter Package (flutter-io.cn)
官网提供的视频在油管上,有兴趣的同学可前往观看。
声明式路由
go_router 由 GoRouter
构造器中指定的路由集合部分来管理。
class App extends StatelessWidget {
...
final _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const Page1Screen(),
),
GoRoute(
path: '/page2',
builder: (context, state) => const Page2Screen(),
),
],
);
}
这种情况下,定义了两个路由,每个路由 path
都会匹配用户导航的位置。只有单个路由会被匹配,具体来说,就是某个路由的 path
会匹配整个位置(所以路由的列出顺序无关紧要)。 path
匹配时忽略大小写,即使参数会保留大小写。
除了 path
之外,每个路由会代表性地带有一个 builder
函数,该函数负责构建用来占据应用整个屏幕的组件。页面间会使用默认转换 ,取决于添加到组件树顶部的应用类型。例如,使用 MaterialApp
会让 go_router 使用 MaterialPage
的转换。
路由状态
如果在上面代码片段中不使用时,builder
函数会接收一个 state
对象,该对象是 GoRouterState
的一个实例,包含一些有用的信息:
GoRouterState
:
属性 | 描述 | 示例 1 | 示例 2 |
---|---|---|---|
location |
完整路由的位置,包含查询参数 | /login?from=/family/f2 |
/family/f2/person/p1 |
subloc |
子路由的位置,不包含查询参数 | /login |
/family/f2 |
name |
路由名称 | login |
family |
path |
路由路径 | /login |
family/:fid |
fullpath |
该子路由的完整路径 | /login |
/family/:fid |
params |
从位置中提取的参数 | {} |
{'fid': 'f2'} |
queryParams |
位置末尾的可选参数 | {'from': '/family/f1'} |
{} |
extra |
可选的对象参数 | null |
null |
error |
如果有 Exception('404') ,该属性为子路由关联的 Exception (异常) |
… | |
pageKey |
该子路由的唯一键 | ValueKey('/login') |
ValueKey('/family/:fid') |
state
对象用于为参数化路由传递参数和重定向。不是每次都要设置所有的 state 参数。通常, GoRouterState
定义一个 GoRouterState
实例可能的当前 state 的超集。例如,仅当有错误时会设置 error
参数。
初始化
用路由列表可以创建 GoRouter
的实例,它自身提供了调用 MaterialApp.router
构造器(或CupertinoApp.router
构造器)所需的对象:
class App extends StatelessWidget {
App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => MaterialApp.router(
routeInformationParser: _router.routeInformationParser,
routerDelegate: _router.routerDelegate,
);
final _router = GoRouter(routes: ...);
}
使用合适位置的路由,你的应用现在可以在页面间导航navigate between pages了。
错误处理
默认情况下,go_router 会为 MaterialApp
和 MaterialApp
转到默认的错误界面,即使没有为两者设置默认的错误界面。也可以通过设置 GoRouter
的 errorBuilder
参数来替换掉默认的错误界面。
class App extends StatelessWidget {
...
final _router = GoRouter(
...
errorBuilder: (context, state) => ErrorScreen(state.error),
);
}
错误界面,无论你或 go_router 有没有提供,都会在以下情况时使用:位置没有匹配路由(404),一个位置匹配了多个的路由,或者某个 builder 函数抛出了异常。
深度链接
Flutter 把 “深度链接” 定义为 打开一个 URL 来展示应用中的对应页面。任意作为 GoRoute
列出的内容都可以在 Android、iOS、Web 平台通过深度链接(打开)。对于支持 Web 开箱即用,当然是通过地址栏。但是在 Android 和 iOS 上需要另外的配置,在 Flutter docs 文档中有相关描述。
导航
在页面间导航,使用 GoRouter.go
方法:
// 使用 GoRouter 导航
onTap: () => GoRouter.of(context).go('/page2')
go_router 还提供了一个使用 Dart 扩展方法的简化的导航:
// 使用 GoRouter 导航更容易
onTap: () => context.go('/page2')
简化的版本直接映射到完整版本,所以也可以使用。 如果你对此好奇,只是调用 context.go(...)
就能发生魔法,这就是 go_router 的来历。
如果想用 Link
组件来导航,那也是有效的:
Link(
uri: Uri.parse('/page2'),
builder: (context, followLink) => TextButton(
onPressed: followLink,
child: const Text('Go to page 2'),
),
),
如果为 Link
组件提供一个带 scheme (页面内跳转协议) 的 URL,例如:https://flutter.dev
,它会在浏览器中载入这个 URL。否则,它会在应用内使用内置的导航系统打开链接。
页面入栈
除了 go
方法之外, go_router 还提供了 push
方法。 go
和 push
都可用来构建页面栈,但是方式不同。 go
方法通过使用 子路由 把单个位置转换为栈中的多个页面。
push
方法把单个页面推入到现有页面的栈中,这意味着可以编程式来构建栈,而不是声明式。当 push
方法通过子路由匹配整个栈时,它会从栈中选择最顶层的页面,并推入到栈中。
弹出页面
如果想要从栈中弹出一个页面,可以使用 pop
方法。这个方法只是简单地调用 Navigator.pop
。关于使用 Navigator
和 GoRouter
集成的注意事项的更多信息,参考导航集成。
初始化位置
如果想为路由设置一个初始化位置,可以设置 GoRouter
构造器的 initialLocation
参数:
final _router = GoRouter(
routes: ...,
initialLocation: '/page2',
);
如果应用是通过深度链接 [中文]启动的,提供给 initialLocation
的值会被忽略。
当前位置
如果想知道当前位置,使用 GoRouter.location
属性。
如果想知道现在位置改变的时间点,由手动导航、或深度链接、或用户点击返回按钮的弹出,GoRouter
本身也是 ChangeNotifier
,这意味着可以调用 addListener
用来在现在位置改变时接收通知,手动或通过用 Flutter 为 ChangeNotifier
对象构建组件 AnimatedBuilder
,命名不是很直观。
class RouterLocationView extends StatelessWidget {
const RouterLocationView({Key? key}) : super(key: key);
@