在Flutter开发的页面间实现跳转的话,一切都离不开Navigator,系统提供了Navigator管理界面跳转、传参、返回等操作,但是使用起来不太方便,比如router的注册需添加到routers中,如下左图,界面跳转的话需要引入目的页的类,获取当前context,在进行跳转,获取也是一样,都比较复杂。总结起来就是,耦合严重、不便使用,写小demo还感觉不到差别,当应用于完整项目的时候,耦合的问题就需要考虑了。
获取.png 注册routers.png- 解耦
解耦是在根据fluro
这个库进行的,将flutter 路由表统一进行管理
路由表.png
基于这个库也可以做到了跳转一行代码
DRouter.navigateTo(DartRouter.inviteFriend, isLogin: true);
不过还不够,这个库本身也有一些限制,于是在此基础上进行了修改,仓库地址
只支持scheme传参,这显然有很大的局限性,系统的Navigator是支持传参Object的,对比系统源码和库源码,扩展了 RouteSettings,基于此进行的一些改造可以支持Object传参,同时也保留了库本身基于scheme方式
RouteSettings userSettings = RouteSettings(name: umpPath, arguments: arguments);
- 便捷使用
此外进行了方法的扩展,满足常见的跳转,此外还增加popUntil
和popSkip
的支持
void popUntil(BuildContext context, String path) {
serviceLocator.getIt<NavigateService>().popUntilG(ModalRoute.withName(path));
}
void popSkip(BuildContext context, String skip) {
serviceLocator.getIt<NavigateService>().popUntilG(
(Route<dynamic> route) {
bool routePredicate = !route.willHandlePopInternally
&& route is ModalRoute
&& Uri.parse(route.settings.name).host != skip;
if (!routePredicate) { // 双重否定为肯定
// 包含则 skip 出栈
print('popSkip $skip -- ${route.settings.name}');
}
return routePredicate;
}
);
}
其中popSkip
是基于 RoutePredicate
对fluro以及系统Navigator的创新,可以跳过某个模块,而不用关心来源是什么页面。
- 无context导航
@optionalTypeArgs
static Future<T> pushNamed<T extends Object>(
BuildContext context,
String routeName, {
Object arguments,
}) {
return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments);
}
Navigator的跳转的时候需要有上下文context,这是个重要的参数,在混合栈中提到会有native页面和flutter页面之间的相互跳转,那么就有如下问题,native跳转flutter的时候,native页面是没有context信息的,全局变量保存当前flutter页面的context信息也是不可取的。那么怎么实现无context导航呐?
这里使用到了GlobalKey
,GlobalKey可以保存widget,我们可以在程序的入口通过GlobalKey来保存flutter tree的根节点,这样就可以通过根节点进行任何页面内的跳转。
// 获取根节点
child: MaterialApp(
navigatorKey: Application.serviceLocator.getIt<NavigateService>().key,
home: ContainerPage(),
// 获取NavigatorState
class NavigateService {
final GlobalKey<NavigatorState> key = GlobalKey(debugLabel: 'navigate_key');
NavigatorState get navigator => key.currentState;
get pushNamedG => navigator.pushNamed;
get pushG => navigator.push;
get pushReplacementG => navigator.pushReplacement;
get popG => navigator.pop;
get popUntilG => navigator.popUntil;
}
- NavigatorObserver
flutter的所有ui都是由widget组成,生命周期也是基于widget的state,但是对于页面来说state生命周期还是比较弱,难以做到像native页面一样监控各种页面展示退出。这点可以通过NavigatorObserver进行解决,可以在程序初始化时设置navigatorObservers
child: MaterialApp(
navigatorKey: Application.serviceLocator.getIt<NavigateService>().key,
home: ContainerPage(),
debugShowCheckedModeBanner: false,
onGenerateRoute: Application.router.generator,
navigatorObservers: [AppRouterNavigatorObserver()],
NavigatorObserver
里面有丰富的api,didPush、didPop、didRemove、didStartUserGesture、didStopUserGesture等,举一个例子:
void didPop(Route<dynamic> route, Route<dynamic> previousRoute) { }
两个参数Route
,这样就可以根据router知道前一个页面后一个面是什么,可以在这里进行埋点之类的全局监控。
-
两个路由表
上面主要讲述了flutter router的管理,还不算真正的路由管理,路由管理这里不再阐述,和Native的路由功能一样。那两个路由表是是什么意思呐?flutter 页面维护一个路由表,主要是flutter页面是由Navigator进行管理跳转,此外如果涉及到混合栈,路由问题就复杂了一些,native的路由管理是由Native的导航栏进行的,所以针对native页面又建立了一个路由表,两个路由表结合在一起,构成了大路由的基础。当需要页面跳转的时候,先去两个路由中查找,目的页面是来自native页面还是flutter页面,再有响应的navigate进行处理,当然混合栈的问题还牵涉到通信,流程图如下:
混合栈通信.png
网友评论