Flutter Element挂载核心逻辑分析

前言

本篇主要介绍一下关于Flutter中父子Element如何实现挂载,Element树又是怎么样一个一个枝节节点完成搭建。尽管这些基础知识的文章介绍已经很多,但是相信5分钟看完本篇文章后你或许可以更透彻的理解这部分framework的源码逻辑。

本篇文章基于Flutter 3.7.1版本进行源码分析

开始分析

首先,贴一段看起来很...的代码:

我们要知道Flutter树结构是从上向下开始完成构建的,这里说的从上向下,通俗来讲就是先声明的Widget先构建,后声明的后构建。从这段代码片段里来看就是A→B→C,即元素深度是A先于B先于C的;

从这段及其简单及冗余的代码开始...我们先来理解一下这个步骤在framework代码中是怎么实现的;

可能有的朋友会说,这实际上就是一行代码,只是对象构造中的多层嵌套,最终return出去,明明是先执行了C而后是B再后才是A

没错,但是我们要知道,Flutter中每个Widget都可以理解为一个描述文件,传入的child以及其他各种配置参数只有在最终执行build方法的时候才会启动转换挂载;

这里就要追溯一下build方法在哪里被调用

从我前一篇文章中《Flutter中从runApp开始看Element树是如何实现第一次挂载的》可以得知,Flutter应用Element树上挂载的第一颗元素是runApp方法传入的Widget对应的Element,最终进入其Element#mount方法实现挂载,回顾一下mount方法:

这是Element类的mount方法,看起来好像并没有什么值得深入的东西,不过我们要知道Flutter是有多种Element的,这里我们就要看一下mount方法的子类实现

可以看到直接子类、间接子类种有非常多的方法重载;

回到上面的Container嵌套例子,我们就假设现在是ContainerA开始了挂载,来看一下其mount方法都做了什么逻辑,ContainerStatelessWidget,其对应的ElementStatelessElementStatelessElement 没有重写mount方法,其父类ComponentElement 进行了重写:

注意以上的mount_firstBuild方法块是ComponentElement中的,而rebuild方法又回到了Element中,继续看Element类中的performRebuild方法

Flutter 老版本此方法是抽象方法,强制子类实现;新版本(当前3.7.1)在父类中多了_dirty的赋值

不用怀疑,这就是一个要交给子类去重载的方法;

这里回到我们的例子中,我们现在假设上述看到的mount_firstBuildrebuild都是在ContainerA中的逻辑,而Container是一个StatelessWidget,其对应的ElementStatelessElement,而StatelessElement的父类是ComponentElement,我们来看看ComponentElement是如何重载这个父类"空"方法的:

这个方法里主要有两个重要的任务

  1. 调用自己的build方法,获取到一个Widget
  2. updateChild方法(这个方法是Element中的,也就是说这里从ComponentElement回到了父类中),返回值为_child变量赋值(_child是一个Element,是子类的Element

这里我们清楚一点,从我们的例子来解读,此处执行performRebuild的是ContainerAElement,所以这个build就是StatelessElementbuild方法:

这里就是我们熟知的Container类的build方法实现,这里就是对于传入的子Widget(即child)进行包装,然后返回一个Widget,看到这里,也就是我们的ContainerB,实际上就是被包装后赋值给了built变量,然后传入updateChild,这里先不深入,简单解释一下updateChild方法;

关于updateChild简单说明方法

Element类中的方法,主要负责的是更新子类,从方法入参可以看到,传入了_child,又将返回值给了_child,就是说这里面会进行一系列判断:子Element是否加载过?是否仅需更新而非重新构建?等等。无论如何,最终方法内会返回一个Element,并赋值给_child

尽管先不关注updateChild方法中的各种判断,不过有一个重要的点我们需要知道:

如果子Element(也就是我们例子中ContainerB对应的Element)之前不存在,需要重新构建一个,那么会进入其中的inflateWidget方法

这里面有两个我们熟悉的方法,createElementmount

至此,就完成了从ContainerAmountContainerBmount,就是我们的Element树的向下逐个挂载逻辑。


总结

看到这里我们只是看完了ComponentElement挂载的完整逻辑,至于其他类型的Element(比如RenderObjectElement),他们去加载子类Element的流程是不完全一样的,不过核心方法没有区别,最终都会进入它们的顶级父类Element中的updateChildinflateWidget等方法。

本篇先只梳理了ComponentElement的源码逻辑,最后做一下总结:

  • Element类中的mount方法里,只进行了一些通用的赋值操作,加载子类等操作都是在对应子Element的方法重载中
  • 最终加载子Element的核心方法是在顶级父类Element中的updateChildinflateWidget方法中
相关推荐
problc6 分钟前
CSS 工具对比:UnoCSS vs Tailwind CSS,谁是你的菜?
前端·css
天天摸鱼的java工程师12 分钟前
互联网行业能力解刨:从Java后端八年开发经验看
前端·后端·程序员
brzhang19 分钟前
Android 16 卫星连接 API 来了,带你写出「永不失联」的应用
前端·后端·架构
John_ToDebug33 分钟前
Chrome 浏览器前端与客户端双向通信实战
前端·c++·chrome
要加油哦~37 分钟前
CSS | transition 和 transform的用处和区别
前端·css
小鱼人爱编程1 小时前
现代大前端是如何编码的?
android·前端·flutter
移动开发者1号1 小时前
Android中Activity、Task与Process的关系
android·kotlin
神仙别闹1 小时前
基于Java+VUE+MariaDB实现(Web)仿小米商城
java·前端·vue.js
琪阿不会编程1 小时前
Mysql8 忘记密码重置,以及问题解决
android·数据库·sql·mysql
袁煦丞1 小时前
低成本私有云存储方案Nas-Cab:cpolar实验室第508次成功挑战
前端·程序员·远程工作