记InheritedWidget使用思考
InheritedWidget 是项目中必不可少的组件,用户数据共享。老生常谈的
Provider
框架也是基于InheritedWidget
实现的
简介
InheritedWidget
组件是功能性组局,实现了由上向下共享数据的功能。即子组件通过BuildContext.dependOnInheritedWidgetOfExactType
方法从父组件获取数据。
值得提一下,这种由上向下提供书共享数据的方式和Notification
传递方向正好相反。两者相同点是:都是由子组件发起的。InheritedWidget
是又子组件通过content向树上方查找数据,Notification
是由子组件向上发起通知。
特点
- 子组件通过
[BuildContext.dependOnInheritedWidgetOfExactType]
方法查找对应最近的InheritedWidget
并获取数据。 InheritedWidget
根据习惯或者协议,提供of
静态方法,便于子组件获取数据。该方法并非一定返回该继承的widget,也可以返回其他数据。- 调用
of
方法的组件,必须要在InheritedWidget
内。如果在同一个widget中使用,可以通过Builder
将需要使用的组件包裹起来。
作用
在使用中我们发现,InheritedWidget
作用是父组件提供数据,子组件可以由下往上查找对应的共享数据,这个是它直观的作用。
常用的错误更新数据方式
**setState
**: 我们发现网上的有很多文章在介绍InheritedWidget
的使用,并更新数据。但是这个只是体现了该组件的作用。绝大多数是在InheritedWidget
组件中通过final
申明共享数据。然后在父组件中通过setState
强制刷新父组件,同时也刷新了InheritedWidget
组件,当然其多层子组件都会跟着一起rebuild
,造成了很大的非必要消耗。
如何去优化?
InheritedWidget
提供了数据的共享,避免了数据由上到下层层申明传递。我们使用数据的地方也很明确。接下来要做的是,数据刷新后,让指定组件去更新。这个时候我们就想到了ValueNotifier
:**InheritedWidget
在共享数据的时候通过ValueNotifier
包裹,子组件通过of
方法获取的数据类型也是ValueNotifier
,在使用的地方通过ValueListenableBuilder
实现**。
进一步思考
在【老孟Flutter】源码分析系列之InheritedWidget一文中提到了,修改of
方法:
dependOnInheritedWidgetOfExactType
getElementForInheritedWidgetOfExactType
().widget 两者的区别:调用dependOnInheritedWidgetOfExactType() 和 getElementForInheritedWidgetOfExactType()的区别就是前者会注册依赖关系,而后者不会,所以在调用dependOnInheritedWidgetOfExactType()时,InheritedWidget和依赖它的子孙组件关系便完成了注册,之后当InheritedWidget发生变化时,就会更新依赖它的子孙组件,也就是会调这些子孙组件的didChangeDependencies()方法和build()方法。而当调用的是 getElementForInheritedWidgetOfExactType()时,由于没有注册依赖关系,所以之后当InheritedWidget发生变化时,就不会更新相应的子孙Widget。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23@override
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>(
{Object? aspect}) {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement? ancestor =
_inheritedWidgets == null ? null : _inheritedWidgets![T];
// 多出部分
if (ancestor != null) {
assert(ancestor is InheritedElement);
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
@override
InheritedElement?
getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement? ancestor =
_inheritedWidgets == null ? null : _inheritedWidgets![T];
return ancestor;
}
很明显修改为getElementForInheritedWidgetOfExactType<T>().widget
,就会少一个非必要注册,也算是好事。
进一步思考产生疑问
既然通过了ValueNotifier
和InheritedWidget
结合,我们通过修改ValueNotifier
的value来改变页面时,InheritedWidget
组件没有重新构建并不会触发updateShouldNotify
,也就是在使用数据的子组件中didChangeDependencies
方法也不会因为value的改变而调用。
所以of
方法具体用什么去实现,取决于具体业务设计。
文中的图片均借用于【老孟Flutter】源码分析系列之InheritedWidget