引用以下两篇文章,已经很详细说明路由原理了
https://juejin.im/post/5c8db8e8f265da2d864b889f
https://juejin.im/post/5c8db8fff265da2de52dd80f
Overlay rebuild的时候是会调用
List<OverlayEntry> _entries
内OverlayEntry的builder方法rebuild child widget的
MaterialApp
MaterialApp(
routes: {'main': (context) => Counter()},
onGenerateRoute: (setting) {
// setting.name
return MaterialPageRoute(builder: (_) => Counter());
},
),
_MaterialAppState内使用了WidgetsApp,pushName的时候先判断routes是否有,routes没有再返回widget.onGenerateRoute(settings)
WidgetsApp(
routes: widget.routes,
onGenerateRoute: widget.onGenerateRoute,
)
Route<dynamic> _onGenerateRoute(RouteSettings settings) {
final String name = settings.name;
final WidgetBuilder pageContentBuilder = name == Navigator.defaultRouteName && widget.home != null
? (BuildContext context) => widget.home
: widget.routes[name];
if (pageContentBuilder != null) {
final Route<dynamic> route = widget.pageRouteBuilder<dynamic>(
settings,
pageContentBuilder,
);
return route;
}
if (widget.onGenerateRoute != null)
return widget.onGenerateRoute(settings);
return null;
}
Widget build(BuildContext context) {
Widget navigator;
if (_navigator != null) {
navigator = Navigator(
...
onGenerateRoute: _onGenerateRoute,
...
);
}
}
路由管理封装
为了达到的目的:
- 使用
popUntil
等和路由名称相关的方法 - 判断当前widget或者state是否是App正在显示的页面
- 新接手项目的成员可以快速找到相关页面
不使用
Navigator.pushNamed(context, 'routeName',arguments:arguemntsOjbect);
和onGenerateRoute
组合的原因是:pushNamed
传参麻烦;没有参数类型提示;且通过pushName
跳转页面的话不能通过代码直接跳到相应页面(如通过pushName
跳转到A页面,那在pushName
代码处不能通过点击A类名跳转到A类代码处)。
用法
- 监听路由跳转
MaterialApp(
navigatorObservers: [CustomNavigatorObserver()],
)
- 跳转到页面A
Navigator.of(context).push(Routes.routeForPage(page:PageA(),),);
- 跳转到页面A,同时传递Bloc
Navigator.of(context, rootNavigator: true).push(
Routes.routeForPage(
page: PageA(),
pageWrapBuilder: (page, _) => BlocProvider(
create: (context) => bloc,
child: page,
),
),
);
- 跳转到页面A,同时自定义Route
Navigator.of(context, rootNavigator: true).push(
Routes.routeForPage(
page: PageA(),
customRouteBuilder: (pageBuilder, settings) => CustomRoute(
settings: settings,
pageBuilder: (builderContext, _, __) => pageBuilder(builderContext),
),
),
);
- 返回页面A
Navigator.popUntil(context, Routes.filterRoute(Routes.pageA));
- 判断页面A是否在路由最顶层,也就是正在显示页面A
Routes.isPageAtTop(context,[PageA页面对应的widget或者state对象])
源码
typedef PageWrapBuilder = Widget Function(Widget child, BuildContext context);
typedef RouteBuilder = Route<dynamic> Function(WidgetBuilder pageBuilder, RouteSettings setting);
class Routes {
Routes._();
static Routes _routes = Routes._();
List<String> _routesName = [];
///页面A,此行代码可有可无,更多是为了让不熟悉项目的人快速找到相应页面。如果没用此方式重新定义路由名称的获取,则可以直接通过`Routes.routeNameForPage(PageA)`得到
static String pageA = _routeNameForPage(PageA);
static String _routeNameForPage(Type page) => page.toString();
///创建Route,routeName是page.runtimeType.toString()
static Route<dynamic> routeForPage({
@required Widget page,
PageWrapBuilder pageWrapBuilder,
RouteBuilder customRouteBuilder,
}) {
assert(page != null);
String routeName = _routeNameForPage(page.runtimeType);
WidgetBuilder builder;
if (pageWrapBuilder != null) {
builder = (context) => pageWrapBuilder(page, context);
} else {
builder = (_) => page;
}
if (customRouteBuilder != null) {
return customRouteBuilder(builder, RouteSettings(name: routeName));
} else {
return MaterialPageRoute(builder: builder, settings: RouteSettings(name: routeName));
}
}
///当前页面是否在路由顶层
static bool isPageAtTop(BuildContext context, dynamic page) {
assert(page is State || page is Widget);
String currentRouteName = ModalRoute.of(context).settings.name;
Widget widget = page is Widget ? page : (page as State).widget;
return _routeNameForPage(widget.runtimeType) == currentRouteName;
}
///页面对应的路由名称
static String routeNameForPage(dynamic page) {
assert(page is State || page is Widget);
Widget widget = page is Widget ? page : (page as State).widget;
return _routeNameForPage(widget.runtimeType);
}
///记录push的routeName
static _pushRouteName(String routeName) {
_routes._routesName.add(routeName ?? routeName.toString());
}
///pop的时候删除最后一个routeName
static _popRouteName() {
if (_routes._routesName.isNotEmpty) {
_routes._routesName.removeLast();
}
}
///最后一个route的name
static String get previousRouteName {
if (_routes._routesName.length > 1) {
return _routes._routesName[_routes._routesName.length - 2];
}
return null;
}
///最后一个route的name
///因为弹框之类的所有route name(为null)都记录在_routesName中,所以建此方法返回_routesName中在该类中定义的route name的最后一个
static String get previousNotNullRouteName {
return _routes._routesName?.reversed?.firstWhere((test) => test != null, orElse: () => null);
}
static RoutePredicate filterRoute(String routeName) => (route) => route.settings.name == routeName;
}
///用来监听路由push和pop
class CustomNavigatorObserver extends NavigatorObserver {
@override
void didPush(Route route, Route previousRoute) {
super.didPush(route, previousRoute);
///如果没有给route设置name的话,默认name是null
Routes._pushRouteName(route.settings.name);
}
@override
void didPop(Route route, Route previousRoute) {
super.didPop(route, previousRoute);
Routes._popRouteName();
}
@override
void didRemove(Route route, Route previousRoute) {
super.didRemove(route, previousRoute);
Routes._popRouteName();
}
@override
void didReplace({Route newRoute, Route oldRoute}) {
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
Routes._popRouteName();
Routes._pushRouteName(newRoute.settings.name);
}
}
网友评论