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

Flutter 的 Widget Key 提议大调整?深入聊一聊 Key 的作用

Flutter 的 Widget Key 提议大调整?深入聊一聊 Key 的作用

在 Flutter 里,Key 对象存在的目的主要是区分和维持 Widget 的状态,它是控件在渲染树里的「复用」标识之一,这一点在之前的《深入 Flutter 和 Compose 在 UI 渲染刷新时 Diff 实现对比》 聊到过,可以说 Key 的存在关乎了 Flutter 的性能,因为它的作用就是提高 Element Tree 的复用效率,例如减少匹配阶段所需的 Widget 比较次数。

另外通过 Key 还可以提高如 AnimatedListListView 里重新排序时对应 Item widget 的效率,通过将 Key 分配给 Item ,Flutter 可以更有效地识别何时添加、删除或更新列表并执行动画,在这个时候, Key 可以确保每个 Item 即使在对列表进行排序时也保持其状态。

大多数情况下,无状态的 Widget 是不需要 Key,而默认情况下,我们在不主动配置 Key 的时候,它会是 null :

也就是在没有 Key 的情况下,framewok 一般只判断 runtimeType 去决定是否「复用」,举个很老的官方例子,如下图片代码里的 StatelessColorfulTile 所示,它是一个无状态的 StatelessWidget ,显示了一个随机颜色的 200x200 大小的正方形,通过点击右下角按键,每次调整两个方块的位置,可以看到方块可以正常切换:

因为此时没有 Key ,在 Element Tree 只需要判断 runtimeType ,明显此时 Element 符合复用条件,而代码里又是直接使用 StatelessColorfulTile 的 Widget 实例对象进行 tiles.insert(1, tiles.removeAt(0)) ,所以在 Widget 切换位置之后,Element 和 RenderObject 只需要 update 一下新位置 Widget 实例的颜色即可:

但是,如果我们修改为 StatefulWidget ,此时我们再点击右下角按键,可以看到此时颜色方块不会切换了:

因为此时颜色 color 被保存在 State 下,在 Widget 切换位置之后,因为 runtimeType 符合条件,所以 Element 复用,但是颜色被保存在 State 下,State 又是保存在 Element 里,从而导致颜色并没有按照需求被更新切换:

但是,如果这时候我们给两个 StatefulWidget 添加上 Key ,就可以看到它们可以被切换了,因为 canUpdate 判断条件会增加 Key 判断:

也就是,在有了 Key 之后,新 Widget 的 key 就可以在老 Element 列表里进行匹配,从而更新 Element 的位置并刷新 RenderObject,两个 Element 在状态保留的情况下,被 Tree 里调换了位置进行更新,从而实现了切换的效果:

所以,从这个简单的例子,可以直观看到 Key 在有状态的情况下能够发挥的作用,当然,目前在 Flutter 里的 Key 类型很丰富,但是大致可以简单分为两类: Local Keys 和 Global Keys

顾名思义就是它的作用范围,举个例子,如果我们给 StatelessColorfulTile 增加了一个 Padding ,再点击切换按键,可以看到此时点击后 Element 一直被重构:

因为此时在 Row 里面,此时处于“一级”位置 children 是两个 Padding,而 Padding 没有 Key,所以它在 runtimeType 条件的情况下,是直接被复用:

而对于 StatelessColorfulTile 而言,它处于 Padding 之下,Padding 不是一个 Multi Child 的控件,所以在 canUpdate 为 false 的时候,Flutter 内部会认为它需要被重新创建:

从这里我们就可以很直观体验到 Local Keys 这个概念:它只作用于标识同一父 Widget 中的 Widget,不能用于识别其父 Widget 之外的 Widget

同时,我们也可以是直观感受到:Multi Child 和 Single Child 的 Element 对于 Diff 更新时的策略差别

另外,我们还可以感受到 Widget 作为「配置文件」的存在,要知道,代码里我们操作的一直都是 tiles.insert(1, tiles.removeAt(0)); ,也就是 Widget 的实例化都的对象,虽然 Widget 实例没变,但是 Element 层面还是会根据情况「重新创建」对应的 Element ,由于颜色是在 State 里,所以也就会跟着 Element 重新随机变化。

最后如下图所示,对于 Local Keys 来说,左侧这样的写法是可以的,而右侧这样的写法是违规的:

所以,在 Widget 的 Key 注释里也有这样一句描述:通常情况下,作为另一个 widget 的唯一子项的 widget 不需要显式 Key

GlobalKey

那么,除开 Local Keys ,Flutter 里还有一个特殊的 GlobalKey,允许开发者在 Widget 树里去「唯一」标识 Widget,并提供 BuildContext(Element)/State 的全局访问:

这里的「唯一」更多体现在当前这一帧里的「唯一」。

比如前面的例子,我们只需要把对应的 Local Keys 换成 GlobalKey ,就可以看到,虽然 Key 所在的 StatelessColorfulTile 还是在 Padding 下的“二级” child ,但是现在点击切换时,它不会被「重新创建」导致颜色发生变化:

这是因为,虽然在 updateChild 的时候,逻辑依然会走到 inflateWidget 去创建 Element ,但是由于是 GlobalKey,所以会从全局保存的 Map 里获取到当前 GlobalKey 绑定的 Element ,从而 retake 复用:

从这里可以看出来, 如果 Element 在同一帧中移动或者删除,并且它具有 GlobalKey,那么它仍然可能被重新激活使用

所以 GlobalKey 不仅可以作为 Key 区分 Widget ,帧内还可以在 BuildOwner 里“全局”保持住 Element 、State 和关联 RenderObject 的“状态”,即使它出现移动或者删除。

同时,通过 GlobalKey ,我们也可以访问对应的 BuildContext 和 State 数据,甚至是直接给 MaterialApp 添加 GlobalKey 来操作导航:

那么 GlobalKey 这么好用,它又存在什么问题呢?其实在注释里已经有对应说明:

GlobalKey 在使用的过程中可能会出现需要重新设置 [Element] 父级的情况,而这个操作会触发对关联的 [State] 及其所有后代 [State.deactivate] 的调用,还会强制重建所有依赖于 [InheritedWidget] 的控件。

具体就体现在这下面两段代码:

  • _retakeInactiveElement 内可能会触发所有关联 State 的 deactivate
  • _activateWithParent 会触发 Element 的 activate ,从而通过 didChangeDependencies 强制重建所有依赖于 [InheritedWidget] 的控件

当然,GlobalKey 也有一些注意事项,例如:

使用 GlobalKey 不能频繁创建,通常应该是让它和 State 对象拥有类似的“生命长度”,因为新的 GlobalKey 会丢弃与旧 Key 关联的子树的状态,并为新键创建一个新的子树,频繁创建会导致状态丢失和性能损耗。

变更提议

前面我们主要介绍了 Key 的作用和分类下的职能,而本次 PR 提议的调整,则是在于打算简化 Local Keys 相关的实现上,可以看到在以往的实现里,关于 LocalKey 的实现有好几种类型,但是其中一些职能其实「相对重复」:

在 #159225 的 PR 里,将打算把 Key 对象切换到 Object ,从而“消灭”过往这些 Local Keys 的“重叠”,让 Key API 更加灵活:

另外,除了灵活和简化之外,针对目前存在的 Local Keys ,它和 Dart 的 Extension Types 不同,比如使用 ValueKey() 多多少少会有一点点点点点点 wrapper 成本,而如果这个提议合并后,大概会是如下所示的情况,或多或少对性能还是有那么一点点点点点点帮助:

事实上对于 LocalKey ,大多数人应该都只会使用到 ValueKey 居多。

当然,这个 PR 整体来说还是属于底层大调整,而目前看起来提议应该是暂时搁置了,不过就算推进落地,相信对于大多数上层 Flutter 开发者来说,应该也不会有明显的感知,毕竟大多数时候 Flutter 开发者对 Key 并不敏感:

所以,你是喜欢现在的 Local Keys 分类还是提议里的 Object ?

参考链接:

  • https://github.com/flutter/flutter/pull/159225
  • https://api.flutter.dev/flutter/foundation/Key-class.html

相关文章:

Flutter 的 Widget Key 提议大调整?深入聊一聊 Key 的作用

Flutter 的 Widget Key 提议大调整?深入聊一聊 Key 的作用 在 Flutter 里,Key 对象存在的目的主要是区分和维持 Widget 的状态,它是控件在渲染树里的「复用」标识之一,这一点在之前的《深入 Flutter 和 Compose 在 UI 渲染刷新时…...

只需三步!5分钟本地部署deep seek——MAC环境

MAC本地部署deep seek 第一步:下载Ollama第二步:下载deepseek-r1模型第三步:安装谷歌浏览器插件 第一步:下载Ollama 打开此网址:https://ollama.com/,点击下载即可,如果网络比较慢可使用文末百度网盘链接 注:Ollama是…...

网络工程师 (31)VLAN

前言 VLAN(Virtual Local Area Network)即虚拟局域网,是一种将物理局域网划分成多个逻辑上独立的虚拟网络的技术。 一、定义与特点 定义:VLAN是对连接到的第二层交换机端口的网络用户的逻辑分段,不受网络用户的物理位置…...

浏览器网络请求全流程深度解析

一、核心流程概述 现代浏览器的网络请求过程是一个分层协作的精密系统,涉及应用层协议、传输层协议、操作系统内核及网络基础设施的协同工作。整个过程可抽象为以下关键阶段: 请求构建与初始化DNS解析与寻址TCP连接建立HTTP协议交互响应处理与资源解析…...

React历代主要更新

一、React 16之前更新 React Fiber是16版本之后的一种更新机制,使用链表取代了树,是一种fiber数据结构,其有三个指针,分别指向了父节点、子节点、兄弟节点,当中断的时候会记录下当前的节点,然后继续更新&a…...

【数据结构】(8) 二叉树

一、树形结构 1、什么是树形结构 根节点没有前驱,其它节点只有一个前驱(双亲/父结点)。所有节点可以有 0 ~ 多个后继,即分支(孩子结点)。每个结点作为子树的根节点,这些子树互不相交。 2、关于…...

navicat导出表结构到Excel 带字段备注

navicat导出表结构到Excel 带字段备注 SELECTCOLUMN_NAME AS 字段名称,COLUMN_TYPE AS 字段类型, IF( IS_NULLABLE NO, 否, 是 ) AS 是否必填,COLUMN_COMMENT AS 注释 FROMINFORMATION_SCHEMA.COLUMNS WHERE -- 数据库名table_schema vmscenter -- 表名AND table_name y…...

使用pocketpal-ai在手机上搭建本地AI聊天环境

1、下载安装pocketpal-ai 安装github的release APK 2、安装大模型 搜索并下载模型,没找到deepseek官方的,因为海外的开发者上传了一堆乱七八糟的deepseek qwen模型,导致根本找不到官方上传的……deepseek一开源他们觉得自己又行了。 点击之…...

程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<10>

大家好啊,我是小象٩(๑ω๑)۶ 我的博客:Xiao Xiangζั͡ޓއއ 很高兴见到大家,希望能够和大家一起交流学习,共同进步。 今天我们继续来复习指针… 目录 一、看一段代码二、 一维数组传参的本质三、冒泡排序3.1 基本思想四、二…...

FPGA简介|结构、组成和应用

Field Programmable Gate Arrays(FPGA,现场可编程逻辑门阵列),是在PAL、GAL、CPLD等可编程器件的基础上进一步发展的产物, 是作为专用集成电路(ASIC)领域中的一种半定制电路而出现的&#xff0c…...

[c语言日寄]在不完全递增序中查找特定要素

【作者主页】siy2333 【专栏介绍】⌈c语言日寄⌋:这是一个专注于C语言刷题的专栏,精选题目,搭配详细题解、拓展算法。从基础语法到复杂算法,题目涉及的知识点全面覆盖,助力你系统提升。无论你是初学者,还是…...

Golang的多团队协作编程模式与实践经验

Golang的多团队协作编程模式与实践经验 一、多团队协作编程模式概述 在软件开发领域,多团队协作编程是一种常见的工作模式。特别是对于大型项目来说,不同团队间需要协同合作,共同完成复杂的任务。Golang作为一种高效、并发性强的编程语言&…...

cv2.Sobel

1. Sobel 算子简介 Sobel 算子是一种 边缘检测算子,通过对图像做梯度计算,可以突出边缘。 Sobel X 方向卷积核: 用于计算 水平方向(x 方向) 的梯度。 2. 输入图像示例 假设我们有一个 55 的灰度图像,像素…...

Windows软件自动化利器:pywinauto python

Pywinauto WindowsAPP UI自动化 Windows软件自动化利器:pywinauto python...

关于 IoT DC3 中驱动(Driver)的理解

在开源IoT DC3物联网系统中,驱动(Driver)扮演着至关重要的角色,它充当了软件系统与物理设备之间的桥梁。驱动的主要功能是依据特定的通信协议连接到设备,并根据设备模板中配置的位号信息进行数据采集和指令控制。不同的…...

LogicFlow自定义节点:矩形、HTML(vue3)

效果: LogicFlow 内部是基于MVVM模式进行开发的,分别使用preact和mobx来处理 view 和 model,所以当我们自定义节点的时候,需要为这个节点定义view和model。 参考官方文档:节点 | LogicFlow 1、自定义矩形节点 custo…...

多模态本地部署ConVideoX-5B模型文生视频

文章目录 一、多模态概念1.多模态学习2. 人机交互3. 健康医疗4. 内容创作和娱乐 二、模型介绍三、环境安装1. 安装工具包2. 模型下载 四、运行代码五、代码解析六、效果生成七. 总结1. 模型介绍2. 部署环境3. 部署步骤4. 生成视频5. 应用场景 一、多模态概念 多模态&#xff0…...

html 点击弹出视频弹窗

一、效果: 点击视频按钮后,弹出弹窗 播放视频 二、代码 <div class="index_change_video" data-video-src="</...

业务干挂数据库,Oracle内存分配不足

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 作者&#xff1a;IT邦德 中国DBA联盟(ACDU)成员&#xff0c;10余年DBA工作经验 Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主&#xff0c;全网粉丝10万 擅长主流Oracle、MySQL、PG、高斯…...

MongoDB 7 分片副本集升级方案详解(下)

#作者&#xff1a;任少近 文章目录 1.4 分片升级1.5 升级shard11.6 升级shard2,shard31.7 升级mongos1.8重新启用负载均衡器1.9 推荐MongoDB Compass来验证数据 2 注意事项&#xff1a; 1.4 分片升级 使用“滚动”升级从 MongoDB 7.0 升级到 8.0&#xff0c;即在其他成员可用…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

【单片机期末】单片机系统设计

主要内容&#xff1a;系统状态机&#xff0c;系统时基&#xff0c;系统需求分析&#xff0c;系统构建&#xff0c;系统状态流图 一、题目要求 二、绘制系统状态流图 题目&#xff1a;根据上述描述绘制系统状态流图&#xff0c;注明状态转移条件及方向。 三、利用定时器产生时…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

Web后端基础(基础知识)

BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 优点&#xff1a;维护方便缺点&#xff1a;体验一般 CS架构&#xff1a;Client/Server&#xff0c;客户端/服务器架构模式。需要单独…...

书籍“之“字形打印矩阵(8)0609

题目 给定一个矩阵matrix&#xff0c;按照"之"字形的方式打印这个矩阵&#xff0c;例如&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为&#xff1a;1&#xff0c;…...