InheritedWidget 源码定义
/// Base class for widgets that efficiently propagate
///information down the tree.
/// To obtain the nearest instance of a particular type of inherited
///widget from a build context, use
///[BuildContext.inheritFromWidgetOfExactType].
/// Inherited widgets, when referenced in this way, will cause the
///consumer to rebuild when the inherited widget itself changes state.
大体意思如下:
InheritedWidget 是在树中高效向下传递信息的基类部件;
调用[BuildContext.inheritFromWidgetOfExactType]方法可以从 BuildContext 中获取到最近的 InheritedWidget 类型的实例;
在 InheritedWidget 类型的控件被引用,也就是调用过 inheritFromWidgetOfExactType 方法后,当 InheritedWidget 自身状态改变时,会导致引用了 InheritedWidget 类型的子控件重构(rebuild)。
InheritedWidget 用法案例
定义数据模型
这里随便定义一个 Person 类。
//数据模型
class Person {
String name;
int age;
Person(this.name, this.age);
@override
String toString() {
// TODO: implement toString
return "我叫${this.name},今年${this.age}了";
}
}
自定义 InheritedWidget 控件类
创建一个类继承 InheritedWidget,并实现 updateShouldNotify 方法。
class InheriedDataWidget extends InheritedWidget {
final Person person; //需要在树中共享的数据
InheriedDataWidget({@required this.person,Widget child})
: super(child: child);
//定义一个便捷方法,方便子树中的widget获取共享数据
static InheriedDataWidget of(BuildContext context) {
return context.inheritFromWidgetOfExactType(InheriedDataWidget);
}
@override
bool updateShouldNotify(InheriedDataWidget oldWidget) {
// TODO: implement updateShouldNotify
//如果返回true,则子树中依赖(build函数中有调用)本widget的子 .
//widget的`state.didChangeDependencies`方法会被调用
return this.person.name != oldWidget.person.name ||
this.person.age != oldWidget.person.age;
}
}
之前说到调用[BuildContext.inheritFromWidgetOfExactType]方法可以从 BuildContext 中获取到最近的 InheritedWidget 类型的实例,所以此处定义一个静态的 of 方法,通过传入的 context 获取到最近的 InheriedDataWidget 实例。
InheriedDataWidget 的使用
InheriedDataWidget 使用起来也很简单,它本身也是一个控件,只要在任意一个页面的子控件调用其构造方法就行,这里我们定义一个形如
的 Widget 树。
WidgetA 类
//WidgetA
class WidgetA extends StatefulWidget {
@override
_WidgetAState createState() => _WidgetAState();
}
class _WidgetAState extends State {
Person person = new Person("小明", 24);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
//调用构造,在此节点共享了一个Person类型的数据
child: InheriedDataWidget(
person: person,
child: WidgetA1(),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
//在setState中刷新Person的值
this.person=new Person(person.name, person.age+1);
});
},
child: Icon(Icons.add),
),
);
}
}
WidgetA 是一个 StatefulWidget 类型的控件,可以调用 setState 刷新,如果是继承 Stateless 类型的控件,那我们也可以通过 Stream 或者其他方式刷新数据,感兴趣请看什么是 Stream? Dart
WidgetA1 类
//控件WidgetA
class WidgetA1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.grey,
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.all(10),
child: Column(
children: [
Text(
"A1 Widget",
style: TextStyle(color: Colors.white,fontSize: 20),
),
//调用InheriedDataWidget.of()获取数据,其实也就是调用了 .
//inheritFromWidgetOfExactType方法
Text(
"共享数据的信息: ${InheriedDataWidget.of(context).person.toString()}",
style: TextStyle(color: Colors.white,fontSize: 20),
),
Container(
margin: EdgeInsets.only(top: 40),
color: Colors.deepOrangeAccent,
height: MediaQuery.of(context).size.height*0.5,
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
WidgetA1_1(),
WidgetA1_2(),
WidgetA1_3(),
],
),
),
],
),
);
}
}
WidgetA1_1 类
class WidgetA1_1 extends StatefulWidget {
@override
_WidgetA1_1State createState() => _WidgetA1_1State();
}
class _WidgetA1_1State extends State {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
height: 140,
width: MediaQuery.of(context).size.width/3-20,
//引用共享数据
child: Text("WidgetA1_1ful\n\n共享数据是:${InheriedDataWidget.of(context).person.toString()}"),
);
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
//当WidgetA中Person数据发生改变时,此方法会被调用,
//前提是updateShouldNotify返回true
print("WidgetA1_1ful===>didChangeDependencies");
}
}
WidgetA1_2 类
class WidgetA1_2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
height: 140,
width: MediaQuery.of(context).size.width/3-20,
child: Text("WidgetA1_2less\n\n共享数据是:${InheriedDataWidget.of(context).person.toString()}"),
);;
}
}
WidgetA1_3 类
class WidgetA1_3 extends StatefulWidget {
@override
_WidgetA1_3State createState() => _WidgetA1_3State();
}
class _WidgetA1_3State extends State {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
height: 140,
width: MediaQuery.of(context).size.width/3-20,
child: Text("WidgetA1_3ful\n\n未引用共享数据信息"),
);
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
print("WidgetA1_3===>didChangeDependencies");
}
}
运行结果
当我们点击 floatingActionButton 的时候,WidgetA1, WidgetA1_1, WidgetA1_2 的控件都会更新 Person 的信息,而且每点 floatingActionButton 一次,
当我们点击 floatingActionButton 的时候,WidgetA1, WidgetA1_1, WidgetA1_2 的控件都会更新 Person 的信息,而且每点 floatingActionButton 一次,都会输出:
WidgetA1_1ful===>didChangeDependencies
如果我们试图在和 WidgetA 的同一层级的兄弟节点去访问 InheriedDataWidget 的 Person 数据,是不行的,因为父节点中并没有插入 InheriedDataWidget。
- Widget B
把 WidgetB 和 WidgetA 保持同一节点
//WidgetB
class WidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
//此处将报错,因为WidgetB 与WidgetA 是兄弟节点,
//B中并未找到A中 InheriedDataWidget 的数据
return Text(InheriedDataWidget.of(context).person.toString());
}
}
这也体现了 Inheried(遗传) 这一单词的特性,遗传只存在于父子。兄弟不存在遗传的关系。
一张图总结
这种数据共享的方式在某些场景还是很有用的,就比如说全局主题,字体大小,字体颜色的变更,只要在 App 根层级共享出这些配置数据,然后在触发数据改变之后,所有引用到这些共享数据的地方都会刷新,这换主题,字体是不是就很轻松,事实上 Theme.of(context).primaryColor 之流就是这么干的。。。
InheriedWidget 的源码浅析
InheritedWidget 源码
abstract class InheritedWidget extends ProxyWidget {
const InheritedWidget({ Key key, Widget child })
: super(key: key, child: child);
@override
InheritedElement createElement() => InheritedElement(this);
@protected
bool updateShouldNotify(covariant InheritedWidget oldWidget);
}
可见 InheritedWidget 是一个抽象类,其子类必须实现 updateShouldNotify 方法,之前的 InheriedDataWidget 也实现了该方法,还可以看到它继承自 ProxyWidget。
ProxyWidget 源码
abstract class ProxyWidget extends Widget {
/// Creates a widget that has exactly one child widget.
const ProxyWidget({ Key key, @required this.child }) : super(key: key);
final Widget child;
}
可见 ProxyWidget 中也没什么,最主要的还是 InheritedWidget 中 的 InheritedElement createElement() 方法,它返回了一个 的 InheritedElement 实例。
InheritedElement 源码
class InheritedElement extends ProxyElement {
InheritedElement(InheritedWidget widget) : super(widget);
@override
InheritedWidget get widget => super.widget;
// 记录了所有依赖的对象
final Map _dependents = HashMap();
// 该方法是基类Element 的重写,会在Element mount和activate方
//法中调用,基类的该方法实现待会贴出。该方法的作用就是维护
//基类的_inheritedWidgets,其类型为 Map,
//在此处它除了保存父类的依赖映射关系之外,还将自身添加到
//_inheritedWidgets中。这也使得查找InheritedWidget的效率大大的
//提高,因为他都是从最近的节点去查找的
@override
void _updateInheritance() {
assert(_active);
final Map incomingWidgets = _parent?._inheritedWidgets;
if (incomingWidgets != null)
_inheritedWidgets = HashMap.from(incomingWidgets);
else
_inheritedWidgets = HashMap();
_inheritedWidgets[widget.runtimeType] = this;
}
@override
void debugDeactivated() {
assert(() {
assert(_dependents.isEmpty);
return true;
}());
super.debugDeactivated();
}
//返回对应的依赖,通常是在updateDependencies方法中调用
@protected
Object getDependencies(Element dependent) {
return _dependents[dependent];
}
//设置或者更新依赖,通常是在updateDependencies方法中调用
@protected
void setDependencies(Element dependent, Object value) {
_dependents[dependent] = value;
}
//Called by [inheritFromWidgetOfExactType] when a new [dependent] is added
//当调用inheritFromWidgetOfExactType时候,
//inheritFromWidgetOfExactType会调用 updateDependencies更新依赖
@protected
void updateDependencies(Element dependent, Object aspect) {
setDependencies(dependent, null);
}
// 被notifyClients 通知调用,通知相关依赖的Widget调用didChangeDependencies
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
@override
void updated(InheritedWidget oldWidget) {
//如果 updateShouldNotify 返回true 则通知更新
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
//通知依赖了 InheritedWidget 的控件调用 didChangeDependencies
@override
void notifyClients(InheritedWidget oldWidget) {
assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
for (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);
}
}
}
其中 _updateInheritance 在 Element 基类中的实现如下:
void _updateInheritance() {
assert(_active);
_inheritedWidgets = _parent?._inheritedWidgets;
}
只是单纯把父 element 的 _inheritedWidgets 属性保存在自身 _inheritedWidgets 里。从而实现映射关系的层层向下传递。
总结来说就是 Element 在 mount 的过程中,如果不是 InheritedElement,就简单的将缓存指向父节点的缓存,如果是 InheritedElement,就创建一个缓存的副本,然后将自身添加到该副本中,这样做会有以下几点:
- 查找 InheritedWidget 的效率比较高,不用一层层向上找。
InheritedElement 的父节点们是无法查找到自己的,即 InheritedWidget 的数据只能由父节点向子节点传递,反之不能。
如果某节点的父节点有不止一个同一类型的 InheritedWidget,调用 inheritFromWidgetOfExactType 获取到的是离自身最近的该类型的 InheritedWidget。
之前 WidgetA1,WidgetA1_1 之类的依赖是如何添加到 _dependents 中,在共享数据改变时能调用到 didChangeDependencies 的呢?
WidgetA1,WidgetA1_1 之中用引用的就是调用 InheriedDataWidget.of(context)方法,而其中 of 方法调用的还是:
static InheriedDataWidget of(BuildContext context) {
return context.inheritFromWidgetOfExactType(InheriedDataWidget);
}
inheritFromWidgetOfExactType 源码
@override
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet();
_dependencies.add(ancestor);
//将该实例添加到自身的依赖列表中,同时将自身添加到对应的依赖项列表中,
//updateDependencies 这个方法在之前的 InheritedElement 出现过
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
@override
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
assert(_debugCheckStateIsActiveForAncestorLookup());
//通过 _inheritedWidgets 映射中查找是否有指定类型的 InheritedElement
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
if (ancestor != null) {
//如果 InheritedElement 不为空
assert(ancestor is InheritedElement);
//调用 inheritFromElement
return inheritFromElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
其作用就是在 _inheritedWidget 映射中查找是否有特定类型 InheritedWidget 的实例。如果有则将该实例添加到自身的依赖列表中,同时将自身添加到对应的依赖项列表中。这样该 InheritedWidget 在更新后就可以通过其 _dependents 属性知道需要通知哪些依赖了它的 widget。
inheritFromWidgetOfExactType 由 BuildContext 的实例调用,但它却能直接访问 Element 中的_inheritedWidgets 这个属性? 这是因为 BuildContext 是接口,而 Element 实现了 BuildContext 接口,inheritFromWidgetOfExactType 方法的具体实现就是在 Element 类里。
至此,你是否也想看看 Flutter 中 Theme 的源码呢?
最后
最后厚颜贴一张自己学习Flutter的公众号,感兴趣的小伙伴可以一起学习哦。。。
本文实例代码
import 'package:flutter/material.dart';
class InheriedHomePage extends StatefulWidget {
@override
_InheriedHomePageState createState() => _InheriedHomePageState();
}
class _InheriedHomePageState extends State {
int _curIndex = 0;
PageController _pageController;
List _tabs = ["View1", "View2"];
@override
void initState() {
// TODO: implement initState
super.initState();
_pageController = new PageController(initialPage: _curIndex);
}
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("InheriedDemo"),
),
body: PageView(
children: [
WidgetA(),
WidgetB(),
],
controller: _pageController,
onPageChanged: (index) {
setState(() {
_curIndex = index;
});
},
),
bottomNavigationBar: BottomNavigationBar(
items: [
BottomNavigationBarItem(
icon: new Icon(Icons.tablet_android), title: new Text(_tabs[0])),
BottomNavigationBarItem(
icon: new Icon(Icons.tablet_mac), title: new Text(_tabs[1])),
],
currentIndex: _curIndex,
onTap: (index) {
_curIndex = index;
_pageController.animateToPage(index,
duration: Duration(milliseconds: 100), curve: Curves.linear);
},
),
);
}
}
//WidgetA
class WidgetA extends StatefulWidget {
@override
_WidgetAState createState() => _WidgetAState();
}
class _WidgetAState extends State {
Person person = new Person("小明", 24);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
//调用构造,在此节点共享了一个Person类型的数据
child: InheriedDataWidget(
person: person,
child: WidgetA1(),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
//在setState中刷新Person的值
this.person=new Person(person.name, person.age+1);
});
},
child: Icon(Icons.add),
),
);
}
}
//控件WidgetA
class WidgetA1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.grey,
width: MediaQuery.of(context).size.width,
margin: EdgeInsets.all(10),
child: Column(
children: [
Text(
"A1 Widget",
style: TextStyle(color: Colors.white,fontSize: 20),
),
//调用InheriedDataWidget.of()获取数据,其实也就是调用了inheritFromWidgetOfExactType方法
Text(
"共享数据的信息: ${InheriedDataWidget.of(context).person.toString()}",
style: TextStyle(color: Colors.white,fontSize: 20),
),
Container(
margin: EdgeInsets.only(top: 40),
color: Colors.deepOrangeAccent,
height: MediaQuery.of(context).size.height*0.5,
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
WidgetA1_1(),
WidgetA1_2(),
WidgetA1_3(),
],
),
),
],
),
);
}
}
class WidgetA1_1 extends StatefulWidget {
@override
_WidgetA1_1State createState() => _WidgetA1_1State();
}
class _WidgetA1_1State extends State {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
height: 140,
width: MediaQuery.of(context).size.width/3-20,
//引用共享数据
child: Text("WidgetA1_1ful\n\n共享数据是:${InheriedDataWidget.of(context).person.toString()}"),
);
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
//当WidgetA中Person数据发生改变时,此方法会被调用,前提是updateShouldNotify返回true
print("WidgetA1_1ful===>didChangeDependencies");
}
}
class WidgetA1_2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
height: 140,
width: MediaQuery.of(context).size.width/3-20,
child: Text("WidgetA1_2less\n\n共享数据是:${InheriedDataWidget.of(context).person.toString()}"),
);;
}
}
class WidgetA1_3 extends StatefulWidget {
@override
_WidgetA1_3State createState() => _WidgetA1_3State();
}
class _WidgetA1_3State extends State {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
height: 140,
width: MediaQuery.of(context).size.width/3-20,
child: Text("WidgetA1_3ful\n\n未引用共享数据信息"),
);
}
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
print("WidgetA1_3===>didChangeDependencies");
}
}
//WidgetB
class WidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
//此处将报错,因为WidgetB 与WidgetA 是兄弟节点,B中并未找到A中 InheriedDataWidget 的数据
return Text(InheriedDataWidget.of(context).person.toString());
}
}
//数据模型
class Person {
String name;
int age;
Person(this.name, this.age);
@override
String toString() {
// TODO: implement toString
return "我叫${this.name},今年${this.age}了";
}
}
class InheriedDataWidget extends InheritedWidget {
final Person person; //需要在树中共享的数据
InheriedDataWidget({@required this.person,Widget child})
: super(child: child);
//定义一个便捷方法,方便子树中的widget获取共享数据
static InheriedDataWidget of(BuildContext context) {
return context.inheritFromWidgetOfExactType(InheriedDataWidget);
}
@override
bool updateShouldNotify(InheriedDataWidget oldWidget) {
// TODO: implement updateShouldNotify
//如果返回true,则子树中依赖(build函数中有调用)本widget的子widget的`state.didChangeDependencies`方法会被调用
return this.person.name != oldWidget.person.name ||
this.person.age != oldWidget.person.age;
}
}