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;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/35530.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt下载安装及配置教程

进入qt中文网站:https://www.qt.io/zh-cn/ 下载开源版 往下滑,下载Qt在线安装程序 它已经检测出我的是windows系统,直接点击download就好。如果是其它的系统,需要找到对应自己系统的安装包。 然后跟网速有关,等…

登录验证码实现

Hutool代码改造 Hutool 有参考文档&#xff1b;很多工具类&#xff1b;把一些功能都封装好&#xff1b;都不用你自己去写&#xff1b;直接调用它的工具类 它这里会详细告诉你引入方式Hutool <dependency><groupId>cn.hutool</groupId><artifactId>hu…

STM32F429IGT6使用CubeMX配置SPI通信(W25Q256芯片)

1、硬件电路 需要系统性的看一下W25Q256芯片手册 2、设置RCC&#xff0c;选择高速外部时钟HSE,时钟设置为180MHz 3、配置SPI 4、生成工程配置 5、相关代码 #define sFLASH_ID 0XEF4019 // W25Q256#define SPI_FLASH_PageSize 256 #define SPI_FLASH_PerWritePageSize 256#def…

《雷达像智能识别对抗研究进展》阅读记录

&#xff08;1&#xff09;引言 ​ 神经网络通常存在鲁棒性缺陷&#xff0c;易受到对抗攻击的威胁。攻击者可以隐蔽的诱导雷达智能目标识别做出错误预测&#xff0c;如&#xff1a; ​ a图是自行车&#xff0c;加上对抗扰动后神经网络就会将其识别为挖掘机。 &#xff08;2&a…

【Quarkus技术系列】打造基于Quarkus的云原生微服务框架实践(1)

前提介绍 本系列文章主要讲解如何基于Quarkus技术搭建和开发"专为Kubernetes而优化的Java微服务框架"的入门和实践&#xff0c;你将会学习到如何搭建Quarkus微服务脚环境及脚手架&#xff0c;开发Quarkus的端点服务&#xff0c;系统和应用层级的配置介绍与Quarkus的…

单芯片3路CC管理的VR转接器解决方案

VR眼镜即VR头显&#xff0c;也称虚拟现实头戴式显示设备&#xff0c;随着元宇宙概念的传播&#xff0c;VR眼镜的热度一直只增不减&#xff0c;但是头戴设备的续航一直被人诟病&#xff0c;如果增大电池就会让头显变得笨重影响体验&#xff0c;所以目前最佳的解决方案还是使用VR…

[HDLBits] Exams/m2014 q3

Consider the function f shown in the Karnaugh map below. Implement this function. d is dont-care, which means you may choose to output whatever value is convenient. //empty

学习左耳听风栏目90天——第六天 6/90(学习左耳朵耗子的工匠精神,对技术的热爱)【如何拥有技术领导力】

学习左耳听风栏目90天——第六天 6/90&#xff08;学习左耳朵耗子的工匠精神&#xff0c;对技术的热爱&#xff09;【如何拥有技术领导力】

虚拟机内搭建CTFd平台搭建及CTF题库部署,局域网内机器可以访问

一、虚拟机环境搭建 1、安装docker、git、docker-compose ubuntu&#xff1a; sudo apt-get update #更新系统 sudo apt-get -y install docker.io #安装docker sudo apt-get -y install git #安装git sudo apt-get -y install python3-pip #安装pip3 sudo pip install dock…

Kubeadm安装K8s集群

一、硬件环境 准备3台Linux服务器&#xff0c;此处用Vmware虚拟机。 主机名CPU内存k8smaster2核4Gk8snode12核4Gk8snode22核4G 二、系统前置准备 配置三台主机的hosts文件 cat << EOF > /etc/hosts 192.168.240.130 k8smaster 192.168.240.132 k8snode1 192.168.…

腾讯出品Pag动画框架在Android端的使用-初级

Pag动画框架作为一个第三方框架&#xff0c;它的优缺点与Lottie是相似&#xff0c;此处不过多赘述。如果你们的项目中打算用了&#xff0c;肯定是经过了一定的调研的。Pag动画框架分几个版本&#xff0c;有免费的有收费的。我们目前用的社区免费版&#xff0c;只用来展示Pag动画…

项目实战 — 消息队列(8){网络通信设计②}

目录 一、客户端设计 &#x1f345; 1、设计三个核心类 &#x1f345; 2、完善Connection类 &#x1f384; 读取请求和响应、创建channel &#x1f384; 添加扫描线程 &#x1f384; 处理不同的响应 &#x1f384; 关闭连接 &#x1f345; 3、完善Channel类 &#x1f384; 编…

广州华锐互动:VR3D课程在线教育平台为职业院校提供沉浸式的虚拟现实学习体验

随着科技的飞速发展&#xff0c;虚拟现实(VR)和增强现实(AR)技术已经逐渐渗透到我们生活的各个领域。其中&#xff0c;VR3D课程在线教育平台作为一种新兴的教育方式&#xff0c;正在逐渐改变我们的学习方式和体验。本文将详细介绍VR3D课程在线教育平台的应用前景及特点。 VR3D课…

【设计模式】工厂模式

工厂模式 工厂模式&#xff08;Factory Pattern&#xff09;是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 工厂模式提供了一种将对象的实例化过程封装在工厂类中的方式。通过使用工厂模式&#xff0c;可以…

uniapp软键盘谈起遮住输入框和头部被顶起的问题解决

推荐&#xff1a; pages.json中配置如下可解决头部被顶起和表单被遮住的问题。 { "path": "pages/debug/protocol/tagWord", "style": { "app-plus": { "soft…

Java实战:高效提取PDF文件指定坐标的文本内容

前言 临时接到一个紧急需要处理的事项。业务侧一个同事有几千个PDF文件需要整理&#xff1a;需要从文件中的指定位置获取对应的编号和地址。 要的急&#xff0c;工作量大。所以就问到技术部有没有好的解决方案。 问技术的话就只能写个demo跑下了。 解决办法 1. 研究下PDF文档…

案例15 Spring Boot入门案例

1. 选择Spring Initializr快速构建项目 ​ 2. 设置项目信息 ​ 3. 选择依赖 ​ 4. 设置项目名称 ​ 5. 项目结构 ​ 6. 项目依赖 自动配置了Spring MVC、内置了Tomcat、配置了Logback(日志)、配置了JSON。 ​ 7. 创建HelloController类 com.wfit.boot.hello目录下创建HelloCo…

开发过程中遇到的问题以及解决方法

巩固基础&#xff0c;砥砺前行 。 只有不断重复&#xff0c;才能做到超越自己。 能坚持把简单的事情做到极致&#xff0c;也是不容易的。 开发过程中遇到的问题以及解决方法 简单易用的git命令 git命令&#xff1a; 查看有几个分支&#xff1a;git branch -a 切换分支&#…

Azure创建第一个虚拟机

首先&#xff0c;登录到 Azure 门户 (https://portal.azure.com/)。在 Azure 门户右上角&#xff0c;点击“虚拟机”按钮&#xff0c;并点击创建&#xff0c;创建Azure虚拟机。 在虚拟机创建页面中&#xff0c;选择所需的基本配置&#xff0c;包括虚拟机名称、操作系统类型和版…

【JVM】JVM 调优的参数都有哪些?

文章目录 1. 设置堆空间大小2. 虚拟机栈的设置3. 年轻代中Eden区和两个Survivor区的大小比例4. 年轻代晋升老年代阈值5. 设置垃圾回收收集器 1. 设置堆空间大小 设置堆的初始大小和最大大小&#xff0c;为了防止垃圾收集器在初始大小、最大大小之间收缩堆而产生额外的时间&…