写在前面
InheritedWidget
是一个用于数据共享的组件,它的数据传输方向是从上到下,所以当我们把数据放在它里面,当有数据变更的时候,依赖它的那些子 Widget 都可以获得数据的变更。
内容
abstract class InheritedWidget extends ProxyWidget {
const InheritedWidget({ Key? key, required Widget child })
: super(key: key, child: child);
@override
InheritedElement createElement() => InheritedElement(this);
@protected
bool updateShouldNotify(covariant InheritedWidget oldWidget);
}
updateShouldNotify()
方法用于让 Framework 判断是否去提醒那些有依赖于这个InheritedWidget
的 Widget。
InheritedWidget
是一个抽象类,意味着我们必须去实现它,这里以《Flutter 实战》一书里的例子 7.2 数据共享(InheritedWidget) 作为表示:
class ShareDataWidget extends InheritedWidget {
ShareDataWidget({
@required this.data,
Widget child
}) :super(child: child);
final int data; //需要在子树中共享的数据,保存点击次数
//定义一个便捷方法,方便子树中的widget获取共享数据
static ShareDataWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
}
//该回调决定当data发生变化时,是否通知子树中依赖data的Widget
@override
bool updateShouldNotify(ShareDataWidget old) {
//如果返回true,则子树中依赖(build函数中有调用)本widget
//的子widget的`state.didChangeDependencies`会被调用
return old.data != data;
}
}
class _TestWidget extends StatefulWidget {
@override
__TestWidgetState createState() => new __TestWidgetState();
}
class __TestWidgetState extends State<_TestWidget> {
@override
Widget build(BuildContext context) {
//使用InheritedWidget中的共享数据
return Text(ShareDataWidget
.of(context)
.data
.toString());
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
//如果build中没有依赖InheritedWidget,则此回调不会被调用。
print("Dependencies change");
}
}
class InheritedWidgetTestRoute extends StatefulWidget {
@override
_InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState();
}
class _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> {
int count = 0;
@override
Widget build(BuildContext context) {
return Center(
child: ShareDataWidget( //使用ShareDataWidget
data: count,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: _TestWidget(),//子widget中依赖ShareDataWidget
),
RaisedButton(
child: Text("Increment"),
//每点击一次,将count自增,然后重新build,ShareDataWidget的data将被更新
onPressed: () => setState(() => ++count),
)
],
),
),
);
}
}
当我们点击按钮,然后调用setState()
的方法,在下一帧来临的时候,就会调用InheritedWidgetTestRoute
进行 rebuild,然后去递归调用其 child 的update()
方法。
由于继承关系:
会先调用ProxyElement
的update()
方法,里面调用了updated()
方法:
abstract class ProxyElement extends ComponentElement {
@override
void update(ProxyWidget newWidget) {
final ProxyWidget oldWidget = widget;
assert(widget != null);
assert(widget != newWidget);
super.update(newWidget);
assert(widget == newWidget);
updated(oldWidget);
_dirty = true;
rebuild();
}
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
}
InheritedElement
重写了updated()
方法,在这里就用到了我们的updateShouldNotify()
方法,整个流程下来可以知道就是遍历一个Map类型的_dependents
,把里面的依赖的Element
调用它们的didChangeDependencies
方法。
class InheritedElement extends ProxyElement {
final Map<Element, Object?> _dependents = HashMap<Element, Object?>();
...
@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();
}
}
那么是在哪里添加这些依赖者的呢?
abstract class Element extends DiagnosticableTree implements BuildContext {
...
@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 }) {
...
_dependencies ??= HashSet<InheritedElement>();
_dependencies!.add(ancestor);
// 这里的 this 就是当前 Element 实例
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
}
我们在Element
类里可以看到,在我们前面调用的dependOnInheritedWidgetOfExactType()
方法里,会调用到dependOnInheritedElement()
方法,里面会调用到我们当前ShareDataWidget
所对应的InheritedElement
的updateDependencies()
方法。
class InheritedElement extends ProxyElement {
...
@protected
void updateDependencies(Element dependent, Object? aspect) {
setDependencies(dependent, null);
}
@protected
void setDependencies(Element dependent, Object? value) {
_dependents[dependent] = value;
}
}
也就是说当我们某个 Widget 通过调用ShareDataWidget
的of
静态方法的时候,就把自己给注册进去了。这样后续的更新就可以通知到了。
那么使用dependOnInheritedWidgetOfExactType()
方法我们可以看到由于这样会注册依赖关系,所以当数据有更新的时候,这些依赖关系的子 Widget 都会被调用到didChangeDependencies()
方法,然后造成它们都需要进行build()
。
如果我们只想要ShareDataWidget
的数据,而不想要didChangeDependencies()
被调用,那么就要用另外一个方法getElementForInheritedWidgetOfExactType<T extends InheritedWidget>()
,把ShareDataWidget
的of
方法改写如下:
static ShareDataWidget of(BuildContext context) {
//return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
return context.getElementForInheritedWidgetOfExactType<ShareDataWidget>().widget;
}
abstract class Element extends DiagnosticableTree implements BuildContext {
...
@override
InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
...
final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
return ancestor;
}
...
}
getElementForInheritedWidgetOfExactType<T extends InheritedWidget>()
方法跟dependOnInheritedWidgetOfExactType()
的区别就是少了那一层注册关系。
总结
- 实现
InheritedWidget
,在updateShouldNotify()
方法里写通过什么条件会触发提醒 - 通过
context.dependOnInheritedWidgetOfExactType()
方法,既让子 Child 可以访问到数据,同时也起到了把子 Child 自己的 Element 注册到InheritedWidget
里。 - 当数据变更,触发到
InheritedWidget
实现类的update()
方法的时候,它会遍历之前注册进来的 Element 列表,然后调用它们的didChangeDependencies()
方法,接着 Framework 会调用build
方法。