Flutter InheritedWidget, 就是一个观察者模式,里面两大东西,一个是收集,一个通知, 接下来我们看一个demo例子一步一步分析里面的原理
1. 收集过程
import 'package:flutter/material.dart';
class MyCounterWidget extends InheritedWidget {
final int counter;
// 抛出去对象
static MyCounterWidget of(BuildContext context) {
// 沿着 elment树 => 找最近的 MyCounterWidget 这个widget
final MyCounterWidget? result =
context.dependOnInheritedWidgetOfExactType<MyCounterWidget>();
assert(result != null, 'No MyCounterWidget found in context');
return result!;
}
const MyCounterWidget({required super.child, super.key, this.counter = 100});
@override
// state => did
bool updateShouldNotify(MyCounterWidget oldWidget) {
return oldWidget.counter != counter;
}
}
class ShareInHeritedState extends StatefulWidget {
const ShareInHeritedState({super.key});
@override
State<ShareInHeritedState> createState() => _ShareInHeritedStateState();
}
class _ShareInHeritedStateState extends State<ShareInHeritedState> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("InheritedWidget test"),
),
body: MyCounterWidget(
counter: _count,
child: Column(
children: [
HYShowData01(),
HYShowData02(),
FloatingActionButton(
child: Icon(Icons.pets),
onPressed: () {
setState(() {
_count += 1;
});
})
],
),
),
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("didChangeDependencies: _ShareInHeritedStateState");
}
@override
void didUpdateWidget(covariant ShareInHeritedState oldWidget) {
super.didUpdateWidget(oldWidget);
print("didUpdateWidget: _ShareInHeritedStateState");
}
}
class HYShowData01 extends StatelessWidget {
const HYShowData01({super.key});
@override
Widget build(BuildContext context) {
return Card(
color: Colors.red,
child: Text("当前计数1:${MyCounterWidget.of(context).counter}"),
);
}
}
class HYShowData02 extends StatefulWidget {
const HYShowData02({super.key});
@override
State<HYShowData02> createState() => _HYShowData02State();
}
class _HYShowData02State extends State<HYShowData02> {
@override
void initState() {
super.initState();
print("initState: _HYShowData02State");
}
@override
Widget build(BuildContext context) {
return Card(
color: Colors.blue,
child: Text(
"当前计数2:${MyCounterWidget.of(context).counter}"), // statelessBUild的时候 => 触发收集
);
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
print("didChangeDependencies: _HYShowData02State");
}
@override
void didUpdateWidget(covariant HYShowData02 oldWidget) {
super.didUpdateWidget(oldWidget);
print("didUpdateWidget: _HYShowData02State");
}
}
哪里收集?
当调用build的时候, 就会调用 MyCounterWidget.of(context).counter => context.dependOnInheritedWidgetOfExactType<MyCounterWidget>();
这里的context 就是 HYShowData02Element ,相当于从 子的Element中去 拿到 MyCounterWidget 实例? 有点疑惑先放这里;
class _HYShowData02State {
Widget build(BuildContext context) {
return Card(
color: Colors.blue,
child: Text(
"当前计数2:${MyCounterWidget.of(context).counter}"), // statelessBUild的时候 => 触发收集
);
}
}
进入 context.dependOnInheritedWidgetOfExactType , 在 里面完成了 收集过程 ancestor._dependents[子Element] = null;
// Element 类中
@override
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
// _inheritedWidgets 是一个Map <InheritedWidget 类型, InheritedElement 实例>
final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
if (ancestor != null) {
// 通过拿到 InheritedElement实例 去 获取 element.widget, 并返回回去
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
assert(ancestor != null);
// 子Element 通过 _dependencies持有 ancestor: InheritedElement
// 其实 子Element 可以通过 _inheritedWidgets 根据 InheritedElement 类型可以拿到,为什么需要有个数组去添加,确实有点奇怪,可能是有其他的作用????
_dependencies ??= HashSet<InheritedElement>();
_dependencies!.add(ancestor);
// ancestor: InheritedElement 持有 子Element
// ancestor._dependents[this] = aspect;
// 这里就完成了 收集过程
ancestor.updateDependencies(this, aspect);
return ancestor.widget as InheritedWidget;
}
这里有点迷惑的是 为什么 _inheritedWidgets 这里包含了 我们所需要的 InheritedElement
其实这个在 Element mount 的时候 从 父node 传递过来的 有个 _updateInheritance(); 就是把父node的传递给子node _inheritedWidgets = _parent?._inheritedWidgets;
// Element
void mount(Element? parent, Object? newSlot) {
_parent = parent;
_slot = newSlot;
_lifecycleState = _ElementLifecycle.active;
_depth = _parent != null ? _parent!.depth + 1 : 1;
if (parent != null) {
// Only assign ownership if the parent is non-null. If parent is null
// (the root node), the owner should have already been assigned.
// See RootRenderObjectElement.assignOwner().
_owner = parent.owner;
}
assert(owner != null);
final Key? key = widget.key;
if (key is GlobalKey) {
owner!._registerGlobalKey(key, this);
}
//
_updateInheritance();
attachNotificationTree();
}
2. 通知过程
从前面的分析,知道了收集过程 ancestor._dependents[子Element] = null;
接下来我们看看他们是怎么通知的,这里其实就很简单直接遍历 ancestor._dependents
在 MyCounterWidget updateShouldNotify 里面 打个断点,并点击按钮 触发;
bool updateShouldNotify(MyCounterWidget oldWidget) {
return oldWidget.counter != counter;
}
InheritedElement extend ProxyElement
- 下面InhertedElement的 updated方法会 被 @override;
// 这里的child 其实就是对应的 MyCounterWidget的 Element
child.update(newWidget);
// ProxyElement
@override
void update(ProxyWidget newWidget) {
final ProxyWidget oldWidget = widget as ProxyWidget;
super.update(newWidget);
// 这里会先进入 InheritedElement的 updated,如果需要更新才会去更新
updated(oldWidget);
rebuild(force: true);
}
// InheritedElement
@override
void updated(InheritedWidget oldWidget) {
if ((widget as InheritedWidget).updateShouldNotify(oldWidget)) {
super.updated(oldWidget);
}
}
// 进入 ProxyElement 的更新,这里我们就看到 notifyClients(oldWidget)
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
// InheritedElement
@override
void notifyClients(InheritedWidget oldWidget) {
// 拿到 _dependents.keys 进行遍历
for (final Element dependent in _dependents.keys) {
// check that it really depends on us
notifyDependent(oldWidget, dependent);
}
}
}
// InheritedElement
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
// Element
@mustCallSuper
void didChangeDependencies() {
// 标记更新状态
markNeedsBuild();
}
以上的分析,我们就能看到 怎么样通知的过程
接下来我们继续看看 为什么需要 rebuild(force: true);
@pragma('vm:prefer-inline')
void rebuild({bool force = false}) {
performRebuild();
}
// ComponentElement
void performRebuild() {
Widget? built;
built = build();
super.performRebuild(); // clears the "dirty" flag
// 这里就是更新孩子Element
// 这里如果没有child 会进行创建 Element newChild = newWidget.createElement();
_child = updateChild(_child, built, slot);
}