在Tree中从上往下高效传递数据的基类widget , 定义为:abstract class InheritedWidget extends ProxyWidget
常见的使用方式有两种
定义继承InheritedWidget的子类, 因为InheritedWidget为抽象类
class DataInheritedWidget extends InheritedWidget {
//持有的数据源,由父widget赋值
T data;
//父widget的更新方法
Function? update;
DataInheritedWidget(Widget child, this.data, this.update, {Key? key})
: super(child: child, key: key);
@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) {
//此处可以选择是否通知依赖InheritedWidget的子widget集合进行更新,
return true;
}
}
定义持有该InheritedWidget的工具父widget
class InheritedParentWidget extends StatefulWidget {
final Widget child;
final T data;
const InheritedParentWidget(
{required this.child, required this.data, Key? key})
: super(key: key);
//仅获取InheritedParentWidget中的数据,
static T? ofData(BuildContext context) {
//context Element实现了BuildContext抽象类,此处context就是InheritedParentWidget子widget对应的Element,
return (context
.getElementForInheritedWidgetOfExactType>()
?.widget as DataInheritedWidget?)
?.data;
}
//仅获取InheritedParentWidget中的数据,并对InheritedParentWidget进行依赖监听
//context 同上
static T? of(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType>()
?.data;
}
static void update(BuildContext context) {
(context
.getElementForInheritedWidgetOfExactType>()
?.widget as DataInheritedWidget?)
?.update?.call();
}
@override
State createState() {
return InheritedParent();
}
}
class InheritedParent extends State {
void update() {
setState(() {});
}
@override
Widget build(BuildContext context) {
return DataInheritedWidget(widget.child, widget.data, update);
}
}
InheritedParentWidget接受一个需要显示的子widget以及一个数据源,并将其传递给InheritedWidget
使用如下:
class TestWidget extends StatefulWidget {
@override
State createState() {
return TestWidgetState();
}
}
class TestWidgetState extends State {
var model = Model();
@override
Widget build(BuildContext context) {
return InheritedParentWidget(
data: model,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
//这里使用StatefulBuilder 模拟StatefulWidget实例,主要是方便观察
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
print("StatefulBuilder--tip");
return Text(
"当前数值:${InheritedParentWidget.of(context)?.value}");
},
),
//这里使用StatefulBuilder 模拟StatefulWidget实例,主要是方便观察
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
print("StatefulBuilder--click");
return TextButton(
onPressed: () {
var model = InheritedParentWidget.ofData(context);
model?.value = model.value + 1;
InheritedParentWidget.update(context);
},
child: Text("--点击--:${InheritedParentWidget.ofData(context)?.value}"));
},
),
],
));
}
}
点击底部的TextButton时,只会调用上面的Text重建,而不是调用底部的TextButton重建,即是
只打印: StatefulBuilder–tip,
此处定义实现:
class DataListen extends InheritedNotifier {
final T listenable;
static T? of(BuildContext context) {
return context
.dependOnInheritedWidgetOfExactType>()
?.listenable;
}
static T? ofData(BuildContext context) {
return (context
.getElementForInheritedWidgetOfExactType>()
?.widget as DataListen?)
?.listenable;
}
const DataListen({required Widget child, required this.listenable,Key? key})
: super(child: child, notifier: listenable,key: key);
}
代码使用:
//此处需要继承 Listenable 实现自动更新
class Test2Model extends ChangeNotifier {
int value = 1;
void increase(){
value++;
notifyListeners();
}
}
class Test2Widget extends StatefulWidget {
Test2Widget({Key? key}) : super(key: key);
@override
_Test2WidgetState createState() => _Test2WidgetState();
}
class _Test2WidgetState extends State {
Test2Model model = Test2Model();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return DataListen(
listenable: model,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
print("StatefulBuilder--tip");
return Text(
"当前数值:${DataListen.of(context)?.value}");
},
),
StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
print("StatefulBuilder--click");
return TextButton(
onPressed: () {
DataListen.ofData(context)?.increase();
},
child: Text(
"--点击--:${DataListen.ofData(context)?.value}"));
},
),
],
));
}
}
实现的效果和”方式一”一致
flutter视图的创建过程大致为:
flutter通过上述流程递归完成整个Element树的创建和挂载.
我们可以看到Element的挂载方法mount(),是Element树创建的核心方法也是其必调的方法,而Element代码规定其所有子类也必须调用 父类的挂载方法mount(),如下为mount的简化方法
// @mustCallSuper 标明子类必须调用父类方法
@mustCallSuper
void mount(Element? parent, Object? newSlot) {
....
//InheritedWidget数据传递 的核心实现.
_updateInheritance();
...
}
每个widget所对应的Element 在被添加到Element树中时(调用mount()方法),都会调用updateInheritance()方法,而其实现如下:
Map? _inheritedWidgets;
void _updateInheritance() {
//将父Element _parent的 _inheritedWidgets实例赋值给子类
_inheritedWidgets = _parent?._inheritedWidgets;
}
每个Element都会有一个 Map
上面的_updateInheritance方法只有赋值引用,但是并没有创建Map
在整个Element体系中,只有InheritedWidget对应的InheritedElement重写了_updateInheritance方法,并创建相应实例,如下:
@override
void _updateInheritance() {
final Map? incomingWidgets = _parent?._inheritedWidgets;
//如果父类incomingWidgets 不为空,则将父类数据拷贝进一个新创建的HashMap实例
if (incomingWidgets != null)
_inheritedWidgets = HashMap.from(incomingWidgets);
else
_inheritedWidgets = HashMap();
_inheritedWidgets![widget.runtimeType] = this;
}
即: InheritedElement 在父类Map
结论: 所有InheritedElement的子Element共享同一个Map
我们回头看,子widget获取InheritedWidget数据的实现:
static T? ofData(BuildContext context) {
//获取DataInheritedWidget对应的Elemnt实例,
return (context
.getElementForInheritedWidgetOfExactType>()
?.widget as DataInheritedWidget?)
?.data;
}
static T? of(BuildContext context) {
//获取DataInheritedWidget对应的Elemnt实例后并对DataInheritedWidget进行依赖监听
return context
.dependOnInheritedWidgetOfExactType>()
?.data;
}
上述方法都是在子widget中调用的,也就是context是子widget实例中的context,我们知道widget中的BuildContext就是Element, 上述方法在Element中的实现如下:
@override
T? dependOnInheritedWidgetOfExactType({Object? aspect}) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
if (ancestor != null) {
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
@override
InheritedElement? getElementForInheritedWidgetOfExactType() {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
return ancestor;
}
我们先看,仅获取数据而不依赖监听的 getElementForInheritedWidgetOfExactType方法,其内部实现为:
final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
而: _inheritedWidgets即是:前面分析的 InheritedElement所有子Element所共享的 Map
这里也就是解决了第一个疑问, 子widget是如何获取从当前widget到根widget路径中的所有InheritedWidget :
即是:(1)子widget内部对应的Element持有一个 缓存从当前节点到根节点路径上的所有InheritedElement的map引用: Map
(2)我们可以通过调用Elemnt的widget方法获取和其对应的Widget实例
问题一中getElementForInheritedWidgetOfExactType
是仅获取InheritedWidget的数据而不对InheritedWidget进行依赖监听, 而dependOnInheritedWidgetOfExactType
是获取InheritedWidget数据后并对InheritedWidget进行依赖监听,对比两个方法实现中: dependOnInheritedWidgetOfExactType
比getElementForInheritedWidgetOfExactType
多调用了一行dependOnInheritedElement(ancestor, aspect: aspect)
,
dependOnInheritedElement(ancestor, aspect: aspect)
的实现如下:
abstract class Element {
....
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
assert(ancestor != null);
//如果dependencies为null 则初始化
_dependencies ??= HashSet();
//将依赖对象增加到依赖对象合集中
_dependencies!.add(ancestor);
//更新InheritedElement的被依赖对象集合
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
....
}
当我们调用dependOnInheritedWidgetOfExactType
方法时,我们会找到相对应的InheritedElement, 将其添加到自己的依赖集合中,并将自己添加到InheritedElement的被依赖合集了,当InheritedWidget进行更新时,便能定位到所有依赖自己的子widget,
为了分析InheritedWidget的局部刷新,我们需要先大致了解下widget的刷新机制
_dirty = true
,然后调用BuildOwner->scheduleBuildFor(Element element)方法List _dirtyElements
集合中并调用刷新屏幕的方法 scheduleFrame()_dirtyElements[index].rebuild();
这里是widget刷新的大致流程,针对不同的Element,其performRebuild方法会有区别,
InheritedWidget对应的InheritedElement 对上的继承关系为
InheritedElement ->ProxyElement ->ComponentElement -> Element ,整个继承链中,只有ComponentElement重写了 performRebuild()方法,去除无用代码后如下:
void performRebuild() {
Widget? built;
try {
//创建Widget
built = build();
} catch (e, stack) {
} finally {
_dirty = false;
}
try {
//根据新创建的widget 更新 Element? _child;
_child = updateChild(_child, built, slot);
} catch (e, stack) {
_child = updateChild(null, built, slot);
}
}
而InheritedElement的父类ProxyElement 重写了build()方法
Widget build() => widget.child;
即,创建的widget,是传递给ProxyWidget 的子widget参数, InheritedElement继承
ProxyElement但是没有重写build(),所以其方法也是如此,(如果子widget实例不变,build()返回的对象永远相同)
对于updateChild()更新方法, InheritedElement的继承链都没重写,下面是Element类的updateChild方法:
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
final Element newChild;
if (child != null) {
//在刷新时 child不为空,所以走此处
bool hasSameSuperclass = true;
if (hasSameSuperclass && child.widget == newWidget) {
//1,注意此处,如果新创建的子widget和之前持有的子Element的widget相同则不更新
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
//2,如果两个widget的runtimeType和key不同,则进行更新
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
newChild = child;
} else {
//如果上述情况都不满足,则重新构建widget和element,并进行挂载
deactivateChild(child);
assert(child._parent == null);
newChild = inflateWidget(newWidget, newSlot);
}
} else {
newChild = inflateWidget(newWidget, newSlot);
}
return newChild;
}
注意上述 1 处,当InheritedWidget自身进行更新时,其刷新流程是:
在performRebuild方法中 如我们上述所说,InheritedElemnt调用 Widget build() => widget.child;
返回的外部传递的widget,所以如果更新流程是从InheritedWidget自身开始且传递的子widget不变,则build()返回的值恒定. => 在updateChild()方法中触发上述 1 处标明的条件,即其子Widget不会更新,(如果传递更新节点比较高,导致传递进来的widget对象发生变化,则会像正常的widget一样刷新)
上述,我们得到一个结论,如果仅调用InheritedElement的更新方法,进行更新时,其内部会因为返回的widget实例相同,而不更新子widget
那么当我们在InheritedWidget外部包装一个(非ProxyWidget类型)父widget,并调用其内部的setState方法时,
因为build() 返回的widget实例,是新创建的,所以和当前Element的widget不同,所以会走
2 处, 调用子Element的update(covariant Widget newWidget) 方法
在InheritedElement的继承链中 ProxyElement 重写了该方法
@override
void update(ProxyWidget newWidget) {
final ProxyWidget oldWidget = widget;
super.update(newWidget);
//调用ProxyElement-> updated()
updated(oldWidget);
_dirty = true;
//调用父类Element的rebuild方法 => Element->performRebuild()=>ComponentElement-> performRebuild()=>回到上述我们分析的InheritedElement的更新流程
rebuild();
}
所以当 InheritedWidget的父widget 调用其 子Element(即是InheritedElement)的update方法最终调用了 ProxyElement-> updated()方法
ProxyElement-> updated()方法详情
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
@protected
void notifyClients(covariant ProxyWidget oldWidget);
InheritedElement分别重写了这两个方法
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
@override
void notifyClients(InheritedWidget oldWidget) {
for (final Element dependent in _dependents.keys) {
notifyDependent(oldWidget, dependent);
}
}
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
可以看到
我们在前面分析问题一的时候,当调用 dependOnInheritedElement方法时,会将当前的Element填充进InheritedWidget 的dependents集合中,所以InheritedWidget再notifyClients方法中遍历其被依赖的集合并调用其didChangeDependencies方法
而Element的didChangeDependencies方法 会调用markNeedsBuild方法 更新自己
@mustCallSuper
void didChangeDependencies() {
markNeedsBuild();
}
结论:经过以上分析,得到第二第三个问题结论:
如果仅更新InheritedWidget自身的话,因为传递给InheritedWidget的子widget实例不变,所以在更新子widget的过程,创建的widget实例都相同,所以不刷新子widget, 同时因为从子节点开始更新的话 不会调用的 void update() 方法.所以也不会更新InheritedWidget被监听依赖的Widget集合
当InheritedWidget的父widget的 调用进行刷新时,调用child.update() 方法,即是InheritedWidget会调用notifyClients 遍历其被依赖的集合Element,调用其didChangeDependencies方法进行更新,而InheritedWidget本身因为build()方返回的widget实例是同一个,故其本身不刷新, 即是实现了局部刷新
经过问题二三的分析,我们知道InheritedWidget 本身刷新时,因为没有调用notifyClients方法,所以被依赖的对象没有进行刷新,那如果我们手动调用 是不是就实现了 InheritedWidget的自动刷新机制呢 ,
官方的InheritedNotifier就是通过手动调用notifyClients方法来实现更新的
InheritedNotifierElement 手动调用notifyClients方法
class _InheritedNotifierElement extends InheritedElement {
_InheritedNotifierElement(InheritedNotifier widget) : super(widget) {
widget.notifier?.addListener(_handleUpdate);
}
@override
InheritedNotifier get widget => super.widget as InheritedNotifier;
bool _dirty = false;
@override
void update(InheritedNotifier newWidget) {
final T? oldNotifier = widget.notifier;
final T? newNotifier = newWidget.notifier;
if (oldNotifier != newNotifier) {
oldNotifier?.removeListener(_handleUpdate);
newNotifier?.addListener(_handleUpdate);
}
super.update(newWidget);
}
@override
Widget build() {
//手动调用
if (_dirty)
notifyClients(widget);
return super.build();
}
void _handleUpdate() {
_dirty = true;
markNeedsBuild();
}
@override
void notifyClients(InheritedNotifier oldWidget) {
super.notifyClients(oldWidget);
_dirty = false;
}
@override
void unmount() {
widget.notifier?.removeListener(_handleUpdate);
super.unmount();
}
}
1. 子widget内部对应的Element持有一个 缓存从当前节点到根节点路径上的所有InheritedElement的map引用: Map
2. Element在调用dependOnInheritedWidgetOfExactType方法时,会将自己添加进InheritedElement的Map
3. InheritedElement刷新调用updateChild()方法时,因为build()返回的widget实例不变,所以导致不会调用 Elemnt->update()方法,既:不会更新子widget
4. 当InheritedElement的父Elemnt 刷新执行 child.update(newWidget),方法会调用 InheritedElement->updated() => InheritedElement->notifyClients(),在notifyClients的方法中会遍历 _dependents 集合并调用 Element->didChangeDependencies()方法,而didChangeDependencies方法中会调用 markNeedsBuild()方法,进而实现了InheritedWidget被依赖对象的刷新