前言

上一篇 Flutter路由管理和页面参数的传递(获取&返回) 文章中我们讲述了这么用代码实现 Flutter 中页面参数的传递,这一篇我们用源码分析一下 Navigator 为什么可以进行页面参数传递。

从页面跳转入口的代码进行分析:

Navigator.of(context).pushNamed('/route1');

Navigator 的获取

Navigator 对应的 StateNavigatorState ,所以实际上我们需要获取的是 NavigatorState

class Navigator extends StatefulWidget {/******部分代码省略*****/static NavigatorState of(BuildContext context, {bool rootNavigator = false,bool nullOk = false,}) {final NavigatorState navigator = rootNavigator? context.rootAncestorStateOfType(const TypeMatcher<NavigatorState>()): context.ancestorStateOfType(const TypeMatcher<NavigatorState>());assert(() {if (navigator == null && !nullOk) {throw FlutterError('Navigator operation requested with a context that does not include a Navigator.\n''The context used to push or pop routes from the Navigator must be that of a ''widget that is a descendant of a Navigator widget.');}return true;}());return navigator;}
}

我们从源看到 NavigatorState 的获取实际是获取的 context.ancestorStateOfType

abstract class Element extends DiagnosticableTree implements BuildContext {/******部分代码省略*****/@overrideState ancestorStateOfType(TypeMatcher matcher) {assert(_debugCheckStateIsActiveForAncestorLookup());Element ancestor = _parent;while (ancestor != null) {//从当前的Element节点一直向上寻找到匹配的StatefulElementif (ancestor is StatefulElement && matcher.check(ancestor.state))break;ancestor = ancestor._parent;}final StatefulElement statefulAncestor = ancestor;//返回匹配的StatefulElement的statereturn statefulAncestor?.state;}
}

循环遍历向上寻找 Navigator 的 state ,这里就是 NavigatorState

Navigator的生成

NavigatorWidget 是是什么时候添加到视图树中的呢?我们从 Flutter 应用程序的入口开始一步一步跟进代码的执行:

void main() => runApp(MyApp());
class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(...);}
}

MaterialApp 传入 routesonGenerateRoute 等参数,MaterialAppstate_MaterialAppState 它构建的是 WidgetsApp 类型的 Widget ,同时 routesonGenerateRoute 等参数也进行了透传。

class MaterialApp extends StatefulWidget {const MaterialApp({Key key,this.navigatorKey,this.home,this.routes = const <String, WidgetBuilder>{},this.initialRoute,this.onGenerateRoute,this.onUnknownRoute,/******部分代码省略*****/})/******部分代码省略*****/@override_MaterialAppState createState() => _MaterialAppState();
}
class _MaterialAppState extends State<MaterialApp> {/******部分代码省略*****/@overrideWidget build(BuildContext context) {Widget result = WidgetsApp(key: GlobalObjectKey(this),navigatorKey: widget.navigatorKey,navigatorObservers: _navigatorObservers,pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) =>MaterialPageRoute<T>(settings: settings, builder: builder),home: widget.home,routes: widget.routes,initialRoute: widget.initialRoute,onGenerateRoute: widget.onGenerateRoute,onUnknownRoute: widget.onUnknownRoute,/******部分代码省略*****/);
}

我们再看看 WidgetsApp 对应的 State_WidgetsAppState 。在_WidgetsAppStateWidget build(BuildContext context) 方法中我们找到了管理路由的 Navigator 的构造时机。

class WidgetsApp extends StatefulWidget {WidgetsApp({ // can't be const because the asserts use methods on Iterable :-(Key key,this.navigatorKey,this.onGenerateRoute,this.onUnknownRoute,this.navigatorObservers = const <NavigatorObserver>[],this.initialRoute,this.pageRouteBuilder,this.home,this.routes = const <String, WidgetBuilder>{},/******部分代码省略*****/);@override_WidgetsAppState createState() => _WidgetsAppState();
}
class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserver {@overrideWidget build(BuildContext context) {Widget navigator;if (_navigator != null) {navigator = Navigator(key: _navigator,// If window.defaultRouteName isn't '/', we should assume it was set// intentionally via `setInitialRoute`, and should override whatever// is in [widget.initialRoute].initialRoute: WidgetsBinding.instance.window.defaultRouteName != Navigator.defaultRouteName? WidgetsBinding.instance.window.defaultRouteName: widget.initialRoute ?? WidgetsBinding.instance.window.defaultRouteName,onGenerateRoute: _onGenerateRoute,onUnknownRoute: _onUnknownRoute,observers: widget.navigatorObservers,);}Widget result;if (widget.builder != null) {result = Builder(builder: (BuildContext context) {return widget.builder(context, navigator);},);} else {assert(navigator != null);result = navigator;}/******部分代码省略*****//**上面经过多次的操作之后,navigator变为result的某个子孙节点上的child**/Widget title;if (widget.onGenerateTitle != null) {title = Builder(// This Builder exists to provide a context below the Localizations widget.// The onGenerateTitle callback can refer to Localizations via its context// parameter.builder: (BuildContext context) {final String title = widget.onGenerateTitle(context);assert(title != null, 'onGenerateTitle must return a non-null String');return Title(title: title,color: widget.color,child: result,);},);} else {title = Title(title: widget.title,color: widget.color,child: result,);}/******部分代码省略*****//**上面经过多次的操作之后,result变为title的某个子孙节点上的child**/return MediaQuery(data: MediaQueryData.fromWindow(WidgetsBinding.instance.window),child: Localizations(locale: appLocale,delegates: _localizationsDelegates.toList(),//将title作为child视图,也就是说navigator变为其中的某个子孙节点视图child: title,),);}
}

在构建的 MediaQuery 就存在我们需要的 Navigator

这张图是程序运行时候使用(DevTools)进行的页面元素分析,也证明了 Navigator 是在页面的 Widget 元素路径上的。

pushNamed方法解析

@optionalTypeArgs
static Future<T> pushNamed<T extends Object>(BuildContext context,String routeName, {Object arguments,}) {return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments);
}
@optionalTypeArgs
Future<T> pushNamed<T extends Object>(String routeName, {Object arguments,
}) {return push<T>(_routeNamed<T>(routeName, arguments: arguments));
}
Route<T> _routeNamed<T>(String name, { @required Object arguments, bool allowNull = false }) {assert(!_debugLocked);assert(name != null);final RouteSettings settings = RouteSettings(name: name,isInitialRoute: _history.isEmpty,arguments: arguments,);Route<T> route = widget.onGenerateRoute(settings);if (route == null && !allowNull) {assert(() {if (widget.onUnknownRoute == null) {throw FlutterError(...);}return true;}());route = widget.onUnknownRoute(settings);assert(() {if (route == null) {throw FlutterError(...);}return true;}());}return route;
}

我们看到是调用了 widget.onGenerateRoute(settings) 生成路由, 这里的 onGenerateRouteNavigator 在构造的时候传入的 onGenerateRoute

onGenerateRoute

Navigator 在构造的时候如果我们细心就会发现 onGenerateRoute 现在改为了 _onGenerateRoute

也就是 _WidgetsAppState_onGenerateRoute 方法实现:

Route<dynamic> _onGenerateRoute(RouteSettings settings) {final String name = settings.name;//从widget注册的路由中获取name对应的WidgetBuilderfinal WidgetBuilder pageContentBuilder = name == Navigator.defaultRouteName && widget.home != null? (BuildContext context) => widget.home: widget.routes[name];//如果pageContentBuilder不为空,那么和RouteSettings一起执行widget.pageRouteBuilder构造一个routeif (pageContentBuilder != null) {assert(widget.pageRouteBuilder != null,'The default onGenerateRoute handler for WidgetsApp must have a ''pageRouteBuilder set if the home or routes properties are set.');final Route<dynamic> route = widget.pageRouteBuilder<dynamic>(settings,pageContentBuilder,);assert(route != null,'The pageRouteBuilder for WidgetsApp must return a valid non-null Route.');return route;}//如果pageContentBuilder为空,那么执行widget.onGenerateRoute的方法if (widget.onGenerateRoute != null)return widget.onGenerateRoute(settings);return null;
}

widget.pageRouteBuilder 的方法,我们在生成 WidgetsApp可以看到是:

pageRouteBuilder: <T>(RouteSettings settings, WidgetBuilder builder) =>MaterialPageRoute<T>(settings: settings, builder: builder)

所以最终我们通过在 MaterialApp 注册 routes 生成了一个 MaterialPageRoute 用来进行页面跳转。

最后如果 routes 为空的话,我们执行 widget.onGenerateRoute 。这个解释了在 Flutter路由管理和页面参数的传递(获取&返回) 这篇文章末尾说的 onGenerateRoute 方式进行的参数传递,必须不能进行 routers 的注册。

文章到这里就全部讲述完啦,若有其他需要交流的可以留言哦

想阅读作者的更多文章,可以查看我 个人博客 和公共号:

Flutter路由管理和页面参数的传递(源码分析)相关推荐

  1. Flutter路由管理和页面参数的传递(获取返回)

    前言 在做 Flutter 开发的时候所有的页面以及页面上的元素都变成了 Widget ,创建一个页面或者视图直接 new 一个新的 widget 就可以,相关的参数我们可以直接通过构造函数直接传递. ...

  2. 分布式定时任务—xxl-job学习(四)——调度中心web页面端api调用源码分析

    分布式定时任务-xxl-job学习(四)--调度中心web页面端api调用源码分析 前言 一.controller目录下非controller类 1.1 PermissionLimit自定义注解 1. ...

  3. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

  4. 二进制安全:ptmalloc内存管理机制与堆块chunk源码分析

    敬告: <中华人民共和国刑法>第三百八十六条[破坏计算机系统罪:网络服务渎职罪]违反国家规定,对计算机信息系统功能进行删除.修改.增加.干扰,造成计算机系统不能正常运行,后果严重的,处五年 ...

  5. [pig框架实战] 手撕视频管理发布平台[02] - pig框架源码分析(去除开发过程中每次输入验证码的过程,方便快速登录)

    需求 将自己搭建好的项目添加到pig项目中 去除开发过程中每次输入验证码的过程,方便快速登录 去除验证码校验(服务器端) 去除验证码校验(前端) 将自己搭建好的项目myvideos添加到pig项目中 ...

  6. Linux内核源码分析《进程管理》

    Linux内核源码分析<进程管理> 前言 1. Linux 内核源码分析架构 2. 进程原理分析 2.1 进程基础知识 2.2 Linux进程四要素 2.3 进程描述符 task_stru ...

  7. Linux内存管理 brk(),mmap()系统调用源码分析1:基础部分

    Linux内存管理 brk(),mmap(),munmap()系统调用源码分析 基础部分 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.c ...

  8. v05.05 鸿蒙内核源码分析(任务管理) | 如何管理任务池 | 百篇博客分析HarmonyOS源码

    曾子曰:"吾日三省吾身:为人谋而不忠乎?与朋友交而不信乎?传不习乎?"<论语>:学而篇 百篇博客系列篇.本篇为: v05.xx 鸿蒙内核源码分析(任务管理篇) | 如何 ...

  9. Flutter系列-flutter路由管理

    Flutter路由管理 初识路由概念 一.路由管理 1.1.Route 1.2.MaterialPageRoute 1.3.Navigator 1.4.路由传值 1.5 命名路由 1.6.命名路由参数 ...

最新文章

  1. Android 判断某个服务是否正在运行
  2. 微软亚研院20周年独家撰文:数据智能的现在与未来
  3. 为什么我又喜欢过年了
  4. 学习python需要什么基础-python编程学习需要哪些基础和准备工作?
  5. Python源码深度解析—float空闲对象缓存池
  6. 第二阶段 站立会议 10
  7. 操作系统概念之OSAL
  8. 扫描工具扫描法查看网内IP使用情况
  9. CATIA V6二次开发——复杂装配体的结构树遍历
  10. 2015061906 - firebug安装和下载(2)
  11. 保留字符串中的大写字母(汇编语言)
  12. android 基带版本,基带是什么 基带版本是什么
  13. Altium Designer之多图纸设计
  14. 关于程序代码的时间复杂度
  15. VUE游戏开发:使用Box2D模拟球体的飞行和撞击特效
  16. Excel日期修改为统一月份
  17. 134.如何进行实时计算
  18. 最新第三方橘子/威客接单系统网站源码+有充值API
  19. 微信红包生成器App遭腾讯起诉:被判赔偿75万元
  20. 通讯:博物馆里过大年——英国科学博物馆举办科学“春晚”

热门文章

  1. 计算机组成原理地址码方案,《计算机组成原理》课程设计汇本案报告
  2. 树莓派2B利用HDMI驱动1440×2560屏设置
  3. 《Python核心编程》笔记 Python对象
  4. Android使用adb安装和覆盖安装apk
  5. ubuntu(23):ubuntu系统具有两个“系统盘”的说明:一个盘为系统盘,另一个为被系统文件占用的数据盘--可以格式化并重新分配卷类型和卷名称
  6. 为什么刘彻会成为一代名君?
  7. 用python让excel飞起来(行/列操作)
  8. JAVAweb开发技术-------(六)技术点-定时任务实现的三种方式
  9. 在使用matlab对矩阵求逆时出现了“警告: 矩阵接近奇异值,或者缩放错误。结果可能不准确
  10. HNOI 2002 营业额统计(Splay树)