Flutter Navigator 2.0 使用宣告式(declarative style)的方式定义路由导览的方式,需要事先具备基本概念:
Navigator
透过RouterDelegate
的pages
设定显示对应画面RouteInformation
路由资讯解析成自定义的路由设定类别(T)RouteInformation
的来源RouteInformationParser
提供的路由设定管理pages
的内容Router
点击返回按钮
的事件引用:Flutter Navigator 2.0 and Deep Links (Kevin D Moore, Feb 23 2021)
www.raywenderlich.com
我们需要实作 RouterDelegate
与 RouteInformationParser
这两个抽象类别。
class MyApp extends StatefulWidget {
@override
_MyApp createState() => _MyApp();
}
class _MyApp extends State<MyApp> {
AppRouterDelegate _delegate = AppRouterDelegate();
AppRouterParser _parser = AppRouterParser();
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: "Navigator v2",
routerDelegate: _delegate,
routeInformationParser: _parser,
);
}
}
App在启动的时候,会使用 RouteInformationParser
解析要处理的路径,在parseRouteInformation
方法里我们会收到 RouteInfomationProvider
提供的 RouteInformation
。
@override
Future<RoutePage> parseRouteInformation(RouteInformation routeInformation) {
print(
'RouteInformationParser parseRouteInformation ${routeInformation.location}');
RoutePage configuration =
getRoutePage(name: routeInformation.location ?? '/');
return SynchronousFuture(configuration);
}
@override
RouteInformation restoreRouteInformation(RoutePage configuration) {
print(
'RouteInformationParser restoreRouteInformation ${configuration.path}');
return RouteInformation(location: configuration.path ?? '/');
}
其中泛型<T>
可以自行定义想要的类别(RoutePage
),这边简单的使用一个函数处理对应的路由设定并绑定 widget 内容。
class RoutePage extends RouteSettings {
final Widget? widget;
const RoutePage({
required String name,
Object? arguments,
this.widget,
}) : super(name: name, arguments: arguments);
}
RoutePage getRoutePage({required String name}) {
print("getRoutePage $name");
switch (name) {
case '/':
return RoutePage(name: name, widget: HomePage());
case '/items':
return RoutePage(name: name, widget: ItemsPage());
default:
return RoutePage(name: '404', widget: UnknownPage());
}
}
接着宣告 RouterDelegate
必要的属性 pages
class AppRouterDelegate extends RouterDelegate<RoutePage>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<RoutePage> {
final List<Page> _pages = [];
@override
final GlobalKey<NavigatorState> navigatorKey;
...
}
定义setNewRoutePath
行为:RouteInformationParser
解析完路由会接着呼叫setNewRoutePath
方法并带入自定义的路由设定泛型(RoutePage)。
我们可以在这边定义收到新的路由设定时要如何操作pages
的内容。
@override
Future<void> setNewRoutePath(RoutePage configuration) {
print("RouterDelegate setNewRoutePath ${configuration.name}");
final shouldAddPage = _pages.isEmpty ||
(_pages.last.arguments as RoutePage).name != configuration.name;
if (shouldAddPage) {
replace(configuration);
}
return SynchronousFuture(null);
}
RouterDelegate
会透过 build 方法重构 Navigator
。Navigator
提供了底层路由的架构,Navigator 在透过 Overlay
将 pages
的内容渲染在画面上。
@override
Widget build(BuildContext context) {
print("RouterDelegate build");
return Navigator(
key: navigatorKey,
onPopPage: _onPopPage,
pages: List.of(_pages),
);
}
还需要覆写 RouterDelegate
的 currentConfiguration
,在呼叫完 build 後,程序会呼叫RouteInformationParser
的 restoreRouteInformation
,用来将路由设定写回记录(可参考 browser history 的行为)。
@override
RoutePage get currentConfiguration {
print("RouterDelegate currentConfiguration");
return _pages.last.arguments as RoutePage;
}
RouterDelegate
管理 pages
来处理路由的状态。 pages
只能是 Page
抽像类别的子类别,可以实作相关设定客制化想要的行为,例如过场特效。
对於 pages
异动後,需要呼叫 notifyListeners
通知 Router
重新整理。
replace(RoutePage configuration) {
_pages.clear();
_pages.add(
MaterialPage(
child: configuration.widget,
key: ValueKey(configuration.name),
name: configuration.name,
arguments: configuration,
),
);
notifyListeners();
}
可以比对主控台的讯息,理解呼叫的先後顺序。
Restarted application in 166ms.
RouteInformationParser parseRouteInformation /
[RoutePage] getRoutePage /
RouterDelegate setNewRoutePath Home
RouterDelegate build
RouterDelegate currentConfiguration
RouteInformationParser restoreRouteInformation /
为了让 Navigator
下的子元素可以存取路由的设定,我们可以透过 Router.of
的方式找到父层中的的 delegate
。
static AppRouterDelegate of(BuildContext context) {
RouterDelegate routerDelegate = Router.of(context).routerDelegate;
return routerDelegate as AppRouterDelegate;
}
使用方式如下:
AppRouterDelegate.of(context).pushNamed('/items');
范例中使用两个画面测试自己定义的 replace
和 push
方法,这些方法都只是对 pages
进行操作而已。
<<: Day26 - 轻前端 Component - jQuery UI Selectmenu
>>: Day11-Database——效能的储备足够吗?-N+1 query
Lambda在刚开始学Java一定会很不想碰,会觉得好不容易对Java有点熟悉了,结果又搞出一整陀新...
The future is already here – it's just not evenly...
铁人赛写到现在,其实主题中的内容还有很多可以实作的部分,但一方面是不确定时间到期後,铁人赛系列还能不...
今天要介绍透过CDN连结的方式快速运用react,简单建立一个react component。 CD...
GCE 昨天有说到了执行个体建立,那如果有需求将相同服务性质绑在同一个群组GCP上是否可以做得到,没...