InheritedWidget提供了一种数据在widget树中从上到下传递、共享的方式 ,
简而言之
InheritedWidget 中暴露出来的数据能有效地向下(子widget)传播(和共享)信息
如Flutter SDK中正是通过InheritedWidget来共享应用主题(Theme
)和Locale (当前语言环境)信息的。
InheritedWidget
abstract class InheritedWidget extends ProxyWidget
为了使用 先定义一个子类InheritedProvider
class InheritedProvider<T> extends InheritedWidget{
final T data;//需要在子树中共享的数据
InheritedProvider({
this.data,
Widget child,
}) :super(child: child);
//定义一个便捷方法,方便子树中的widget获取共享数据
static InheritedProvider of<T>(BuildContext context){
return context.inheritFromWidgetOfExactType(InheritedProvider);
}
//该回调决定当data发生变化时,是否通知子树中依赖data的Widget
@override
bool updateShouldNotify(InheritedProvider<T> oldWidget) {
//如果返回true,则子树中依赖(build函数中有调用)本widget
//的子widget的`state.didChangeDependencies`会被调用
return oldWidget.data != data;
}
}
InheritedProvider类的目的是为它的所有子widget提供数据
InheritedWidget
在widget树中数据传递方向是从上到下的,并且可以跨级传递
访问inhertedProvider中的数据
final InheritedProvider inheritedWidget = InheritedProvider.of(context);
return new Container(
color: inheritedWidget.data.color,
);
didChangeDependencies
State
对象有一个didChangeDependencies
回调,它会在“依赖”发生变化时被Flutter Framework调用。而这个“依赖”指的就是子widget是否使用了父widget中InheritedWidget
的数据!如果使用了,则代表子widget依赖有依赖InheritedWidget
;如果没有使用则代表没有依赖。这种机制可以使子组件在所依赖的InheritedWidget
变化时来更新自身!比如当主题、locale(语言)等发生变化时,依赖其的子widget的didChangeDependencies
方法将会被调用。
如果我们只想在子widget中引用InheritedProvider数据,但却不希望在InheritedProvider发生变化时调用子Widget的didChangeDependencies()
方法应该怎么办?其实答案很简单,我们只需要将InheritedProvider.of()
的实现改一下即可:
//定义一个便捷方法,方便子树中的widget获取共享数据
static InheritedProvider of(BuildContext context) {
//return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
return context.getElementForInheritedWidgetOfExactType<ShareDataWidget>().widget;
}
@override
InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
assert(_debugCheckStateIsActiveForAncestorLookup());
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
return ancestor;
}
@override
InheritedWidget dependOnInheritedWidgetOfExactType({ 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;
}
我们可以看到,dependOnInheritedWidgetOfExactType()
比 getElementForInheritedWidgetOfExactType()
多调了dependOnInheritedElement
方法,dependOnInheritedElement
源码如下:
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
调用dependOnInheritedWidgetOfExactType()
和 getElementForInheritedWidgetOfExactType()
的区别就是前者会注册依赖关系,而后者不会,所以在调用dependOnInheritedWidgetOfExactType()
时,InheritedWidget
和依赖它的子孙组件关系便完成了注册,之后当InheritedWidget
发生变化时,就会更新依赖它的子孙组件,也就是会调这些子孙组件的didChangeDependencies()
方法和build()
方法。而当调用的是 getElementForInheritedWidgetOfExactType()
时,由于没有注册依赖关系,所以之后当InheritedWidget
发生变化时,就不会更新相应的子孙Widget。
点击"Increment"按钮后,会调用界面state的setState()
方法,此时会重新构建整个页面,由于示例中, 并没有任何缓存,所以它也都会被重新构建,所以也会调用build()
方法。