小菜计划针对页面间跳转的路由相关知识做一个汇总,发现有两类特殊方法暂未研究,今天特补充 Navigator 相关方法应用;
canPop
小菜理解 Navigator 是对栈的操作,对于出栈的过程,可以通过 canPop 判断栈内 Page 是否存在,防止在栈内没有元素时强制 Pop 出栈引起异常;
源码解析
bool canPop() {
return _history.length > 1 || _history[0].willHandlePopInternally;
}
复制代码
案例尝试
if (Navigator.of(context).canPop()) {
print('当前 ${ModalRoute.of(context).settings.name} 可以 Pop !');
Navigator.pop(context);
} else {
print('当前 Page 无法 Pop ! ModalRoute.of(context).isFirst = ${ModalRoute.of(context).isFirst}');
}
复制代码
maybePop
canPop 只是对栈内元素是否可以出栈的判断,而 maybePop 不仅可以判断还可以执行 Pop 出栈操作;
源码解析
Future<bool> maybePop<T extends Object>([ T result ]) async {
final _RouteEntry lastEntry = _history.lastWhere(_RouteEntry.isPresentPredicate, orElse: () => null);
if (lastEntry == null) return false;
final RoutePopDisposition disposition = await lastEntry.route.willPop(); // this is asynchronous
if (!mounted)
return true; // forget about this pop, we were disposed in the meantime
final _RouteEntry newLastEntry = _history.lastWhere(_RouteEntry.isPresentPredicate, orElse: () => null);
if (lastEntry != newLastEntry)
return true; // forget about this pop, something happened to our history in the meantime
switch (disposition) {
case RoutePopDisposition.bubble:
return false;
case RoutePopDisposition.pop:
pop(result);
return true;
case RoutePopDisposition.doNotPop:
return true;
}
return null;
}
复制代码
简单分析源码可得,maybePop 会有限判断当前路由栈在列表中是否为最后一个,如果是最后一个则不进行出栈操作,否则进行 Pop 出栈;小菜简单理解为 maybePop >= canPop + Pop;
案例尝试
// 分别在 PageA 和 PageB 页面调用 maybePop
Navigator.of(context).maybePop();
复制代码
MaterialApp
我们每次新建一个工程,通常会采用 MaterialApp 作为 runApp() 的始点,MaterialApp 是 Android 风格的,若需要 iOS 风格的,则需要 CupertinoApp;即作为整个应用风格 Widget;而 MaterialApp / CupertinoApp / WidgetApp 等小组件默认是内嵌 Navigator 的,小菜接下来介绍 MaterialApp 几个重要属性;
1. home
当进入应用时,初始化展示的 Widget;
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
initialRoute: '/',
onGenerateRoute: generateRoute,
home: Scaffold(
appBar: AppBar(title: Text('HomePage')),
body: Center(child: Text('HomePage'))));
}
}
复制代码
2. routes
routes 为静态路由映射表,是 Map<String, WidgetBuilder> 类型,当使用类似于 pushNamed 静态路由方式进行页面跳转时,其对应路由首先需要在此绑定;一般默认 / 对应 root 页面,当然我们可以自定义为其他名称,只是系统规则一般是 /,其中 Navigator.defaultRouteName 对应的也是 /;其余的页面路由可以根据业务逻辑进行文件夹式的层级结构;小菜在 Android 原生开发时采用过 ARouter 插件,其方式基本类似;
注意: 一般采用 home 方式展示 Widget 时,路由表中不设置 / 对应 root 路由;
3. initialRoute
initialRoute 用于设置初始启动页面,一般设置后就无需设置 home 属性,因为 home 对应展示 Widget 优先级更高;若首页映射表名称采用 / 对应 root 路由时,可以省略 initialRoute 属性;
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
// initialRoute: '/',
routes: { '/': (context) => HomePage(title: 'HomePage') },
onGenerateRoute: generateRoute,
// home: Scaffold(
// appBar: AppBar(title: Text('HomePage')),
// body: Center(child: Text('HomePage'))),
);
}
}
复制代码
4. onGenerateRoute
onGenerateRoute 为 RouteFactory 类型构造函数,当使用静态路由进行页面跳转时,进入未在 routes 中绑定的页面时,都会在 onGenerateRoute 中进行回调;一般在封装时,不设置 routes 属性,均在 onGenerateRoute 中进行业务判断,常用作类似于拦截器的路由守卫等;同时对于公共的自定义路由专场动画也可以再此处理;
Function generateRoute = (settings) {
print('onGenerateRoute -> $settings');
if (settings == null ||
settings.name == null ||
routes[settings.name] == null) {
return MaterialPageRoute(builder: (context) => routes['/error']());
} else if (settings.arguments != null) {
return MaterialPageRoute(
builder: (context) => routes[settings.name](settings.arguments));
} else if (settings.name == '/') {
return MaterialPageRoute(
builder: (context) => routes[settings.name]('HomePage'));
} else {
return MaterialPageRoute(builder: (context) => routes[settings.name]());
}
};
复制代码
5. onUnknownRoute
onUnknownRoute 同样为 RouteFactory 类型构造函数,当使用静态路由进行页面跳转时,无法在 onGenerateRoute 中生成时进行回调;
6. builder
builder 属性常用作 MediaQuery 设备信息获取或用户信息偏好设置等;小菜之前有整理过关于 MediaQuery 的学习,再次不做赘述;
对于页面间的跳转还有很多需要学习和探索的地方,小菜建议多读源码,多学习优秀三方库的实现方式;如有错误,请多多指导!
来源: 阿策小和尚
评论