【Flutter 专题】110 页面间小跳转 (四)

298 阅读4分钟

      小菜计划针对页面间跳转的路由相关知识做一个汇总,发现有两类特殊方法暂未研究,今天特补充 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() 的始点,MaterialAppAndroid 风格的,若需要 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

      onGenerateRouteRouteFactory 类型构造函数,当使用静态路由进行页面跳转时,进入未在 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 的学习,再次不做赘述;


      对于页面间的跳转还有很多需要学习和探索的地方,小菜建议多读源码,多学习优秀三方库的实现方式;如有错误,请多多指导!

来源: 阿策小和尚