InheritedWidget 介绍
在 Flutter 进行界面开发时,咱们常常会遇到数据传递的问题。因为 Flutter 采纳节点树的形式组织页面,以致于一个一般页面的节点层级会很深。此时,咱们如果还是一层层传递数据,当须要获取多层父节点的数据时,会十分麻烦。
因为呈现上述问题,Flutter 给我咱们提供一种 InheritedWidget
,InheritedWidget
可能让节点下的所有子节点,拜访该节点下的数据。
对于 Scoped Model
、BloC
、Provider
就是基于 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 办法
@override
InheritedElement createElement() => InheritedElement(this);
/// 父级或先人 widget 中扭转 (updateShouldNotify 返回 true) 时会被调用。@protected
bool updateShouldNotify(covariant InheritedWidget oldWidget);
}
InheritedWidget 示例
咱们以官网计数器的例子用 InheritedWIdget
进行革新
第一步:先定义一个 InheritedState 类
- 创立
InheritedState
继承自InheritedWidget
- 定义一个须要共享的数据
count
- 定义一个给内部获取
InheritedState
实例的of
办法 -
重写
updateShouldNotify
办法,次要目标告诉依赖该树共享数据的子widget
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; /// 获取组件最近以后的 InheritedWidget static InheritedState of(BuildContext context) {return context.dependOnInheritedWidgetOfExactType<InheritedState>(); } /// 告诉依赖该树共享数据的子 widget @override bool updateShouldNotify(covariant InheritedState oldWidget) {return count != oldWidget.count;} }
第二步:编写布局并用 InheritedWidget 实现计数器成果
- 首先创立了一个
InheritedCount
组件以及子组件WidgetA
和WidgetB
。 - 在
InheritedCount
中定义一个count
变量,用于子widget
获取该count
数据。 - 应用
InheritedState
组件并传入count
值,以及子组件。 - 子组件中应用
InheritedState
中的共享数据。 -
在
InheritedCount
按钮点击扭转count
值,子组件数据将被刷新。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; @override Widget 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 { @override Widget build(BuildContext context) {return Text("widget text"); } } class WidgetB extends StatelessWidget { @override Widget build(BuildContext context) {return Text(InheritedState.of(context)?.count.toString(), style: TextStyle( color: Colors.green, fontSize: 50 ), ); } }
InheritedWidget 源码剖析
在下面的计数器示例代码中,
WidgetB
和InheritedWidget
产生关联的就是InheritedState.of(context)?.count.toString()
,其中最要害的形式是context.dependOnInheritedWidgetOfExactType()
,咱们查看dependOnInheritedWidgetOfExactType()
在Element
中的源码如下:该代码是在 framework.dart 第 3960 行Map<Type, InheritedElement> _inheritedWidgets; @override T 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 什么状况下会调用:@mustCallSuper void mount(Element parent, dynamic newSlot) { ...... _updateInheritance(); ...... } @mustCallSuper void activate() { ...... _updateInheritance(); ...... }
咱们能够看到在
Element
中它在mount
和activate
函数执行了调用,也就是说element
每次挂载和从新时,会调用该办法。那么当该办法执行的时候,element
就会从下层中拿到所有的InheritedElement
。而InheritedElement
他最终继承了Element
,并能够看到InheritedElement
重写了_updateInheritance
办法:@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; }
InheritedWidget 是如何进行刷新的
后面咱们剖析到
InheritedElement
会拿到父类的所有的InheritedElment
并向下传递,而InheritedWidget
正是通过这种办法能力让上面的子Widget
能拜访的下层中所有的InheritedWidget
,那么它是如何进行刷新的呢?咱们在Element
的dependOnInheritedWidgetOfExactType
办法中调用了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). @protected void updated(covariant ProxyWidget oldWidget) {notifyClients(oldWidget); } @override void notifyClients(InheritedWidget oldWidget) {assert(_debugCheckOwnerBuildTargetExists('notifyClients')); for (final Element dependent in _dependents.keys) {assert(() { // check that it really is our descendant Element ancestor = dependent._parent; while (ancestor != this && ancestor != null) ancestor = ancestor._parent; return ancestor == this; }()); // check that it really depends on us assert(dependent._dependencies.contains(this)); notifyDependent(oldWidget, dependent); } } } @protected void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {dependent.didChangeDependencies(); }
因为当
InheritedElement
更新时,会执行updated
办法,而后持续调用notifyClients
,遍历所有的element
并调用didChangeDependencies
办法。