为什么要用Navigator 2.0
- 1.0 API 中的
initialRoute参数,即系统默认的初始页面,在应用运行后就不能再更改了 。 - 1.0的命令式 Navigator API 只提供给了开发者一些非常针对性的接口,如
push()、pop()等,而没有给出一种更灵活的方式让我们直接操作路由栈。 - 1.0嵌套路由下,手机设备自带的回退按钮只能由根 Navigator 响应 。
路由管理插件(navigator_manager )的诞生
由于Navigator2.0的新增Api对于新手有一些学习成本,所以封装了一套开箱即用的路由管理插件。 需要解决的问题:
1.如何管理路由
RouterDelegate是2.0的路由代理,需要靠它来管理Router。首先我们先要理解下Navigator 2.0的路由原理。
如上图所示:当用户点击按钮跳转页面时,改变的是整个应用的路由栈状态,通知给Router,更新Navigator,从而更新子组件。而每一个子组件是就是一个Route,由我们配置的Page生成。这一点与Widget和Element的关系是相似的。
Navigator2.0的原理明白后,可以看出最核心的部分就是管理Router,需要设计RouterDelegate如下:
其中RouteManager模块是一个Provider,通过内部封装的跳转方法来操作uris(路由uri)和pages(页面栈)去通知Navigator进行更新,从而实现页面跳转。
2. 如何进行路由跳转和传参
由于Flutter对web的支持越来越完善,所以路由跳转传参也需要支持两种应用场景:
- web(路由可见、有刷新页面功能)
- app(路由不可见、无刷新页面功能)
针对两种场景下的特点: web:推荐使用uri方式进行传参。
-
- 路由参数可见(?a=1&b=2)
-
- 刷新页面不会丢失参数
app:推荐使用params方式进行传参。
-
- 支持自定义参数类型(传对象)
-
- 读取路由参数更灵活
跳转方法是统一的、语义化的: 跳转
go(Uri uri, {dynamic params})
替换
replace(Uri uri, {dynamic params})
返回
goBack()
回到首页并清空路由栈
goRoot()
清空路由栈并跳到指定页面
clearAndGo(Uri uri, {dynamic params})
添加多个页面栈(并跳到最后一个)
multipleGo(List<Uri> uris, {List<dynamic> params})
等待页面结果并跳转,配合returnResultGo使用
waitResultGo(Uri uri, {dynamic params})
页面返回结果,支持异步
returnResultGo(dynamic value)
更多方法可以去看文档 。
3. 如何进行路由监听
为什么要有路由监听?举个🌰 : ** 抖音主页跳转到个人主页时需要暂停视频播放,从个人主页返回时需要再次播放视频。 ** 这个功能怎么做?需要注意以下两点
- ios的右滑退出,会触发pop
- 路由跳转后上一个页面并不会执行release,所以无法在release里控制播放
恶心的实现: 在没有路由监听的功能时,我们会在点击跳转事件里进行控制,需要点击个人主页跳转时暂停视频播放,在点击个人主页返回时播放视频。如果又新增其他跳转逻辑,要一样添加播放暂停逻辑,很麻烦也很容易出错。而且个人主页点进去也是播放视频详情,又要进行播放控制。所以一旦产生bug,解决起来会让你十分头大。而且ios右滑退出还要自己做手势监听去处理。
路由有了监听方法: 目前有两种监听方式:全局模式和页面模式。
- 全局模式是全局监听路由栈的变化:
_routerDelegate.addListener(() { print(_routerDelegate.currentConfiguration)
})
- 页面模式是页面的push和pop的钩子函数:
/// 当上个页面退出且该页面显示时
@override
void didPopNext() { }
/// 该页面进入时
@override
void didPush() { }
/// 该页面退出时
@override
void didPop() { }
/// 当新页面进入且当前页面不在显示时
@override
void didPushNext() { }
有了路由监听,我们就可以在钩子函数里针对当前页面进行逻辑处理(播放、暂停),对路由的变化做了统一的监听,十分方便易维护。
navigator_manager使用教程
第一步:在应用顶层main.dart注册LRouterDelegate
import 'package:navigator_manager/navigator_manager.dart';
final RouterDelegate _routerDelegate = LRouterDelegate(
/// 配置所有路由信息
routes: {
'/': (_, __) => HomePage(),
'/login': (uri, _) => LoginPage(uri.queryParameters['id']),
'/personal': (_, params) => PersonalPage(params),
},
);
MaterialApp.router(
title: BasicConfig.APPName,
routeInformationParser: LRouteInformationParser(),
routerDelegate: _routerDelegate,
),
your page
class PersonalPageParams {
final UserInfo userInfo;
PersonalPageParams(this.userInfo);
}
class PersonalPage extends Page {
final PersonalPageParams params;
PersonalPage(this.params) : super(key: ValueKey('personal-page'));
@override
Route createRoute(BuildContext context) {
return MaterialPageRoute(
settings: this,
builder: (BuildContext context) {
return _PersonalPageWidget(params);
},
);
}
}
第二步:调用方法
RouteManager.of(context).go(Uri(path: '/test/todo', queryParameters: {'text': '12'}));
RouteManager.of(context).go(Uri(path: '/login'), params: UserInfo(name: 'your name', age: 18));
第三步:监听路由
监听全局路由信息
_routerDelegate.addListener(() {
if (_routerDelegate.currentConfiguration == Uri(path: '/')) {
/// to do
}
}
or
在页面监听钩子函数
class APage extends StatefulWidget {
const APage({Key key}) : super(key: key);
@override
_APageState createState() => _APageState();
}
// 3. Add `with RouteAware, RouteObserverMixin` to State and override RouteAware methods.
class _APageState extends State<APage> with RouteAware, RouteObserverMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text('A Page'),
),
),
);
}
@override
void didPopNext() { }
@override
void didPush() { }
@override
void didPop() { }
@override
void didPushNext() { }
}
至此,你的项目就有了路由管理功能,该插件使用简单,功能强大,欢迎使用!