1.共享数据
1.1 Normal Widget Element Mount
void mount(Element? parent, Object? newSlot) {
...
_updateInheritance();
...
}
void _updateInheritance() {
_inheritedWidgets = _parent?._inheritedWidgets;#备注①
}
1.2 InheritedWidget Element Mount
void mount(Element? parent, Object? newSlot) {
...
_updateInheritance();
...
}
void _updateInheritance() {
final Map<Type, InheritedElement>? incomingWidgets = _parent?._inheritedWidgets;
if (incomingWidgets != null)
_inheritedWidgets = HashMap<Type, InheritedElement>.of(incomingWidgets);#备注②
else
_inheritedWidgets = HashMap<Type, InheritedElement>();
_inheritedWidgets![widget.runtimeType] = this;#备注③
}
结论:
- 1.通过备注①知道:普通Widget(不是
InheritedWidge
t) _inheritedWidgets属性值是通过继承传递下去的 - 2.通过备注②知道:
InheritedWidget
_inheritedWidgets属性值会深拷贝父节点传递过来的值 - 3.通过备注②③知道:当出现相同的
widget.runtimeType
,最近的InheritedWidget会覆盖祖先节点的数据 -
InheritedElement
随着_inheritedWidgets
传递给各个子孙节点,由于是同样的数据(没有相同的widget.runtimeType覆盖),就实现了数据共享
2.页面刷新
(Element Class)
void update(ProxyWidget newWidget) {①
...
updated(oldWidget);
...
}
(InheritedElement Class)
void updated(InheritedWidget oldWidget) {②
if ((widget as InheritedWidget).updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
(ProxyElement Class)
@protected
void updated(covariant ProxyWidget oldWidget) {③
notifyClients(oldWidget);
}
(ProxyElement Class)
@override
void notifyClients(InheritedWidget oldWidget) {④
for (final Element dependent in _dependents.keys) {
notifyDependent(oldWidget, dependent);
}
}
(InheritedElement Class)
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {⑦
dependent.didChangeDependencies();
}
(Element Class)
@mustCallSuper
void didChangeDependencies() {⑧
markNeedsBuild();
}
继承链 InheritedElement -> ProxyElement -> Element
参考如下代码:
class ShareDataWidget extends InheritedWidget {
const ShareDataWidget({
Key? key,
required this.data,
required Widget child,
}) : super(key: key, child: child);
final int data; //需要在子树中共享的数据,保存点击次数
//定义一个便捷方法,方便子树中的widget获取共享数据
static ShareDataWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
}
//该回调决定当data发生变化时,是否通知子树中依赖data的Widget重新build
@override
bool updateShouldNotify(ShareDataWidget old) {
return old.data != data;
}
}
class MyHomePage extends StatefulWidget {
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int count = 0;
@override
void didChangeDependencies() {
// TODO: implement didChangeDependencies
super.didChangeDependencies();
}
@override
Widget build(BuildContext context) {
return ShareDataWidget(
data: count,
child: GestureDetector(
onTap: () {
setState(() {
count++;
});
},
),
);
}
}
当我们调用setState 方法后,接下来会先调用方法①(这里的入口代码主要是根据Widget的更新原理,具体需要自己参考源码),接着会调用方法②,紧接着会调用方法③,这是根据继承链得来的
这里主要关注下②中的updateShouldNotify
,一般当数据发生改变时候,返回true,才会调用③,通知所有的监听Element,刷新他们,根据第④个方法知道,现在的问题是InheritedElement
中_dependents
是如何将数据填入的
(InheritedElement Class)
@protected
void setDependencies(Element dependent, Object? value) {
_dependents[dependent] = value;
}
根据上面的代码,现在只要知道是谁调用它就OK,答案就是dependOnInheritedWidgetOfExactType
,如上面的ShareDataWidget
中的代码
static ShareDataWidget? of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
}
根据函数调用链,得知:
@override
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {⑤
final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
if (ancestor != null) {
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {⑥
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies!.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget as InheritedWidget;
}
查找_inheritedWidgets
是否有对应类型的InheritedWidget
如果没有,直接返回 ,如果找到,根据代码⑥中的
ancestor.updateDependencies(this, aspect);
这个方法,会将当前的Element加入到InheritedElemnt
中的监听中
@protected
void updateDependencies(Element dependent, Object? aspect) {
setDependencies(dependent, null);
}
当InheritedWidget
需要执行update
的时候,就会通知_dependencies
中所有的Element执行刷新操作