Flutter上线项目实战记录之路由篇
1.应用场景
开发中经常遇到
- 路由跳转时拿不到context怎么办,eg:token失效/异地登录跳转登录页面。
- 获取不到当前路由名称怎么办,eg:点击push推送跳转指定路由,如果已经在当前页面就replace,如果不在就push。
- 注册监听路由跳转,做一些想做的事情,eg:不同路由,显示不同状态栏颜色。
- 等等...
2.解决方案
解决思路:
- MaterialApp的routes属性赋值路由数组,navigatorObservers属性赋值路由监听对象NavigatorManager。
- 在NavigatorManager里实现NavigatorObserver的didPush/didReplace/didPop/didRemove,并记录到路由栈List_mRoutes中。
- 将实时记录的路由跳转,用stream发一个广播,哪里需要哪里注册。
3.具体实现
main.dart
MaterialApp( navigatorObservers:[NavigatorManager.getInstance()], routes:NavigatorManager.configRoutes, ... )
navigator_manager.dart
classNavigatorManagerextendsNavigatorObserver{
/*配置routes*/
staticMapconfigRoutes={
PackageInfoPage.sName:(context)=>
SplashPage.sName:(context)=>SplashPage(),
LoginPage.sName:(context)=>SplashPage()),
MainPage.sName:(context)=>SplashPage(),
//...
}
//当前路由栈
staticList_mRoutes;
Listgetroutes=>_mRoutes;
//当前路由
RoutegetcurrentRoute=>_mRoutes[_mRoutes.length-1];
//stream相关
staticStreamController_streamController;
StreamControllergetstreamController=>_streamController;
//用来路由跳转
staticNavigatorStatenavigator;
/*单例给出NavigatorManager*/
staticNavigatorManagernavigatorManager;
staticNavigatorManagergetInstance(){
if(navigatorManager==null){
navigatorManager=newNavigatorManager();
_streamController=StreamController.broadcast();
}
returnnavigatorManager;
}
//replace页面
pushReplacementNamed(StringrouteName,[WidgetBuilderbuilder]){
returnnavigator.pushReplacement(
CupertinoPageRoute(
builder:builder??configRoutes[routeName],
settings:RouteSettings(name:routeName),
),
);
}
//push页面
pushNamed(StringrouteName,[WidgetBuilderbuilder]){
returnnavigator.push(
CupertinoPageRoute(
builder:builder??configRoutes[routeName],
settings:RouteSettings(name:routeName),
),
);
}
//pop页面
pop([Tresult]){
navigator.pop(result);
}
//push一个页面,移除该页面下面所有页面
pushNamedAndRemoveUntil(StringnewRouteName){
returnnavigator.pushNamedAndRemoveUntil(newRouteName,(Routeroute)=>false);
}
//当调用Navigator.push时回调
@override
voiddidPush(Routeroute,RoutepreviousRoute){
super.didPush(route,previousRoute);
if(_mRoutes==null){
_mRoutes=newList();
}
//这里过滤调push的是dialog的情况
if(routeisCupertinoPageRoute||routeisMaterialPageRoute){
_mRoutes.add(route);
routeObserver();
}
}
//当调用Navigator.replace时回调
@override
voiddidReplace({RoutenewRoute,RouteoldRoute}){
super.didReplace();
if(newRouteisCupertinoPageRoute||newRouteisMaterialPageRoute){
_mRoutes.remove(oldRoute);
_mRoutes.add(newRoute);
routeObserver();
}
}
//当调用Navigator.pop时回调
@override
voiddidPop(Routeroute,RoutepreviousRoute){
super.didPop(route,previousRoute);
if(routeisCupertinoPageRoute||routeisMaterialPageRoute){
_mRoutes.remove(route);
routeObserver();
}
}
@override
voiddidRemove(RouteremovedRoute,RouteoldRoute){
super.didRemove(removedRoute,oldRoute);
if(removedRouteisCupertinoPageRoute||removedRouteisMaterialPageRoute){
_mRoutes.remove(removedRoute);
routeObserver();
}
}
voidrouteObserver(){
LogUtil.i(sName,'&&路由栈&&');
LogUtil.i(sName,_mRoutes);
LogUtil.i(sName,'&&当前路由&&');
LogUtil.i(sName,_mRoutes[_mRoutes.length-1]);
//当前页面的navigator,用来路由跳转
navigator=_mRoutes[_mRoutes.length-1].navigator;
streamController.sink.add(_mRoutes);
}
}
4.如何使用
token失效跳转
case401:
ToastUtil.showRed('登录失效,请重新登陆');
UserDao.clearAll();
NavigatorManager.getInstance().pushNamedAndRemoveUntil(LoginPage.sName);
break;
点击push推送跳转
staticjumpPage(StringpageName,[WidgetBuilderbuilder]){
StringcurrentRouteName=NavigatorManager.getInstance().currentRoute.settings.name;
//如果是未登录,不跳转
if(NavigatorManager.getInstance().routes[0].settings.name!=MainPage.sName){
return;
}
//如果已经是当前页面就replace
if(currentRouteName==pageName){
NavigatorManager.getInstance().pushReplacementNamed(pageName,builder);
}else{
NavigatorManager.getInstance().pushNamed(pageName,builder);
}
}
监听路由改变状态栏颜色
classStatusBarUtil{
staticListlightRouteNameList=[
TaskhallPage.sName,
//...
];
staticListdarkRoutNameList=[
SplashPage.sName,
LoginPage.sName,
MainPage.sName,
//...
];
staticinit(){
NavigatorManager.getInstance().streamController.stream.listen((state){
setupStatusBar(state[state.length-1]);
})
}
setupStatusBar(RoutecurrentRoute){
if(lightRouteNameList.contains(currentRoute.settings.name)){
setLight();
}elseif(darkRoutNameList.contains(currentRoute.settings.name)){
setDart();
}
}
}
完结,撒花