快速开始
最简单的使用是用 RoutesLocationBuilder
实现,这种方式产出的代码最少。对于导航场景较少的应用或者页面栈浅的应用(即页面很少堆叠在一起)来说,是很棒的选择。
class MyApp extends StatelessWidget {
final routerDelegate = BeamerDelegate(
locationBuilder: RoutesLocationBuilder(
routes: {
// Return either Widgets or BeamPages if more customization is needed
// 返回 Widgets 或 BeamPages(如果需要更多定制的话)
'/': (context, state, data) => HomeScreen(),
'/books': (context, state, data) => BooksScreen(),
'/books/:bookId': (context, state, data) {
// Take the path parameter of interest from BeamState
// 从 BeamState 获取路径参数
final bookId = state.pathParameters['bookId']!;
// Collect arbitrary data that persists throughout navigation
// 收集在整个导航过程中持续存在的任意数据
final info = (data as MyObject).info;
// Use BeamPage to define custom behavior
// 使用 BeamPage 来自定义行为
return BeamPage(
key: ValueKey('book-$bookId'),
title: 'A Book #$bookId',
popToNamed: '/',
type: BeamPageType.scaleTransition,
child: BookDetailsScreen(bookId, info),
);
}
},
),
);
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routeInformationParser: BeamerParser(),
routerDelegate: routerDelegate,
);
}
}
RoutesLocationBuilder
会根据路径对 routes
进行选择和排序。 例如,导航到 /books/1
会匹配 routes
里的全部3个实体,然后把它们堆叠在一起。导航到 /books
会匹配routes
的前两个实体。
对应的页面被放入到 Navigator.pages
中,BeamerDelegate
(重新)构建 Navigator
,在屏幕上显示选中的页面栈。
为什么我们有一个 locationBuilder
? BeamLocation
是什么?它的输出是什么?
BeamLocation
是一个实体,它基于它的 state
来决定哪个页面要进入到 Navigator.pages
里。locationBuilder
选择适当的 BeamLocation
来进一步处理收到的 RouteInformation
。 这大多数是通过验证 BeamLocation.pathPatterns
来实现。
RoutesLocationBuilder
返回 BeamLocation
的一个特殊类型 - RoutesBeamLocation
,它有用于绝大多数常用的导航场景的实现。 如果 RoutesLocationBuilder
没有提供所需的行为或者足够的定制,可以扩展 BeamLocation
为进入到 Navigator.pages
的任意数量的页面栈来定义和组织行为。
深入阅读: BeamLocation [中文],BeamState[中文]。
导航
导航是用 “beam” 来完成的。可以认为是在应用中传送(beam)到其它地方。 类似于 Navigator.of(context).pushReplacementNamed('/my-route')
,但是 Beamer 并不限于单个页面,或者是推入栈本身。 BeamLocation
创建页面的栈,当 beam 到某个页面时,页面会被构建。 Beaming 感觉像是同时使用了多个 Navigator
的 push/pop
(入栈/出栈)方法。
// Basic beaming
Beamer.of(context).beamToNamed('/books/2');
// Beaming with an extension method on BuildContext
// 使用 BuildContext 的扩展方法 Beaming
context.beamToNamed('/books/2');
// Beaming with additional data that persist
// throughout navigation withing the same BeamLocation
// 用同一个 BeamLocation 的导航过程中的数据 Beaming。
context.beamToNamed('/book/2', data: MyObject());
导航返回
这里有两种返回的类型,即 reverse navigation(反转导航); 向上 和 反转时序.
向上 (从栈中弹出页面)
向上导航是指导航到当前页面栈的前一个页面。就是大家熟知的弹出,通过 Navigator
的 pop
/maybePop
方法来完成。如果不指定其它处理,默认的 AppBar
的 BackButton
返回按钮会调用这个方法。
Navigator.of(context).maybePop();
反转时序 ( beam 到前一个状态)
反转时序导航会导航到前面访问过的任意地方。在深度链接的情况(例如:从 /authors/3
导航到 /books/2
,而不是从 /books
导航到 /books/2
)下,这和弹出是不一样的。 Beamer 在 beamingHistory
历史中保持着导航历史,所以它能够导航到 beamingHistory
中的前一个时间点的入口。这称作 “beam back” (回光返照?皮一下)。
Beamer.of(context).beamBack();
Android 返回按键
集成 beam 的 Android 返回按键通过在 MaterialApp.router
中设置 backButtonDispatcher
来实现。这个分发器需要指向同一个为routerDelegate
设置的 BeamerDelegate
的引用。
MaterialApp.router(
...
routerDelegate: beamerDelegate,
backButtonDispatcher: BeamerBackButtonDispatcher(delegate: beamerDelegate),
)
BeamerBackButtonDispatcher
会首先尝试 pop
(弹出),如果弹出不可用,会改为 beamBack
。如果 beamBack
返回 false
(没有地方可返回),Android 的返回按钮会关闭应用,也可能是返回前一个使用的应用(通过 deep-link (深度链接)打开当前应用)。 BeamerBackButtonDispatcher
可以配置为 alwaysBeamBack
(意思是不会尝试 pop
(弹出))或 fallbackToBeamBack
(意思是不会尝试 beamBack
)。
访问最近的 Beamer
要在组件中访问路由的属性(例如,用于构建 BookDetailsScreen
的 bookId
)可以使用:
@override
Widget build(BuildContext context) {
final beamState = Beamer.of(context).currentBeamLocation.state as BeamState;
final bookId = beamState.pathParameters['bookId'];
...
}
使用 “Navigator 1.0”
注意 “Navigator 1.0”(命令式的 push/pop 和类似的函数)可以和 Beamer 一起使用。我们已经看到 Navigator.pop
用来向上导航。这告诉我们是在使用同样的 Navigator
,只是使用了不同的 API 。
用 Navigator.of(context).push
(或任何类似的动作) 入栈不会反映到 BeamLocation
的状态,这意味着浏览器的 URL 不会改变。可以通过 Beamer.of(context).updateRouteInformation(...)
来只更新 URL 。当然在移动端使用 Beamer 时不会有这个问题,因为看不到 URL 。
通常,每个导航场景应该是可实现的声明式(定义页面栈),而不是命令式(入栈),但是做到这一点的难度会有所不同。
对于中级和高级的用法,现在介绍一些核心概念: BeamLocation
和 BeamState
。
核心概念
从最顶层来看,Beamer
是 Router
的包装,它使用 了自身的对 RouterDelegate
和 RouteInformationParser
的实现。Beamer 的目标是分离【用不同的状态为 Navigator.pages
的多个类来构建页面栈】的职责,代替所有页面栈使用一个全局状态。
例如,我们想要处理所有个人资料相关的页面栈如:
[ ProfilePage ]
(个人资料页面),[ ProfilePage, FriendsPage]
(个人资料页面,好友页面),[ ProfilePage, FriendsPage, FriendDetailsPage ]
(个人资料页面,好友页面,好友详细页面),[ ProfilePage, SettingsPage ]
(个人资料页面,设定页面),- …
用一些 “ProfileHandler” 来知道哪个状态对应哪个页面栈。类似地,我们想要一个 “ShopHandler” 来处理所有商店关联的页面栈。这些页面如:
[ ShopPage ]
(商店页面),[ ShopPage, CategoriesPage ]
(商店页面,品类页面),[ ShopPage, CategoriesPage, ItemsPage ]
(商店页面,品类页面,商品页面),[ ShopPage, CategoriesPage, ItemsPage, ItemDetailsPage ]
(商店页面,品类页面,商品页面,商品详细页面),[ ShopPage, ItemsPage, ItemDetailsPage ]
(商店页面,商品