Flutter完整开发实战详解(六、 深入Widget原理)

devtools/2025/2/10 22:40:16/

Flutter 番外的世界系列文章专栏

首先我们需要明白,Widget 是什么?这里有一个 “总所周知” 的答就是:Widget并不真正的渲染对象 。是的,事实上在 Flutter 中渲染是经历了从 WidgetElement 再到 RenderObject 的过程。

我们都知道 Widget 是不可变的,那么 Widget 是如何在不可变中去构建画面的?上面我们知道,Widget 是需要转化为 Element 去渲染的,而从下图注释可以看到,事实上 Widget 只是 Element 的一个配置描述 ,告诉 Element 这个实例如何去渲染。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

那么 Widget 和 Element 之间是怎样的对应关系呢?从上图注释也可知: Widget 和 Element 之间是一对多的关系 。实际上渲染树是由 Element 实例的节点构成的树,而作为配置文件的 Widget 可能被复用到树的多个部分,对应产生多个 Element 对象。

那么RenderObject 又是什么?它和上述两个的关系是什么?从源码注释写着 An object in the render tree 可以看出到 RenderObject 才是实际的渲染对象,而通过 Element 源码我们可以看出:Element 持有 RenderObject 和 Widget。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

再结合下图,可以大致总结出三者的关系是:配置文件 Widget 生成了 Element,而后创建 RenderObject 关联到 Element 的内部 renderObject 对象上,最后Flutter 通过 RenderObject 数据来布局和绘制。 理论上你也可以认为 RenderObject 是最终给 Flutter 的渲染数据,它保存了大小和位置等信息,Flutter 通过它去绘制出画面。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

说到 RenderObject ,就不得不说 RenderBoxA render object in a 2D Cartesian coordinate system,从源码注释可以看出,它是在继承 RenderObject 基础的布局和绘制功能上,实现了“笛卡尔坐标系”:以 Top、Left 为基点,通过宽高两个轴实现布局和嵌套的。

RenderBox 避免了直接使用 RenderObject 的麻烦场景,其中 RenderBox 的布局和计算大小是在 performLayout()performResize() 这两个方法中去处理,很多时候我们更多的是选择继承 RenderBox 去实现自定义。

综合上述情况,我们知道:

  • Widget只是显示的数据配置,所以相对而言是轻量级的存在,而 Flutter 中对 Widget 的也做了一定的优化,所以每次改变状态导致的 Widget 重构并不会有太大的问题。
  • RenderObject 就不同了,RenderObject 涉及到布局、计算、绘制等流程,要是每次都全部重新创建开销就比较大了。

所以针对是否每次都需要创建出新的 Element 和 RenderObject 对象,Widget 都做了对应的判断以便于复用,比如:在 newWidgetoldWidgetruntimeTypekey 相等时会选择使用 newWidget 去更新已经存在的 Element 对象,不然就选择重新创建新的 Element。

由此可知:Widget 重新创建,Element 树和 RenderObject 树并不会完全重新创建。

看到这,说个题外话:那一般我们可以怎么获取布局的大小和位置呢?

首先这里需要用到我们前文中提过的 GlobalKey ,通过 key 去获取到控件对象的 BuildContext,而我们也知道 BuildContext 的实现其实是 Element,而Element持有 RenderObject 。So,我们知道的 RenderObject ,实际上获取到的就是 RenderBox ,那么通过 RenderBox 我们就只大小和位置了。

showSizes() {
RenderBox renderBoxRed = fileListKey.currentContext.findRenderObject();
print(renderBoxRed.size);
}

showPositions() {
RenderBox renderBoxRed = fileListKey.currentContext.findRenderObject();
print(renderBoxRed.localToGlobal(Offset.zero));
}

自此,第六篇终于结束了!(///▽///)

资源推荐

  • Github : github.com/CarGuo/
  • 开源 Flutter 完整项目:github.com/CarGuo/GSYG…
  • 开源 Flutter 多案例学习型项目: github.com/CarGuo/GSYF…
  • 开源 Fluttre 实战电子书项目:github.com/CarGuo/GSYF…
完整开源项目推荐:

实战电子书项目:github.com/CarGuo/GSYF…**

完整开源项目推荐:

http://www.ppmy.cn/devtools/157340.html

相关文章

SpringBoot高级-底层原理

目录 1 SpringBoot自动化配置原理 01-SpringBoot2高级-starter依赖管理机制 02-SpringBoot2高级-自动化配置初体验 03-SpringBoot2高级-底层原理-Configuration配置注解 04-SpringBoot2高级-底层原理-Import注解使用1 05-SpringBoot2高级-底层原理-Import注解使用2 06-S…

内核日志查看:dmesg命令

dmesg 是 Linux 系统中用于 查看或控制内核环形缓冲区 的命令行工具。它主要用于显示系统启动时的硬件检测信息、内核日志以及运行时的硬件/驱动相关事件(如 USB 设备插拔、磁盘挂载等)。以下是 dmesg 的详细说明: 基本功能 查看内核日志&am…

基于ansible自动化部署ftp服务

Ansible部署FTP服务 基础环境配置就不过多赘述了 配置主机名、主机解析、免密访问、ansible下载、配置ansible主机、防火墙、selinux、配置centos2009镜像为仓库源、配置ftp远程仓库:可参考博文 节点信息如下: 主机名IPansible192.168.200.75node192…

RabbitMQ 从入门到精通:从工作模式到集群部署实战(二)

接上篇:《RabbitMQ 从入门到精通:从工作模式到集群部署实战(一)》 链接 文章目录 4.安装RabbitMQ Messaging Topology Operator 裸金属环境部署RabbitMQ部署单实例部署集群 4.安装RabbitMQ Messaging Topology Operator 使用 cer…

string 与 wstring 的字符编码

测试代码: #include<stdio.h> #include<stdlib.h> #include<windows.h> #include <locale.h> #include <string> #include <iostream>// 函数用于计算UTF-8字符串中的字符数 int utf8_strlen(const char* str) {int len = 0;for (; *s…

Gitee AI上线:开启免费DeepSeek模型新时代

一、引言 在当今数字化浪潮汹涌澎湃的时代&#xff0c;人工智能&#xff08;AI&#xff09;已成为推动各行业变革与发展的核心驱动力。从智能语音助手到图像识别技术&#xff0c;从自动驾驶汽车到金融风险预测&#xff0c;AI的应用无处不在&#xff0c;深刻地改变着我们的生活和…

Windows下AMD显卡在本地运行大语言模型(deepseek-r1)

Windows下AMD显卡在本地运行大语言模型 本人电脑配置第一步先在官网确认自己的 AMD 显卡是否支持 ROCm下载Ollama安装程序模型下载位置更改下载 ROCmLibs先确认自己显卡的gfx型号下载解压 替换替换rocblas.dll替换library文件夹下的所有 重启Ollama下载模型运行效果 本人电脑配…

Elasticsearch term精确查询无数据

Elasticsearch数据库中存在数据&#xff0c;但是使用term无法查到&#xff0c; 原因Elasticsearch 对文本字段的处理有两种主要类型&#xff1a;text 和 keyword, 当你对 text 类型的字段使用 match 查询时&#xff0c;Elasticsearch 会自动对查询字符串进行分词&#xff0c;并…