Flutter的Widget-Element-RenderObject flutter的渲染过程01

Flutter的Widget-Element-RenderObject

我的图片有些时候看得到水印 因为这个是我们的老师上课用的东西
  • 如果觉得有些东西不清楚可以去看老师的微信公众号 coderwhy
  • 这里只是把他上课的东西整理成笔记了

这个东西是flutter的渲染过程

这个东西虽然对我们代码没有直接关系 但是它可以让你写代码更加自信

这节课可以解决的问题

  • BuildContext 是什么东西
  • Widget频繁的创建 会不会影响性能
  • State和StatefulWidget 的关系
  • Key的用法

一. Flutter的渲染过程

1. 1 Widget-Element-RenderObject的关系

在这里插入图片描述
树结构 - 数据结构 它是一种非常重要的数据结构

我们使用flutter开发东西的时候 我们开发出来的东西的时候都是用Widget来进行搭建的

所以我们写的代码其实就是一个一个的Widget

他们之间存在一定的层级关系

比如MyApp就是我们的顶层 然后里面是HYHomePage 然后是其他的东西
在这里插入图片描述
这种结构就是一个树结构

所以这种Widget结构就是一个Widget树

我们运行flutter代码是基于Flutter SDK 这个里面有一个特别重要的东西

flutter Engine 它需要把我们的最终的界面 渲染成最终的界面

比如我们最后写了 一个Text那我们的flutter引擎是不是 就会解析这个Wiget然后把它显示到界面上

  • 但是其实并不是
    在这里插入图片描述
    flutter Engine它并不直接渲染Widget tree

因为我们在flutter 中Widget tree 是特别不稳定的

因为它动不动就会重新调用build方法 一旦调用build方法

build -> Widget -> Widget -> Widget 而且中间会依赖很多的Widget 一旦build 下面所有的Widget 都会重新创建

意味着整个Widget树都是不稳定的

而且一build里面的布局啊那些东西都是会变化的 我们的flutter 性能是做不到很高的

所以这个东西并不是直接渲染Widget的

我们这里还有一个对应的树结构

  • 叫做RenderObjectTree
    在这里插入图片描述

我们真正渲染的Widget就是它们

但是你看这里Widget tree 和Render tree 并不是一一对应的关系的

这个是因为很多的Widget 它其实更多的是一个盒子的作用并不是一个可渲染的东西

甚至我们的Text 它是继承至StatelessWidget那我们就会看他的build方法

哪个RichText才是最终渲染的在这里插入图片描述

然后我们再点RichText
在这里插入图片描述
这里它就继承至 xxRenderObjectWidget

继承制这种Widget才是flutter最终渲染的出来的Widget

Widget tree =》 Render tree 这样才能最终渲染出来

但是你进到Text , opacity, 甚至RichText里面它都是一些属性 它还是没有渲染的东西

在这里插入图片描述

在这里插入图片描述

渲染的时候我们需要做一个事 是要做layout 然后再做一个paint

layout -> paint

只要的你的东西是一个Widget它就没有任何layout相关的东西 也 没有任何和paint相关的东西

所以他们之间存在这样的关系

他们的这种关系其实借鉴react里面的虚拟dom

在这里插入图片描述
在早期的web的时候 你通过js生成的html代码 他是直接转换成真实的dom

但是这样它是非常消耗性能的, 它的代价是非常消耗性能的

如果我们的js里面改了一点点html的代码 我们就要操作dom这个是非常消耗性能的

  • 所以React里面就提出了一个概念 虚拟dom的概念
  • 意思是 加了一层东西
  • 如果js中改了html代码 它就去改虚拟dom

这个虚拟dom就像一个中间商
在这里插入图片描述
如果你改了html代码 它就会生成一个新的虚拟dom和原来的来做一个对比

在这里插入图片描述
这里就会用到diff算法 (different) 然后它就会把原来的地方打一个patch(补丁)
在这里插入图片描述
现在这里flutter这里的三棵树就基本和react 虚拟dom是一样的
在这里插入图片描述
到时候 你如果进行了修改之类的 他就会对比之前的东西 类型是否一样 key值是否一样

如果都一样 那么我们完全没有必要更新我们的Element 也就是我们的我们RenderObject完全没有必要重新创建这个东西 (可能widget里面是重新创建了一个对象 但是Element只是更新一个属性就可以了) 同理和RenderObject
在这里插入图片描述

1.2 Widget 是什么

在这里插入图片描述
官方对Widget的说明:

  • Flutter的Widgets的灵感来自React,中心思想是构造你的UI使用这些Widgets。
  • Widget使用配置和状态,描述这个View(界面)应该长什么样子。
  • 当一个Widget发生改变时,Widget就会重新build它的描述,框架会和之前的描述进行对比,来决定使用最小的改变(minimal changes)在渲染树中,从一个状态到另一个状态。

老师的理解:

  • Widget就是一个个描述文件,这些描述文件在我们进行状态改变时会不断的build。
  • 但是对于渲染对象来说,只会使用最小的开销来更新渲染界面。

1.3 Element是什么

在这里插入图片描述
官方对Element的描述:

  • Element是一个Widget的实例,在树中详细的位置。
  • Widget描述和配置子树的样子,而Element实际去配置在Element树中特定的位置。

1.4 RenderObject 是什么

在这里插入图片描述
官方对RenderObject的描述:

  • 渲染树上的一个对象
  • RenderObject层是渲染库的核心。

二. 对象的创建过程(这个创建的过程是否真会创建Element Render Widget树)

我们常见的几种构建的Widget在渲染的层面主要分成两种

  1. 组件Widget: 不会生成RenderObject
  • Containr
  • Text
  • HYHomePage
  1. 渲染Widget: 会生成RenderObject
  • Padding
  • Row

我们现在点到Padding 里面
在这里插入图片描述
然后我们发现这个Padding它是继承至 SingleChildRenderObjectWidget

这个是一个XXRenderObject的形式

然后这个SingleChildRenderObjectWidget它又继承至谁呢
在这里插入图片描述

Padding -> SingleChildRenderObjectWidget -> RenderObjectWidget

而Container

在这里插入图片描述

在这里插入图片描述

Container -> StatelessWidget -> Widget

所以Widget有那些子类呢 StatelessWidget StatefulWidget然后就是RenderObjectWidget
在这里插入图片描述
这个RenderObjectWidget 它有一个方法 createRenderObject
在这里插入图片描述
也就是说到这里的时候它有一个核心的方法 createRenderObject

这里它是一个抽象方法 可见这个方法他是没有实现的 那就是在子类中实现的(其实并没有在这个子类中实现)
Padding -> SingleChildRenderObjectWidget -> RenderObjectWidget
但是这个子类也没有实现
在这里插入图片描述
但是也没关系因为这个也是一个抽象类 所以我们到它的子类Padding里面去
在这里插入图片描述
Padding里面就有实现了
在这里插入图片描述
然后传入一些东西返回了一个东西 这个东西叫RenderPadding
在这里插入图片描述
然后它有继承关系 就点到父类去
在这里插入图片描述
再点进RenderBox里面

然后你就发现它继承至 RenderObject
在这里插入图片描述





  • 继承关系:
    在这里插入图片描述
  • 方法关系:

Padding -> createRenderObject -> RenderPadding -> … -> RenderBox -> RenderObject

  • 所以Padding这个类是在继承至RenderObjectWidget的方法createRenderObject
  • createRenderObject方法中创建了一个 RenderOject对象

所以很可能这个地方它就创建了一个Render树上的对象





但是我们现在并没有看到这个东西的调用的地方

但是如果你去其他的Container 里面 它是没有这个creatRenderObject 这个方法来创建一个RenderObject对象的

那我们现在就要知道它在什么地方调用创建的这个RenderObject的

我们点到Padding里面发现这个里面 是没有这个createRenderObject相关的东西的
Padding -> SingleChildRenderObjectWidget -> RenderObjectWidget
那我们就到它的父类SingleChildRenderObjectWidget

然后在父类里面有一个方法叫做 createElement 那这个方法是噶你什么的呢
在这里插入图片描述
我们来到 Widget
Padding -> SingleChildRenderObjectWidget -> RenderObject -> Widget
在这里插入图片描述
在这里插入图片描述
这个Widget它有一个抽象方法 createElement 这个东西是所有的Widget的父类

所以它所有的子类都是有这个方法的 它最终都是会实现这个方法的

createElement 方法 要么在某一个类里面实现 要么在子类中实现 要么在中间的某个类实现
在这里插入图片描述
每个类都会实现这个方法 只是说每个类实现的方式是不一样的

Padding虽然没有实现这个方法但是在它的父类SingleChildRenderObjectWidget是实现了这个方法的

在这里插入图片描述
那我们来看看Container里面的这个方法

Container里面是没有实现这个方法的
在这里插入图片描述
所以我们去它的父类去找

可见这个
在这里插入图片描述

所以我们可以下结论了 所有的Widget都会创建Element对象

不论它是渲染Widget还是组件Widget

但是这边创建的Element对象是不一样的

我们看到这个StatelessWidget 的方法 创建的Element对象叫做 StatelessElement
在这里插入图片描述
如过现在到StatefulWidget里面去看
在这里插入图片描述
它创建的是这个Element

但是它们是有相似的地方的

你会发现他们都是继承至ComponentElement
在这里插入图片描述

  • 它们的本质是一样的 它们都是继承至ComponentElement这个Element的

但是StatefulWidget会比StatelessWidget多一些属性

  • StatelessWidget
  • StatefulWidget -> State

同样的Padding 父类SingleChildRenderObjectWidget 也有这个方法 同样也会创建Element对象
在这里插入图片描述
但是 渲染Widget 和 组件Widget 他们是不一样的

  1. 写Widget
  2. 某些Widget会创建RenderObject
  3. 所有的Widget都会创建Element对象
  4. mount 方法

这个方法是flutter Engine会调用的方法

我们点到ElementWidget里面去
在这里插入图片描述
然后点到它的根Widget Element里面去
在这里插入图片描述
这个地方有一个mount方法

所有的widget都有element 同时element的根有mount这个方法

在这里插入图片描述

  1. 写Widget
  2. 某些Widget会创建RenderObject
  3. 所有的Widget都会创建Element对象
  4. mount 方法:

我们来到这个StatelessElement
在这里插入图片描述
但是它没有这个mount方法

所以它的父类必然是有这个方法的
在这里插入图片描述
然后你注意到它执行了一个_fristBuild方法
在这里插入图片描述
我们点进去
在这里插入图片描述
然后这里执行了一个performRebuild方法
在这里插入图片描述
然后就发现这个东西没有实现
在这里插入图片描述

那我们就要看它的实现 我们需要去看它的Components的实现 因为我们是从Components点过来的嘛
在这里插入图片描述
这里就需要看大量的源码

然后它这里干了一件事 它调用了一下这个build方法

这个build() 结果就是 Widget

也就是说我们的这个Element 里面build
在这里插入图片描述

然后再点进去

这个东西是一个抽象的方法

在这里插入图片描述

所以我们再找它的实现方法
在这里插入图片描述
同样我们找 StateElement里的这个方法
在这里插入图片描述
然后发现这个东西它掉了一个下Widget的build方法

  • 所以你看创建Element系统在调mount方法的时候
  • 它调用了_fristBuild -> rebuild -> performBuild -> build -> widget.build

我猜这里就是将Widget和Element链接起来了

那这个widget 是谁呢

我们来到这个StatelessWidget里面的createElement
在这里插入图片描述
我们发现这个地方我们创建StatelessWidget的时候把this传进去了

这个this 是谁 这个this在Widget里面那这个this就是一个widget

我们点进去

发现这个东西传给父类了
在这里插入图片描述
同理
在这里插入图片描述
然后我们就看到在根的Element里面这个东西_widget被赋值为传入的widget
在这里插入图片描述

所以传入的widget 是不是就是创建的widget啊

_fristBuild -> rebuild -> performBuild -> build -> widget.build

我们之前不是说 所有的widget创建以后都会调用自己的build方法

在哪调用的就是在这调用的

为什么每次我们的widget创建出来以后它会调用build方法呢

就是这样一步一步的调用的

  • 整理一下

我们在 所有的Widget的父类它有一个方法createElement

我们来到StatelessWidget的找到它的createElement 发现它是创建了一个StatelessElement然后再到它的父类ComponentElement

发现这个父类里面有一个mount 方法 这个mount比较关键

它里面调用了一个_fristBuild -> rebuild 这个方法也调用了Component方法里面的rebuild方法 我们再点到rebuild方法

发现这个方法来自于Element rebuild方法里面调用了performRebuild 方法 这个方法同样来自Element 但是它是一个抽象方法 所以我们去找它的子类

我们又来到ComponentElement它实现了这个方法 这个方法里面又一个重要的方法build 我们再点到build里面

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Flutter中的TextField是一个常用的输入控件,它允许用户在应用程序中输入文本。 TextField通常用于表单中,可以接收用户输入的文本,比如用户名、密码、电子邮件地址等。 以下是一个基本的TextField示例: ```dart TextField( decoration: InputDecoration( hintText: '请输入文本', ), ); ``` 在上面的示例中,我们使用了decoration属性来设置TextField的外观,其中hintText属性用于设置提示文本。 TextField还有许多其他的属性可以设置,比如maxLength(允许输入的最大长度)、keyboardType(键盘类型)、obscureText(是否隐藏输入内容)等等。 下面是一个更完整的TextField示例: ```dart class MyTextField extends StatefulWidget { @override _MyTextFieldState createState() => _MyTextFieldState(); } class _MyTextFieldState extends State<MyTextField> { final TextEditingController _controller = TextEditingController(); @override Widget build(BuildContext context) { return TextField( controller: _controller, maxLength: 10, keyboardType: TextInputType.text, obscureText: true, decoration: InputDecoration( hintText: '请输入文本', labelText: '文本框', prefixIcon: Icon(Icons.text_fields), suffixIcon: IconButton( icon: Icon(Icons.clear), onPressed: () { setState(() { _controller.clear(); }); }, ), border: OutlineInputBorder(), ), onChanged: (value) { print('输入的文本为:$value'); }, ); } } ``` 在上面的示例中,我们使用了TextEditingController来控制TextField的文本内容,maxLength属性限制了用户输入的最大长度为10,keyboardType属性指定了键盘类型为文本类型,obscureText属性设置为true表示隐藏输入内容。 此外,我们还设置了decoration属性来自定义TextField的外观,包括了输入提示文本、标签文本、前缀图标、后缀图标和边框样式等。 最后,我们通过onChanged回调函数来监听用户输入的文本,并打印出来。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值