
接上一篇文章,全局管理状态的provider包可以简单通过InheritedWidget来实现,不过在此之前,需要先认识一下InheritedWidget。
一. InheritedWidget
InheritedWidget有个很重要的特性,它提供了一种方式,使得数据可以在widget树中从上到下传递、共享,比如在应用的根widget中通过InheritedWidget共享了一个数据,那么我们便可以在任意子widget中来获取该共享的数据!
在之前介绍State的生命周期时,State对象有一个didChangeDependencies回调,它会在“依赖”发生变化时被Flutter Framework调用。而这个“依赖”指的就是子widget是否使用了父widget中InheritedWidget的数据!如果使用了,则代表子widget依赖有依赖InheritedWidget;如果没有使用则代表没有依赖。这种机制可以使子组件在所依赖的InheritedWidget变化时来更新自身!
接下来,举个例子来说明一下。
- 先创建个继承InheritedWidget的类DataWidget:
class DataWidget extends InheritedWidget {
DataWidget({
@required this.data,
Widget child
}) :super(child: child);
final int data;
static DataWidget of(BuildContext context) {
return context.inheritFromWidgetOfExactType(DataWidget);
}
//该回调决定当data发生变化时,是否通知子树中依赖data的Widget
@override
bool updateShouldNotify(DataWidget old) {
//true: 子树中依赖本widget,从而子widget的`state.didChangeDependencies`会被调用
return old.data != data;
}
}
- 然后实现一个组件_TestWidget,在其build方法中使用DataWidget中的数据。
class _TestWidget extends StatefulWidget {
@override
_TestWidgetState createState() => new _TestWidgetState();
}
class _TestWidgetState extends State<_TestWidget> {
@override
Widget build(BuildContext context) {
//使用InheritedWidget中的共享数据
return Text(DataWidget
.of(context)
.data
.toString());
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
//父widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
//如果build中没有依赖InheritedWidget,则此回调不会被调用。
print("Dependencies change");
}
}
- 再在另一个组件中使用DataWidget,并使_TestWidget成为DataWidget的子组件。
class TestRoute extends StatefulWidget {
@override
_TestRouteState createState() => new _TestRouteState();
}
class _TestRouteState extends State<TestRoute> {
int count = 0;
@override
Widget build(BuildContext context) {
return Center(
child: DataWidget(
data: count,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: _TestWidget(),//子widget中依赖DataWidget
),
RaisedButton(
child: Text("Increment"),
//每次点击,count自增,然后重新build, DataWidget的data将被更新
onPressed: () => setState(() => ++count),
)
],
),
),
);
}
}
上面例子就产生了依赖,子组件_TestWidget 的didChangeDependencies()会被调用。如果_TestWidget的build方法中没有使用ShareDataWidget的数据,那它就没有依赖ShareDataWidget,它的didChangeDependencies()也将不会被调用。
如果不想在DataWidget发生变化时调用__TestWidgetState的didChangeDependencies()方法,我们可以把inheritFromWidgetOfExactType()方法换成ancestorInheritedElementForWidgetOfExactType():
static DataWidget of(BuildContext context) {
//return context.inheritFromWidgetOfExactType(ShareDataWidget);
return context.ancestorInheritedElementForWidgetOfExactType(ShareDataWidget).widget;
}
这两个方法有什么区别呢?看源码:
@override
InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType) {
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
return ancestor;
}
@override
InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
//多出的部分
if (ancestor != null) {
assert(ancestor is InheritedElement);
return inheritFromElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
可以看出两者的区别是:inheritFromWidgetOfExactType() 多调了inheritFromElement方法,该方法实现如下:
@override
InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
//注册依赖关系
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
从该方法实现中,可以看出,ancestor被加到依赖数组_dependencies中,从而注册了依赖关系,也就是说使用ancestorInheritedElementForWidgetOfExactType方法不会有依赖,自然就不会调用__TestWidgetState的didChangeDependencies()方法了。
二. 使用InheritedWidget实现Provider
上一篇文章中说到,可以使用Provider来实现全局状态管理,它 是基于InheritedWidget来实现状态共享的。那接下来简单使用InheritedWidget实现一个Provider。
网友评论