Flutter 110: 页面间小跳转 (四)

作者: 阿策神奇 | 来源:发表于2021-01-17 18:39 被阅读0次

          小菜计划针对页面间跳转的路由相关知识做一个汇总,发现有两类特殊方法暂未研究,今天特补充 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 的学习,再次不做赘述;


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

    来源: 阿策小和尚

    相关文章

      网友评论

        本文标题:Flutter 110: 页面间小跳转 (四)

        本文链接:https://www.haomeiwen.com/subject/clczaktx.html