InheritedWidget介绍

在Flutter进行界面开发时,我们经常会遇到数据传递的问题。由于Flutter采用节点树的方式组织页面,以致于一个普通页面的节点层级会很深。此时,我们如果还是一层层传递数据,当需要获取多层父节点的数据时,会非常麻烦。 因为出现上述问题,Flutter给我我们提供一种InheritedWidgetInheritedWidget能够让节点下的所有子节点,访问该节点下的数据。 关于Scoped ModelBloCProvider就是基于InheritedWidget实现的。

InheritedWidget源码分析

可以看到InheritedWidget的源码非常简单。

/// 抽象类,继承自Proxywidget 继承路径InheritedWidget => ProxyWidget => Widget
abstract class InheritedWidget extends ProxyWidget {/// 构造函数/// 因为InheritedWidget是没有界面的Widget,所有需要传入实际的Widget    const InheritedWidget({ Key key, Widget child }): super(key: key, child: child);/// 重写了超类Widget createElement方法@overrideInheritedElement createElement() => InheritedElement(this);/// 父级或祖先widget中改变(updateShouldNotify返回true)时会被调用。@protectedbool updateShouldNotify(covariant InheritedWidget oldWidget);
}

InheritedWidget示例

import 'package:flutter/material.dart';
import 'package:flutter_code/InheritedWidget/InheritedState.dart';class InheritedCount extends StatefulWidget {@override_InheritedCountState createState() => _InheritedCountState();
}class _InheritedCountState extends State<InheritedCount> {int _count = 0;@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("InheritedDemo"),),floatingActionButton: FloatingActionButton(onPressed: () {setState(() {_count++;});},child: Icon(Icons.add, color: Colors.white,),),body: Center(child: InheritedState(count: _count,child: Column(mainAxisAlignment: MainAxisAlignment.spaceAround,crossAxisAlignment: CrossAxisAlignment.center,children: [WidgetA(),WidgetB()],)),),);}
}class WidgetA extends StatelessWidget {@overrideWidget build(BuildContext context) {return Text("widget text");}
}class WidgetB extends StatelessWidget {@overrideWidget build(BuildContext context) {return Text(InheritedState.of(context)?.count.toString(),style: TextStyle(color: Colors.green,fontSize: 50),  );}
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';class InheritedState extends InheritedWidget {/// 构造方法InheritedState({Key key,@required this.count,@required Widget child}): assert(count != null),super(key:key, child: child);/// 需要共享的数据final int count;/// 获取组件最近当前的InheritedWidgetstatic InheritedState of(BuildContext context) {return context.dependOnInheritedWidgetOfExactType<InheritedState>();}/// 通知依赖该树共享数据的子widget@overridebool updateShouldNotify(covariant InheritedState oldWidget) {return count != oldWidget.count;}}

InheritedWidget源码分析

在上面的计数器示例代码中,WidgetBInheritedWidget发生关联的就是InheritedState.of(context)?.count.toString(),其中最关键的方式是context.dependOnInheritedWidgetOfExactType(),我们查看dependOnInheritedWidgetOfExactType()Element中的源码如下:该代码是在framework.dart 第3960行

Map<Type, InheritedElement> _inheritedWidgets;@overrideT dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {/// 断言,用于在调试状态下检测是否有正在使用(激活)的祖先assert(_debugCheckStateIsActiveForAncestorLookup());/// 获取到_inheritedWidgets数组数据final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];if (ancestor != null) {// 断言,判断当前ancestor是否是InheritedElement类型assert(ancestor is InheritedElement);// 返回并调用更新方法return dependOnInheritedElement(ancestor, aspect: aspect) as T;}_hadUnsatisfiedDependencies = true;return null;}

我们不难看出,每一个Element实例都会持有一个_inheritedWidgets,调用次用该方法时会从改集合对象中取出相关类型的InheritedElement实例,那么在这个方法中我们没有看到设置_inheritedWidgets的方法,我们来查看一下_inheritedWidgets是如何赋值的。

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

我们找到赋值是在_updateInheritance方法中,首先断言当前节点是否激活,然后通过父节点的_inheritedWidgets进行赋值,我们继续来看_updateInheritance什么情况下会调用:

 @mustCallSupervoid mount(Element parent, dynamic newSlot) {......_updateInheritance();......}@mustCallSupervoid activate() {......_updateInheritance();......}

我们可以看到在Element中它在mountactivate函数执行了调用,也就是说element每次挂载和重新时,会调用该方法。那么当该方法执行的时候,element就会从上层中拿到所有的InheritedElement。而InheritedElement他最终继承了Element,并可以看到InheritedElement重写了_updateInheritance方法:

 @overridevoid _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;}

InheritedWidget是如何进行刷新的

前面我们分析到InheritedElement会拿到父类的所有的InheritedElment并向下传递,而InheritedWidget正是通过这种方法才能让下面的子Widget能访问的上层中所有的InheritedWidget,那么它是如何进行刷新的呢?我们在ElementdependOnInheritedWidgetOfExactType方法中调用了dependOnInheritedElement方法,代码如下:

Set<InheritedElement> _dependencies;@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {assert(ancestor != null);_dependencies ??= HashSet<InheritedElement>();_dependencies.add(ancestor);ancestor.updateDependencies(this, aspect);return ancestor.widget;
}@protected
void updateDependencies(Element dependent, Object aspect) {setDependencies(dependent, null);
}@protected
void setDependencies(Element dependent, Object value) {_dependents[dependent] = value;
}

可以看到InheritedElement实例调用自己的updateDependencies方法并将当前的Element实例传递过去

 /// Called during build when the [widget] has changed.////// By default, calls [notifyClients]. Subclasses may override this method to/// avoid calling [notifyClients] unnecessarily (e.g. if the old and new/// widgets are equivalent).@protectedvoid updated(covariant ProxyWidget oldWidget) {notifyClients(oldWidget);}@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();}

由于当InheritedElement更新时,会执行updated方法,然后继续调用notifyClients,遍历所有的element并调用didChangeDependencies方法。

flutter系列InheritedWidget介绍相关推荐

  1. openresty开发系列12--lua介绍及常用数据类型简介

    openresty开发系列12--lua介绍及常用数据类型简介 lua介绍   1993 年在巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de ...

  2. 三维向量变化为角度_物体的三维识别与6D位姿估计:PPF系列论文介绍(四)

    作者:仲夏夜之星 Date:2020-04-14 来源:物体的三维识别与6D位姿估计:PPF系列论文介绍(四) 文章"3D Pose Estimation of Daily ObjectsU ...

  3. .NET微信公众号开发系列 - 项目介绍

    由于业务需要,需要开发微信公众号.不过没有查询到比较有用的信息,只能自己摸索前进. 写这个微信公众号开发系列,希望能提供一些帮助. 需要的功能有创建订单.查看订单.还有基本的用户管理. 由于团队人员有 ...

  4. 死磕shell系列-shell介绍

    写的比较好的博客推荐: 博主介绍:-纸短情长 死磕shell系列-shell介绍

  5. Intel 5 6 7 8系列芯片组介绍

    Intel 5 6 7 8系列芯片组介绍 Iknow.2015-11-05 22:40|知识编号:122257 操作步骤: [Inetl 5.6.7.8系列芯片组介绍] 芯片组是主板电路的核心.一定意 ...

  6. FootStep 6、诺基亚、摩托罗拉、索尼爱立信和三星手机系列命名介绍『整理、了解』

    纵观所有手机品牌的命名,可以发现国外手机的命名都比较规范,手机名称的每一个数字和字母都有特殊的含义――它们不仅仅代表某个型号,更代表了厂家对这款机型的定位及策略.但是随着手机市场的竞争日益激烈,每个厂 ...

  7. VR系列--VR介绍

    VR系列--VR介绍 1 定义 基础设施 应用领域 行业痛点 优势 核心 2 发展历程 3 VR/AR/MR/XR区别 4 VR玩家 Facebook Apple Goole Sony HTC Pic ...

  8. 华为智慧办公全系列终端介绍

    ** 华为智慧办公全系列终端介绍 ** 华为企业智慧屏具有智能语音助手.智能显示.智能手写识别.智能音幕.发言人跟踪.电子名牌.多模态会议记录等一系列黑科技,轻松实现跨地域.跨团队的沟通与协作:同时内 ...

  9. OpenX系列标准介绍(2):OpenDRIVE实例分析

    |作者版权所有,未经许可谢绝转载,转载请联系adsimtest@163.com. " 本系列尝试对ASAM OpenX系列标准进行介绍.这是第二篇:通过分析ASAM组织提供的一个OpenDR ...

最新文章

  1. linux shell脚本中调用另一个shell脚本
  2. 你的微信二维码是唯一的吗?【微信二维码的秘密】
  3. 网易云信:要做就做轻量级、小而美的云产品
  4. sql日期相关函数的使用方法
  5. CUBA 7的新功能
  6. 如何在Ubuntu上安装GCC编译器
  7. 安卓案例:初试谷歌图表
  8. Python练习之购物车
  9. 【运动学】基于matlab GUI模拟小球自由落体【含Matlab源码 1630期】
  10. 数据管理能力成熟度评估模型_什么是DCMM
  11. 软件测试之兼容性测试
  12. 宠物卡牌大赛奖励发放结束公告
  13. Matlab学习——求解微分方程(组)
  14. matplotlib画柱状图
  15. 原来手机图片转文字这么简单!按下这个按钮,纸质稿轻松电子化
  16. 1602LCD主要技术参数
  17. PyQt5实现视频流的输出
  18. google/Chrome浏览器如何查看保存在网页上的密码
  19. C语言:学生信息管理系统(详解+源码)
  20. 计算机毕业设计ssm人工智能辅修专业教学管理系统9xg0x系统+程序+源码+lw+远程部署

热门文章

  1. Linux安装gcc、gcc-c++等环境
  2. java mvc 实际分层_SpringMVC体系分层模式原理图解
  3. 如何在ubuntu下安装detectron2_Ubuntu下detectron2 的安装使用笔记
  4. python行业中性_市场风险中性假设的r0只是等效r的其中之一
  5. windows文件路径 正则表达式_Windows非常实用的四款软件
  6. python与tensorflow的关系_Tensorflow GPU与CPU安装库的区别
  7. 弹性地基梁板法计算原理_建筑混凝土结构设计和计算方法,老师傅总结:其原理就是这么简单...
  8. mysql死锁解决办法
  9. python搭建django框架,Python之Web框架Django项目搭建全过程
  10. python监控程序编写_05-python进阶-简单监控程序开发