flutter如何建立的视图树(WidgetTree),元素树(ElementTree)及渲染树(RenderingTree),又是如何更新视图绘制视图? 这个问题太大,刚开始一切又都是陌生的,理解起来千头万绪,所以先搞清这些树的根结点的身份是非常必要的。毫无疑问,这些根节点的建立紧密的与初始化过程关联,而确定了这些根节点之后,遍历查找更新就相对清晰了,因为绘制视图无非也是对树的遍历查找更新操作。

这部分就已经从引擎层进入到了dart层,需要了解的更多的是框架相关的机制,引擎目前用不到了。

环境: flutter sdk v1.7.8+hotfix.4@stable

先不要被Element, RenderObjectElement, RenderObject, Widget,RenderObjectWidget诸多名称吓到。与安卓封装了显式的启动运行过程不同,flutter有一个明确的runApp, 这就是进行分析的方便入口。

语言机制

多继承

需要先了解一下语言层面的一个多继承机制。虽然这里用了多继承这个名词,但是需要明确dart语言在语法上还是单继承,也就是只能extends一个类,其它接口分别再以with串接。

关键字声明

与java不同,dart没有interface(准确的说是已移除)只有abstractabstract的使用与java并无二致。没有了interface如何实现多接口对象的声明?dart用的是mixin关键字,所以就方便理解而言,把mixin当作interface, on当作extends(只针对mixin类)即可。与interface不同的是mixin声明的类是可以有方法实现体和成员对象的

class A extends B implements C, D, E {}
class B {}
interface C {}
interface D {}
interface E {}

dart等同于:

class A extends B with C, D, E {}
class B {}
mixin C {}
mixin D {}
mixin E {}

继承顺序

在以上例子中假如B,C,D都有doSomeThing方法

class A extends B with C, D {@overridevoid doSomeThing() {print("A");super.doSomeThing();}
}class B {@overridevoid doSomeThing() {print("B");}
}mixin C on B {@overridevoid doSomeThing() {print("C");super.doSomeThing();}
}mixin D on B {@overridevoid doSomeThing() {print("D");super.doSomeThing();}
}void main() {A().doSomeThing();
}

那么当执行A.doSomeThing后应该是哪个调用顺序?
直接给结论:以with声明的反顺序继承
那么问题来了:如果没有C on B会发生什么?
语言机制问题可参考这篇文章。

串连调用

需要了解的第2个语法特性是串连调用,可以用..操作符串连调用类的成员方法:

class F {String str;String contact(String s) {return str + s;}void assign(String s) {str = s;}
}void mai() {F f = F()..assign("hello")..contact(' world');print(f.str);
}

需要明确:用了..操作符之后调用返回的就是类对象实例,不再是方法的返回值。

初始化调用

有了以上基础(用到语言特性1: mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding)就可以理清runApp入口的调用序列:

runAppWidgetsFlutterBinding.ensureInitializedWidgetsFlutterBinding()BindingBase()WidgetsBinding.initInstancesRendererBinding.initInstancesSemanticsBinding.initInstancesPaintingBinding.initInstancesSchedulerBinding.initInstancesServicesBinding.initInstancesGestureBinding.initInstancesBindingBase.initInstances

这里包含了大量的数据初始化,用到一个找一个。
再看整体序列(widgets/binding.dart:786, 用到语言特性2):

runAppWidgetsFlutterBinding.ensureInitializedWidgetsBinding.attachRootWidgetWidgetsBinding.scheduleWarmUpFrame

MyApp实例被传给了WidgetsBinding.attachRootWidget方法,于是分析其调用序列:

runAppWidgetsBinding.attachRootWidgetRenderObjectToWidgetAdapter()RenderObjectToWidgetAdapter.attachToRenderTreeRenderObjectToWidgetAdapter.createElementRenderObjectToWidgetElement<RenderBox>.assignOwnerBuildOwner.buildScopeRenderObjectToWidgetElement<RenderBox>.mount

需要注意RenderObjectToWidgetAdapter是一个RenderObjectWidget类型,它用构造函数child: rootWidget, 持有了外部传入的rootWidget作为它的子视图。
RenderObjectToWidgetAdapter.createElement创建的元素被赋值给了_renderViewElement_renderViewElementWidgetsBinding实例持有。

元素关联渲染

那根渲染又是何时创建的呢?继续看mount的调用序列:

RenderObjectToWidgetElement<RenderBox>.mountRootRenderObjectElement.mountRenderObjectElement.mountRenderObjectWidget.createRenderObject => RenderObjectToWidgetAdapter.createRenderObject

这里容易让人误导,调用createRenderObject的其实是RenderObjectElement持有的RenderObjectWidget, 而元素RenderObjectToWidgetElement正是RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this)(widgets/binding.dart:833)所创建,这里的this其实就是RenderObjectToWidgetAdapter,所以根渲染是RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;(widgets/bindings.836),可见根渲染不是在此时创建的,而是预先被赋值仅在此时返回的。

各种根节点

由此可见MyApp作为外部传入的rootWidget不是真正的根视图,真正的根视图其实是RenderObjectToWidgetAdapter, 它被RenderObjectToWidgetElement<RenderBox>持有(一个Element持有一个Widget), 而这个Element被全局WidgetsBinding实例持有,所以根元素为RenderObjectToWidgetElement<RenderBox>

RenderObjectElementmount的时机创建了一个RenderObject实例并持有,而RenderObjectToWidgetElementRenderObjectElement的子类,创建的RenderObject具体类型为RenderObjectWithChildMixin<RenderBox>,所以它才是最终的根渲染。

有了rootElement就可以找到rootWidgetrootRenderObject, 元素树,视图树与渲染树由此建立起来。

根渲染创建

回到RenderObjectToWidgetAdapter调用构造函数的地方,传入的containerRenderingBindingRenderView get renderView => _pipelineOwner.rootNode;(rendering/binding.dart:162, attachRootWidgetWidgetsBinding的方法,但 mixin WidgetsBinding on RendererBinding,所以可以引用到RenderingBinding的成员)。

那么rootRenderObject,也就是上面的RenderView, 作为RenderObjectWithChildMixin<RenderBox>的子类(class RenderView with RenderObjectWithChildMixin<RenderBox>),又是在什么时机创建的?跟踪下来正是在初始化调用中:

runAppWidgetsFlutterBinding.ensureInitializedWidgetsFlutterBinding()BindingBase()WidgetsBinding.initInstancesRendererBinding.initInstances_pipelineOwner = PipelineOwner(RendererBinding.initRenderViewrenderView = RenderView()_pipelineOwner.rootNode = value;

也就是说WidgetBinding把RendererBinding(mixin WidgetBinding with RendererBinding)的renderView作为了根渲染,而它实际是_pipelineOwner.rootNode

至此,我们便知道了所有节点遍历的起点。

转载于:https://www.cnblogs.com/lindeer/p/11296142.html

flutter: 根视图、根元素与根渲染相关推荐

  1. 从当前活动获取根视图

    我知道如何使用View.getRootView()获得根视图. 我还可以从按钮的onClick事件获取视图,其中参数为View . 但是如何获得活动中的视图 ? #1楼 我只在android 4.0. ...

  2. 【JetPack】ViewBinding 视图绑定组件 ( 启用模块 | 视图绑定定制 | 绑定类名称生成规则 | 绑定类字段生成规则 | 绑定类获取根视图 | 绑定类获取布局组件 )

    文章目录 I . 视图绑定组件简介 II . 视图绑定 ViewBinding 使用前提 ( Android Studio 3.6 ) III . 视图绑定组件启用 IV . 定制视图绑定 ( 启用视 ...

  3. android获取根视图

    android获取根视图 activity.getWindow().getDecorView() 转载于:https://www.cnblogs.com/baron89/p/4118342.html

  4. ios修改根视图控制器

    根视图除了可以在AppDelegate.m文件中指定之外也可以在外面定义// 修改根视图 24.AppDelegate* appDelagete = [UIApplication sharedAppl ...

  5. SwiftUI Navigation 如何快速从子视图返回根视图

    关键词 Navigation NavigationView NavigationLink 实战需求 在 UIKit 中,我使用 popToToRootViewController实现从子视图快速返回父 ...

  6. R语言应用uniroot函数求解方程的根(一元解):仿真数据(方程式可视化、并添加y=0的水平横线)、uniroot函数求解方程的根(并添加方程根对应的垂直竖线)

    R语言应用uniroot函数求解方程的根(一元解):仿真数据(方程式可视化.并添加y=0的水平横线).uniroot函数求解方程的根(并添加方程根对应的垂直竖线) 目录

  7. 根可达算法的根_我的JVM(六):GC的基础概念以及GC算法

    一.概述 垃圾收集Garbage Collection通常被称为GC,但是GC一般也指Garbage Collecting(垃圾回收这个动作)或Garbage Collector(垃圾回收器),这些都 ...

  8. 世界有几个终端服务器,全球互联网终端服务器共13根,美国占据10根,美真可以关闭中国网络?...

    全球互联网终端服务器共13根,美国占据10根,美真可以关闭中国网络? 随着人类文明建设的发展,新型信息化社会已油然而生,由一条条数据链组成的世界网覆盖了全球各地,而组建网络互通的纽带就是互联网.互联网 ...

  9. 姓名学中萍字无根 怎么解释_无根Buildah的工作原理:在非特权环境中构建容器

    姓名学中萍字无根 怎么解释 在以前的文章中,包括无根Podman如何工作? ,我谈到了Podman ,该工具使用户可以管理Pod,容器和容器图像. Buildah是用于构建与Podman互补的Open ...

最新文章

  1. 在CentOS8下安装docker遇到的containerd.io >= 1.4.1解决方法
  2. Python实现tab文件操作
  3. python中实例方法与实例属性-Python中的类属性和实例属性以及静态方法和类方法...
  4. 取景框图片 小程序_敲敲级简单的鉴别H图片的小程序
  5. 整理JAVA知识点--基础篇,能力有限不足地方请大神们帮忙完善下
  6. mysql本地连接报错1130_mySql连接问题(本地连接加远程连接)
  7. css3的自定义字体
  8. 1118. Birds in Forest (25)
  9. 平移刚体上各点的加速度和速度_大物学习笔记(一)——刚体力学
  10. Mysql添加用户错误:ERROR 1364 (HY000): Field ‘ssl_cipher‘ doesn‘t have a default value解决方法
  11. RK3288_Android7.1调试以太网ethernet
  12. excel曲线拟合怎么弄
  13. js:常用的3种弹出提示框(alert、confirm、prompt)
  14. 第29章基于锁的并发数据结构
  15. 110.Balanced Binary Tree
  16. Java项目01-菜谱管理系统控制台实现
  17. WireShark 抓包使用教程--详细
  18. html5 银行卡号校验,JQuery验证“银行卡”卡号 代码实例
  19. xpath helper插件:网页爬虫分析工具
  20. 【JavaScript 逆向】某道翻译接口逆向

热门文章

  1. 先查询后修改并发的时候sql_SQL调优总结
  2. python简单例子lof_Python的净值数据接口调用示例分享
  3. mysql capi函数详解_技术分享|MySQLCAPI参数MYSQL_OPT_READ_TIMEOUT的一些行为分析
  4. 小学计算机课的评语,小学信息技术评课稿
  5. linux source多个文件夹,linux下source命令使用详解
  6. Python datatime date
  7. Pandas to_datetime
  8. mac 黑窗口连接mysql_python操作mysql数据库
  9. Cissp全过程(简介到考试后)
  10. 压测学习总结(3)——Jmeter 脚本如何生成