当前位置: 首页 > news >正文

Flutter源码分析笔记:Widget类源码分析

Flutter源码分析笔记
Widget类源码分析

- 文章信息 - Author: 李俊才 (jcLee95)
Visit me at: https://jclee95.blog.csdn.net
Email: 291148484@163.com.
Shenzhen China
Address of this article:https://blog.csdn.net/qq_28550263/article/details/132259681

【介绍】:本文记录阅读与分析Flutter源码 - Widget类源码分析。


1. 概述

Widget类是Flutter框架中的核心类之一,用于描述用户界面的一部分。它是一个不可变的描述,可以被实例化为元素(Element),后者负责管理底层的渲染树。

Widget是Flutter框架的基础,用于描述用户界面的一部分,不可变且无状态。它有一些用于实例化元素、诊断调试和比较的方法和属性,同时它的子类可以是StatelessWidget或StatefulWidget,用于构建静态或有状态的用户界面部分。

这部分源码见附录F-1。

2. 属性

2.1 key

key属性控制一个widget如何替换树中的另一个widget。如果两个widget的runtimeType和key属性分别通过operator==比较是相等的,那么新的widget将通过更新底层元素来替换旧的widget。否则,旧的元素会从树中移除,新的widget会被实例化为元素,然后插入树中。

3. 方法

3.1 createElement()

该方法将配置实例化为一个具体的元素(Element)。一个widget可以在树中被包含零次或多次,每次被包含在树中时,都会被实例化为一个元素。

3.2 canUpdate(Widget oldWidget, Widget newWidget)

该方法用于判断一个新的widget是否可以用来更新一个已有元素的配置。根据runtimeType和key属性的比较来判断两个widget是否可以更新。

3.3 debugFillProperties(DiagnosticPropertiesBuilder properties)

debugFillProperties(DiagnosticPropertiesBuilder properties):向诊断信息属性构建器添加属性,用于调试和诊断。在子类中可以重写以提供更多的调试信息。

3.4 _debugConcreteSubtype(Widget widget)

返回一个数值,表示特定Widget子类型的具体编码。用于在热重载时判断已挂载元素的配置是否被修改。

3.5 其它

operator ==(Object other) 和 get hashCode

实现运算符”==“用于判断两个widget是否相等,以及计算它们的哈希值。

toStringShort()

返回此widget的简短文本描述,通常是它的runtimeType和key的组合。

4. 继承关系

4.1 Widget的父类

Widget 是一个抽象类,继承自 DiagnosticableTree(这个类表示诊断树,主要用于提供调试信息),因此继承了一些用于调试和诊断的方法和属性。

4.2 Widget的子类

4.2.1 StatefulWidget 和 StatelessWidget

Widget 本身没有可变状态,它的所有字段都必须是 final。如果需要关联可变状态,应该使用 StatefulWidget,后者在被实例化为元素并添加到树中时会创建一个State对象。Widget的子类可以是 StatelessWidget(始终以相同的方式构建)或 StatefulWidget(可以在其生命周期内多次构建)。

在这里插入图片描述
StatefulWidget: 这是一个带有可变状态的 Widget 类别。它由两部分组成:一个是不可变的描述部分(Widget),另一个是可变的状态部分(State)。StatefulWidget 实例在构建过程中可以改变其状态,当状态发生变化时,相关的 State 对象会被重新构建以更新界面。适用于有变化状态的部分,比如用户输入、数据加载等。
StatelessWidget: 这是一个不可变的 Widget 类别,其描述和外观在整个生命周期内保持不变。StatelessWidget 实例在构建时不会持有可变状态,因此适用于不需要变化的 UI 部分,如图标、文本等。

Widget可以被多次包含在树中,每次都会被实例化为元素。如果某个 widget 在树中出现多次,它将被多次实例化。

4.2.2 RenderObjectWidget

RenderObjectWidget是Flutter渲染引擎的一部分,表示屏幕上的可见对象,它又有LeafRenderObjectWidgetSingleChildRenderObjectWidgetMultiChildRenderObjectWidget这三个子类。
在这里插入图片描述

  • LeafRenderObjectWidget: 这是一种将 RenderObject 无需管理子元素的 Widget 类别。它通常用于将自定义的绘制逻辑封装为 Widget,然后通过构建 RenderObject 进行绘制。
  • SingleChildRenderObjectWidget: 这是一种管理单个子元素的 RenderObjectWidget 类别。它会创建一个单一的子元素,并将其作为子节点传递给 RenderObject 进行渲染。
  • MultiChildRenderObjectWidget: 这是一种管理多个子元素的 RenderObjectWidget 类别。它会创建多个子元素,并将它们作为子节点传递给 RenderObject 进行渲染。比如 Stack、Column 和 Row 等都是 MultiChildRenderObjectWidget 的子类。

4.2.3 ProxyWidget

ProxyWidget提供一种方式来包装 Widgets,以实现特定的功能。它是一个具有子WidgetWidget,而非新的Widget

在这里插入图片描述

  • ParentDataWidget: 这是一个用于修改子 Widget 布局约束的 ProxyWidget 子类。它在渲染树中修改子元素的布局信息,例如 Positioned 和 Align 等都是 ParentDataWidget 的子类,用于指定子元素的位置和对齐方式。
  • InheritedWidget: 这是一种特殊类型的 ProxyWidget,它允许在 Widget 树中向下传递共享的数据,而不需要显式地传递。当 InheritedWidget 更新时,其子孙节点会自动重新构建。适用于需要在多个部分之间共享数据的情况,如主题、语言等。

5. Widget树

Widget树的概念

在Flutter中,Widget 树是指由各种不同类型的Widget构成的层次结构。每个Widget描述了用户界面的一部分,可以是一个简单的元素,也可以是一个复杂的组合。这些Widget通过嵌套关系形成了一个树状结构,被称为Widget树。这种嵌套关系定义了界面中各个部分的排列和组织方式。

Widget树是构建用户界面的基本模型。当 Flutter 应用程序运行时,它会从一个 根Widget 开始,然后逐级构建出整个界面。每个Widget都有一个与之相关联的Element,负责管理底层的渲染树。渲染树最终会被转化为可视的UI元素,显示在屏幕上。

Widget树的特点

嵌套关系Widget 树的节点由各种不同类型的 Widget 组成,这些 Widget 可以嵌套在彼此内部,形成层次结构。

不可变性Widget 本身是 不可变 的,一旦创建就不能再进行修改。如果需要更新界面,通常是通过创建新的Widget来替换旧的Widget。

构建方式: 构建 Widget 树通常是通过构建方法来完成的。在构建方法中,你可以创建和组合不同的 Widget,从而构建出整个界面。

热重载: Flutter支持热重载,这意味着你可以在不重新启动应用程序的情况下快速修改和查看界面的变化。在热重载期间,Flutter会比较新旧Widget树的差异,并尽可能地保留应用程序的状态。

响应式: Flutter的界面是响应式的,意味着当数据发生变化时,相关的Widget会自动更新。这是通过在StatefulWidget 中管理可变状态来实现的。

F. 附录

F.1 Widget类源码

abstract class Widget extends DiagnosticableTree {/// Initializes [key] for subclasses.const Widget({ this.key });/// Controls how one widget replaces another widget in the tree.////// If the [runtimeType] and [key] properties of the two widgets are/// [operator==], respectively, then the new widget replaces the old widget by/// updating the underlying element (i.e., by calling [Element.update] with the/// new widget). Otherwise, the old element is removed from the tree, the new/// widget is inflated into an element, and the new element is inserted into the/// tree.////// In addition, using a [GlobalKey] as the widget's [key] allows the element/// to be moved around the tree (changing parent) without losing state. When a/// new widget is found (its key and type do not match a previous widget in/// the same location), but there was a widget with that same global key/// elsewhere in the tree in the previous frame, then that widget's element is/// moved to the new location.////// Generally, a widget that is the only child of another widget does not need/// an explicit key.////// See also://////  * The discussions at [Key] and [GlobalKey].final Key? key;/// Inflates this configuration to a concrete instance.////// A given widget can be included in the tree zero or more times. In particular/// a given widget can be placed in the tree multiple times. Each time a widget/// is placed in the tree, it is inflated into an [Element], which means a/// widget that is incorporated into the tree multiple times will be inflated/// multiple times.Element createElement();/// A short, textual description of this widget.String toStringShort() {final String type = objectRuntimeType(this, 'Widget');return key == null ? type : '$type-$key';}void debugFillProperties(DiagnosticPropertiesBuilder properties) {super.debugFillProperties(properties);properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;}bool operator ==(Object other) => super == other;int get hashCode => super.hashCode;/// Whether the `newWidget` can be used to update an [Element] that currently/// has the `oldWidget` as its configuration.////// An element that uses a given widget as its configuration can be updated to/// use another widget as its configuration if, and only if, the two widgets/// have [runtimeType] and [key] properties that are [operator==].////// If the widgets have no key (their key is null), then they are considered a/// match if they have the same type, even if their children are completely/// different.static bool canUpdate(Widget oldWidget, Widget newWidget) {return oldWidget.runtimeType == newWidget.runtimeType&& oldWidget.key == newWidget.key;}// Return a numeric encoding of the specific `Widget` concrete subtype.// This is used in `Element.updateChild` to determine if a hot reload modified the// superclass of a mounted element's configuration. The encoding of each `Widget`// must match the corresponding `Element` encoding in `Element._debugConcreteSubtype`.static int _debugConcreteSubtype(Widget widget) {return widget is StatefulWidget ? 1 :widget is StatelessWidget ? 2 :0;}
}

相关文章:

Flutter源码分析笔记:Widget类源码分析

Flutter源码分析笔记 Widget类源码分析 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at: https://jclee95.blog.csdn.netEmail: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/article/details/132259681 【介绍】&#x…...

PyTorch 微调终极指南:第 2 部分 — 提高模型准确性

一、说明 如今,在训练深度学习模型时,通过在自己的数据上微调预训练模型来迁移学习已成为首选方法。通过微调这些模型,我们可以利用他们的专业知识并使其适应我们的特定任务,从而节省宝贵的时间和计算资源。本文分为四个部分&…...

MySQL数据库----------安装anaconda---------python与数据库的链接

作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 ​🎂 作者介绍: 🎂🎂 🎂 🎉🎉&#x1f389…...

nuxt页面布局

nuxt页面默认布局文件在layouts目录下default.vue&#xff0c;可将页面的头部和脚部提取出来&#xff0c;形成布局页&#xff0c;将主内容区域的内容替换成<nuxt />。附default.vue代码&#xff1a; <template><div class"app-container"><div…...

mac编译ffmpeg

- code&#xff1a; git clone https://git.ffmpeg.org/gitweb/ffmpeg.git - 编译安装 https://trac.ffmpeg.org/wiki/CompilationGuide - 使用homebrew安装dependency brew install automake fdk-aac git lame libass libtool libvorbis libvpx \ opus sdl shtool texi2ht…...

如何让你的图片服务也有类似OSS的图片处理功能

原文链接 前言 有自己机房的公司一般都有一套存储系统用于存储公司的图片、视频、音频、文件等数据&#xff0c;常见的存储系统有以NAS、FASTDFS为代表的传统文件存储&#xff0c;和以Minio为代表的对象存储系统&#xff0c;随着云服务的兴起很多公司逐渐将数据迁移到以阿里云…...

Oracle PL/SQL 类型(Type):索引表、嵌套表、变长数组、pipelined 管道

1、Oracle 新建员工表和部门表.sql。 集合类型 1、Oracle 集合是相同类型元素的组合&#xff0c;在集合中&#xff0c;使用唯一的下标来标识其中的每个元素&#xff0c;与 Java 的 List 很像。 2、常用集合方式&#xff1a; 类型语法下标元素个数初始值.extend能否存在DB中…...

Web 服务器 -【Tomcat】的简单学习

Tomcat1 简介1.1 什么是Web服务器 2 基本使用2.1 下载2.2 安装2.3 卸载2.4 启动2.5 关闭2.6 配置2.7 部署 3 Maven创建Web项目3.1 Web项目结构3.2 创建Maven Web项目 4 IDEA使用Tomcat4.1 集成本地Tomcat4.2 Tomcat Maven插件 Tomcat 1 简介 1.1 什么是Web服务器 Web服务器是…...

armbian使用1panel快速部署部署springBoot项目后端

文章目录 前言环境准备实现步骤第一步&#xff1a;Armbian安装1panel第二步&#xff1a;安装数据库第三步&#xff1a;查看数据库容器重要信息【重要】查看容器所在的网络查看容器连接地址 第四步&#xff1a;项目配置和打包第五步:构建项目镜像 前言 这里只是简单记录部署spr…...

Streamlit 讲解专栏(八):图像、音频与视频魔法

文章目录 1 前言2 st.image&#xff1a;嵌入图像内容2.1 图像展示与描述2.2 调整图像尺寸2.3 使用本地文件或URL 3 st.audio&#xff1a;嵌入音频内容3.1 播放音频文件3.2 生成音频数据播放 4 st.video&#xff1a;嵌入视频内容4.1 播放视频文件4.2 嵌入在线视频 5 结语&#x…...

python使用装饰器记录方法耗时

思路 python使用修饰器记录方法耗时&#xff0c;目的是每当方法执行完后&#xff0c;可以记录该方法耗时&#xff0c;而不需要在每个方法的执行前后&#xff0c;去创建一个临时变量&#xff0c;来记录耗时。 方式一&#xff08;不推荐&#xff09;&#xff1a; 在每个方法的…...

JavaWeb课程学习--Day01

HTML 建立css文件&#xff1a; css使用方式&#xff1a; <span>...</span>无语意包裹标签 css中的三种选择器&#xff1a; 注意&#xff1a;播放视音频时要留出播放空间 盒子模型&#xff1a; 表格标签&#xff1a; 以上表格&#xff1a; 表单标签&#xff1a; 表…...

Spring Boot单元测试使用MockBean注解向Service注入Mock对象

1. 背景介绍 我们在测试时有一个Service&#xff0c;我们需要测试Service&#xff0c;但Service内部依赖ServiceA、ServiceB&#xff0c;此时我们希望Mock ServiceA&#xff0c;ServiceB 注入真实对象。 class Service {private ServiceA A;private ServiceB B;public int me…...

Java中使用instanceof判断对象类型

记录&#xff1a;470 场景&#xff1a;Java中使用instanceof判断对象类型。例如在解析JSON字符串转换为指定类型时&#xff0c;先判断类型&#xff0c;再定向转换。在List<Object>中遍历Object时&#xff0c;先判断类型&#xff0c;再定向转换。 版本&#xff1a;JDK 1…...

postman测试后端增删改查

目录 一、本文介绍 二、准备工作 &#xff08;一&#xff09;新建测试 &#xff08;二&#xff09;默认url路径查看方法 三、增删改查 &#xff08;一&#xff09;查询全部 &#xff08;二&#xff09;增加数据 &#xff08;三&#xff09;删除数据 &#xff08;四&…...

根据源码,模拟实现 RabbitMQ - 通过 SQLite + MyBatis 设计数据库(2)

目录 一、数据库设计 1.1、数据库选择 1.2、环境配置 1.3、建库建表接口实现 1.4、封装数据库操作 1.5、针对 DataBaseManager 进行单元测试 一、数据库设计 1.1、数据库选择 MySQL 是我们最熟悉的数据库&#xff0c;但是这里我们选择使用 SQLite&#xff0c;原因如下&am…...

1、基于 CentOS 7 构建 LVS-DR 群集。 2、配置nginx负载均衡

一、基于CentOS7和、构建LVS-DR群集 准备四台虚拟机 ip作用192.168.27.150客户端192.168.27.151LVS192.168.27.152RS192.168.27.152RS 关闭防火墙 [rootlocalhost ~]# systemctl stop firewalld安装ifconfig yum install net-tools.x86_64 -y1、DS上 1.1 配置LVS虚拟IP …...

android 如何分析应用的内存(十七)——使用MAT查看Android堆

android 如何分析应用的内存&#xff08;十七&#xff09;——使用MAT查看Android堆 前一篇文章&#xff0c;介绍了使用Android profiler中的memory profiler来查看Android的堆情况。 如Android 堆中有哪些对象&#xff0c;这些对象的引用情况是什么样子的。 可是我们依然面临…...

Spring 使用注解储存对象

文章目录 前言存储 Bean 对象五大注解五大注解示例配置包扫描路径读取bean的示例 方法注解 Bean Bean 命名规则重命名 Bean 前言 通过在 spring-config 中添加bean的注册内容&#xff0c;我们已经可以实现基本的Spring读取和存储对象的操作了&#xff0c;但在操作中我们发现读…...

一、初始 Spring MVC

文章目录 一、回顾 MVC 模式二、初始 Spring MVC2.1 Spring MVC 核心组件2.1.1 前端控制器&#xff08;DispatcherServlet&#xff09;2.1.2 处理器映射器&#xff08;HandlerMapping&#xff09;2.1.3 处理器适配器&#xff08;HandlerAdapter&#xff09;2.1.3 后端控制器&am…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?

Redis 的发布订阅&#xff08;Pub/Sub&#xff09;模式与专业的 MQ&#xff08;Message Queue&#xff09;如 Kafka、RabbitMQ 进行比较&#xff0c;核心的权衡点在于&#xff1a;简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...

Redis:现代应用开发的高效内存数据存储利器

一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发&#xff0c;其初衷是为了满足他自己的一个项目需求&#xff0c;即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源&#xff0c;Redis凭借其简单易用、…...