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

透过源码理解Flutter InheritedWidget

 InheritedWidget的核心是保存值和保存使用这个值的widget,通过对比值的变化,来决定是否要通知那些使用了这个值的widget更新自身。

1 updateShouldNotify和notifyClients

InheritedWidget通过updateShouldNotify函数控制依赖其的子组件是否在InheritedWidget变化时会被重建:如果updateShouldNotify返回true,InheritedWidget变化时子组件的build会被调用,反之则不会。

InheritedElement中的updated方法:

@override
void updated(InheritedWidget oldWidget) {if (widget.updateShouldNotify(oldWidget))super.updated(oldWidget);
}

InheritedElement继承于ProxyElement,而ProxyElement的updated实现为:

@protectedvoid updated(covariant ProxyWidget oldWidget) {notifyClients(oldWidget);}

而InheritedElement的notifyClients实现是遍历_dependents中依赖自己的widget,然后调用它的didChangeDependencies进行变化通知:

  @overridevoid notifyClients(InheritedWidget oldWidget) {assert(_debugCheckOwnerBuildTargetExists('notifyClients'));for (final Element dependent in _dependents.keys) {assert(() {// check that it really is our descendantElement? ancestor = dependent._parent;while (ancestor != this && ancestor != null) {ancestor = ancestor._parent;}return ancestor == this;}());// check that it really depends on usassert(dependent._dependencies!.contains(this));notifyDependent(oldWidget, dependent);}}@protectedvoid notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {dependent.didChangeDependencies();}
Element的didChangeDependencies源码如下:void didChangeDependencies() {assert(_active); // otherwise markNeedsBuild is a no-opassert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));markNeedsBuild();
}

可以看到,InheritedWidget变化时,如果updateShouldNotify是true,会通过notifyClients调用子组件的didChangeDependencies函数,从而调用markNeedsBuild,将本Element加入_dirtyElements列表中。大家都知道,_dirtyElements中保存的是需要重建的Element,会在下一帧时被rebuild,因此在下一帧子组件会被重建(rebuild)。

2 InheritedWidget的传递

InheritedWidget能用于让用户快速从子组件获取,这是怎么实现的呢?

其实Flutter Framework也是将InheritedWidget一层层传递下来的,只不过由于Framework层自行处理了,因此这个过程对于我们是透明的。我们现在来梳理下InheritedWidget传递的过程。

Element中,有一个map:_inheritedWidgets。保存了所有上级节点中的InheritedElement。其源码如下:

Map<Type, InheritedElement> _inheritedWidgets;

其中,key中Type是InheritedWidget的子类,value是InheritedElement。为什么这里value保存的是InheritedElement而不是InheritedWidget呢?由以前的文章可以知道Element中保存着对应Widget的引用,因此可以通过InheritedElement获取对应的InheritedWidget。而且Widget在上级Widget重建时会被重建,因此保存InheritedElement更合适。

在普通的Element中,_inheritedWidgets会直接复制其父组件中_inheritedWidgets的值,其源码如下:

void _updateInheritance() {assert(_active);_inheritedWidgets = _parent?._inheritedWidgets;
}

而在InheritedElement中,_inheritedWidgets会首先复制其父组件中_inheritedWidgets的值,然后将自己添加进列表,其源码如下:

@override
void _updateInheritance() {assert(_active);final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;if (incomingWidgets != null)_inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);else_inheritedWidgets = HashMap<Type, InheritedElement>();_inheritedWidgets[widget.runtimeType] = this;
}

由此可以看出,InheritedElement就是这样一层层传递下来的。_inheritedWidgets赋值流程如下:

由该流程图可以看出,_inheritedWidgets在Element被加入Element Tree时就已经被赋值,因此其在子组件的build函数中是可以访问得到的。

3 InheritedWidget的获取及注册依赖

我们已经知道了InheritedElement会传递到下级组件中,那怎么获取它呢?Flutter提供了专门获取某个InheritedWidget类型的函数dependOnInheritedWidgetOfExactType.其源码如下:

@override
T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {assert(_debugCheckStateIsActiveForAncestorLookup());final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];if (ancestor != null) {assert(ancestor is InheritedElement);return dependOnInheritedElement(ancestor, aspect: aspect) as T;}_hadUnsatisfiedDependencies = true;return null;
}

由第三行可以看出,此函数会从_inheritedWidgets中寻找对应的InheritedElement,并返回其InheritedWidget。

除了dependOnInheritedWidgetOfExactType,Flutter还提供了另一个专门获取某个InheritedWidget类型的函数:getElementForInheritedWidgetOfExactType。其源码如下:

@override
InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {assert(_debugCheckStateIsActiveForAncestorLookup());final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];return ancestor;
}

对比其与dependOnInheritedWidgetOfExactType源码,可以看到dependOnInheritedWidgetOfExactType多了dependOnInheritedElement函数的调用,该函数用于创建InheritedWidget和调用dependOnInheritedWidgetOfExactType的组件的依赖关系。其有两个步骤:

  • 将依赖的InheritedElement加入本Element的_dependencies列表,该列表中保存了本Element所有依赖的InheritedElement.
  • 将本Element加入依赖的InheritedElement的_dependents map,该列表中保存了所有依赖该InheritedElement的Element。

如果使用的是dependOnInheritedWidgetOfExactType,则当被依赖的InheritedWidget被更新时,依赖的子组件会被rebuild;而使用的是getElementForInheritedWidgetOfExactType时,由于不会建立相应的依赖关系,InheritedWidget被更新时,依赖的子组件不会被rebuild。

4 主动调用dependOnInheritedWidgetOfExactType

 子组件需要通过调用dependOnInheritedWidgetOfExactType来获取InheritedWidget,并且将自己加入该InheritedWidget的依赖中。方便起见,一般会在InheritedWidget子类中实现of方法:

class ShareDataWidget extends InheritedWidget {ShareDataWidget({@required this.data,Widget child}) :super(child: child);final int data; //需要在子树中共享的数据,保存点击次数//定义一个便捷方法,方便子树中的widget获取共享数据static ShareDataWidget of(BuildContext context) {return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();}//该回调决定当data发生变化时,是否通知子树中依赖data的Widget@overridebool updateShouldNotify(ShareDataWidget old) {//如果返回true,则子树中依赖(build函数中有调用)本widget//的子widget的`state.didChangeDependencies`会被调用return old.data != data;}
}class _TestWidget extends StatefulWidget {@override__TestWidgetState createState() => new __TestWidgetState();
}class __TestWidgetState extends State<_TestWidget> {@overrideWidget build(BuildContext context) {print("__TestWidgetState build");//使用InheritedWidget中的共享数据return Text(ShareDataWidget.of(context).data.toString());// return Text("tex");}@overridevoid didChangeDependencies() {super.didChangeDependencies();//父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。//如果build中没有依赖InheritedWidget,则此回调不会被调用。print("Dependencies change");}
}

参考:

Flutter框架分析 -InheritedWidget - 知乎

相关文章:

透过源码理解Flutter InheritedWidget

InheritedWidget的核心是保存值和保存使用这个值的widget&#xff0c;通过对比值的变化&#xff0c;来决定是否要通知那些使用了这个值的widget更新自身。 1 updateShouldNotify和notifyClients InheritedWidget通过updateShouldNotify函数控制依赖其的子组件是否在Inherited…...

天去面试的时候,遇到一个问题。我三个任务,ABC,我怎么让A执行完执行B,B执行完执行C 3个并行线程,如何解决。程池的核心运行原理和参数。

今天去面试的时候&#xff0c;遇到一个问题。我三个任务&#xff0c;ABC&#xff0c;我怎么让A执行完执行B&#xff0c;B执行完执行C 3个并行线程&#xff0c;如何解决。程池的核心运行原理和参数。 1.线程池核心的参数 1.线程核心数- 线程池中始终保持的活动线程数量。 2.最…...

使用finksql方式将mysql数据同步到kafka中,每次只能同步一张表

使用finksql方式将mysql数据同步到kafka中&#xff0c;每次只能同步一张表 package flink;import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.table.api.TableResult; import org.apache.flink.table.api.bridge.java.…...

ios开发 swift5 苹果系统自带的图标 SF Symbols

文章目录 1.官网app的下载和使用2.使用代码 1.官网app的下载和使用 苹果官网网址&#xff1a;SF Symbols 通过上面的网址可以下载dmg, 安装到自己的mac上 貌似下面这样不能展示出动画&#xff0c;还是要使用动画的代码 .bounce.up.byLayer2.使用代码 UIKit UIImage(system…...

Linux内核源码分析 (3)调度器的实现

Linux内核源码分析 (3)调度器的实现 文章目录 Linux内核源码分析 (3)调度器的实现一、概述二、调度器数据结构1、task_struct中与调度有关的的成员2、调度器类3、就绪队列4、调度实体 三、处理优先级1、优先级的内核表示2、计算优先级3、计算负荷权重 四、核心调度器1、周期性调…...

网络安全法+网络安全等级保护

网络安全法 网络安全法21条 网络安全法31条 网络安全等级保护 网络安全等级保护分为几级? 一个中心&#xff0c;三重防护 等级保护2.0网络拓扑图 安全区域边界 安全计算环境 等保安全产品 物理机房安全设计...

持续集成对软件项目管理的作用

l、对项目目标管理的作用 软件项目的目标是开发出可运行的、客户满意的软件系统持续集成有统一的代 码库。要求开发人员定期地、不断地向代码库提交代码。新近提交的代码会经过编 译与测试&#xff0e;与代码库中旧有的代码相整合&#xff0c;形成安全稳定运行的代码库&…...

【Qt QAxObject】使用 QAxObject 高效任意读写 Excel 表

1. 用什么操作 Excel 表 Qt 的官网库中是不包含 Microsoft Excel 的操作库&#xff0c;关于对 Microsoft Excel 的操作库可选的有很多&#xff0c;包含基于 Windows 系统本身的 ActiveX、Qt Xlsx、xlsLib、LibXL、qtXLS、BasicExcel、Number Duck。 库.xls.xlsx读写平台Qt Xls…...

java八股文面试[多线程]——自旋锁

优点&#xff1a; 1. 自旋锁尽可能的减少线程的阻塞&#xff0c;这对于锁的竞争不激烈&#xff0c;且占用锁时间非常短的代码块来说性能能大幅度的提升&#xff0c;因为自旋的消耗会小于线程阻塞挂起再唤醒的操作的消耗 &#xff0c;这些操作会导致线程发生两次上下文切换&…...

分布式系统的多数据库,实现分布式事务回滚(1.7.0 seata整合2.0.4nacos)

正文 1、解决的应用场景是分布式事务&#xff0c;每个服务有独立的数据库。 2、例如&#xff1a;A服务的数据库是A1&#xff0c;B服务的数据库是B2&#xff0c;A服务通过feign接口调用B服务&#xff0c;B涉及提交数据到B2&#xff0c;业务是在B提交数据之后&#xff0c;在A服…...

PDF可以修改内容吗?有什么注意的事项?

PDF是一种跨平台的电子文档格式&#xff0c;可以在各种设备上轻松阅读和共享。许多人喜欢将文档转换为PDF格式以确保格式的一致性和易读性。但是&#xff0c;PDF文件一般被认为是“只读”文件&#xff0c;即无法编辑。那么&#xff0c;PDF文件是否可以修改呢&#xff1f; 答案是…...

自动泊车的自动驾驶控制算法

1. 自动泊车系统 自动泊车系统(AutomatedParkingASSiSt,APA)利用车辆搭载的传感器感知车辆周边环境,扫描满足当前车辆停放的障碍物空间车位或线车位,并通过人机交互(HumanMachine Interface,HMI)获取驾驶员对目标车位的选择或自动确定目标车位,自动规划泊车路径,通过控制器向车…...

Java doc等文件生成PDF、多个PDF合并

之前写过一遍文章是 图片生成PDF。 今天继续来对 doc等文件进行pdf合并以及多个pdf合并为一个pdf。 兄弟们&#xff0c;还是开箱即用。 1、doc生成pdf 依赖 <!-- doc生成pdf --><dependency><groupId>com.aspose</groupId><artifactId>aspose…...

【C++】list类的模拟实现

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;C的学习之路 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录 前言一、list类的模拟实现1.1 list的…...

机械臂+2d相机实现复合机器人定位抓取

硬件参数 机械臂&#xff1a;艾利特 相机&#xff1a;海康相机 2d识别库&#xff1a;lindmod&#xff0c;github可以搜到 光源&#xff1a;磐鑫光源 软件参数 系统&#xff1a;windows / Linux 开发平台&#xff1a;Qt 开发语言&#xff1a;C 开发视觉库&#xff1a;OpenCV …...

网络编程 http 相关基础概念

文章目录 表单是什么http请求是什么http请求的结构和说明关于http方法 GET和POST区别http常见状态码http响应http 请求是无状态的含义html是什么 &#xff08;前端内容&#xff0c;了解即可&#xff09;html 常见标签 &#xff08;前端内容&#xff0c;了解即可&#xff09;关于…...

LatexEasy公式渲染教程

LatexEasy使用简单的URL渲染公式为图片 https://r.latexeasy.com/image.svg?1-sin^2(x) 使用单个HTML图像标签将公式添加到任何现有网站 <img src"https://r.latexeasy.com/image.svg?1-sin^2(x)" />...

十年测试工程师叙述自动化测试学习思路

自动化测试介绍 自动化测试(Automated Testing)&#xff0c;是指把以人为驱动的测试行为转化为机器执行的过程。实际上自动化测试往往通过一些测试工具或框架&#xff0c;编写自动化测试用例&#xff0c;来模拟手工测试过程。比如说&#xff0c;在项目迭代过程中&#xff0c;持…...

SpringAOP详解(下)

proxyFactory代理对象创建方式和代理对象调用方法过程&#xff1a; springaop创建动态代理对象和代理对象调用方法过程&#xff1a; 一、TargetSource的使用 Lazy注解&#xff0c;当加在属性上时&#xff0c;会产生一个代理对象赋值给这个属性&#xff0c;产生代理对象的代码为…...

主流软件漏洞跟踪 Apache RocketMQ NameServer 远程代码执行漏洞(CVE-2023-37582)

主流软件漏洞跟踪 Apache RocketMQ NameServer 远程代码执行漏洞(CVE-2023-37582) 漏洞描述影响版本安全版本如何修复可供参考的资料主流软件漏洞跟踪 Apache RocketMQ NameServer 远程代码执行漏洞(CVE-2023-37582) CVE编号 : CVE-2023-37582 利用情况 : EXP 已公开 …...

用Qwen3-VL-30B做智能助手:上传文档图片,自动提取关键信息

用Qwen3-VL-30B做智能助手&#xff1a;上传文档图片&#xff0c;自动提取关键信息 1. 为什么需要智能文档处理助手 每天我们都会遇到大量需要处理的文档和图片&#xff1a;合同、发票、报告、表格、名片...手动输入这些信息不仅耗时耗力&#xff0c;还容易出错。传统OCR技术虽…...

网络异常排查:快速定位域连接问题

问题描述与初步排查网络位置异常通常表现为计算机无法正确识别当前所在的AD域环境&#xff0c;导致访问域资源受限或登录问题。常见症状包括系统托盘显示“无法访问域”、组策略无法应用、DNS解析失败等。检查计算机是否能够ping通域控制器的主机名和IP地址。使用nslookup命令验…...

Blender 5.0 插件生态实战指南:从建模到渲染的流程效率革命

1. Blender 5.0插件生态的核心价值 如果你用过Blender&#xff0c;一定遇到过这样的场景&#xff1a;建模时反复手动倒角、UV展开时对着乱七八糟的贴图发呆、渲染时发现场景灯光怎么调都不自然。这些问题在Blender 5.0的插件生态中都能找到优雅的解决方案。 我做了10年三维设计…...

服装打版辅助新思路:Nano-Banana软萌拆拆屋结构化拆解应用

服装打版辅助新思路&#xff1a;Nano-Banana软萌拆拆屋结构化拆解应用 1. 引言&#xff1a;当服装设计遇见“拆解魔法” 想象一下&#xff0c;你是一位服装设计师&#xff0c;面对一件构思精巧的连衣裙&#xff0c;如何向打版师清晰地传达它的内部结构&#xff1f;是画一堆复…...

AI 开发实战:技术决策为什么总失忆,决策日志可以怎么做

AI 开发实战&#xff1a;技术决策为什么总失忆&#xff0c;决策日志可以怎么做 一、这个问题为什么值得专门拿出来做&#xff1f; 在 AI 工程落地里&#xff0c;真正拖慢团队的往往不是模型本身&#xff0c;而是流程和协作方式没有跟上。 围绕“技术决策为什么总失忆&#xff0…...

Claude Code 命令行参数实践指南

前言 很多人第一次打开 Claude Code&#xff0c;只会输入 claude&#xff0c;然后开始聊天。这当然可以&#xff0c;但就像开车只会踩油门一样——你根本没用上方向盘和变速箱。 命令行参数&#xff08;CLI Flags&#xff09;就是那些被忽视的"方向盘"。掌握它们&a…...

无GPU方案:星图平台OpenClaw镜像+百川2-13B-4bits的云端沙盒体验

无GPU方案&#xff1a;星图平台OpenClaw镜像百川2-13B-4bits的云端沙盒体验 1. 为什么选择云端沙盒方案 作为一个长期折腾本地AI部署的技术爱好者&#xff0c;我最近遇到了一个典型困境&#xff1a;想体验最新的OpenClaw智能体框架&#xff0c;但手头的MacBook Pro只有集成显…...

别再让DeepSeek-R1的<think>标签刷屏了!手把手教你用API和Python脚本一键隐藏思考过程

高效隐藏DeepSeek-R1思考过程的工程实践 当你在深夜调试一个集成DeepSeek-R1的客服系统时&#xff0c;终端突然被满屏的<think>标签刷爆——这种场景对开发者来说再熟悉不过了。作为一款强调推理过程的大语言模型&#xff0c;DeepSeek-R1默认会在输出中包含详细的思考步骤…...

深度学习项目训练环境多场景落地:自动驾驶小车图像识别项目快速启动

深度学习项目训练环境多场景落地&#xff1a;自动驾驶小车图像识别项目快速启动 你是不是也遇到过这样的问题&#xff1f;想跑一个深度学习项目&#xff0c;光是配环境就花了大半天&#xff0c;各种版本冲突、依赖报错&#xff0c;好不容易装好了&#xff0c;一运行又提示缺这…...

技术竞赛之道:从创新构想到落地执行的实战心法

技术竞赛之道&#xff1a;从创新构想到落地执行的实战心法 【免费下载链接】A-to-Z-Resources-for-Students ✅ Curated list of resources for college students 项目地址: https://gitcode.com/GitHub_Trending/at/A-to-Z-Resources-for-Students 在当今技术驱动的时…...