flutter inheritedwidget(共享数据)
一、Flutter Sdk正是通过inheritedWidget实现在widget树中共享数据的(Theme和Locale当前语言环境)
1.inheritedWidget是Flutter中一个重要的功能型组件,它提供了一种数据在widget树中从上到下传递、共享的方式,比如我们在应用的根widget中通过InheritedWidget共享了一个数据,那么我们便可以在任意子widget中来获取该共享的数据
2.didChangeDependencies
在statefulwidget 的State中有一个didChangeDependencies方法,它指的正是组件是否依赖了父widget的inheritedwidget共享数据,如果inheritedwidget的数据发生变化,那么didChangeDependencies方法就会被调用,进而更新组件
示例:通过继承Inheritedwidget实现一个点击自增数据以清楚其原理
a:InheritedWidget 的updateShouldNotify方法,此方法决定是否调用State的didChangeDependencies方法,如果返回true则调用
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),
)
],
),
),
);
}
}
以上例子,如果没有依赖ShareDataWidget的数据,那么TestWidget的State 的didChangeDependencies是不会被调用的。
重点结论:InheritedWidget正是Flutter框架在widget树中实现共享数据,子widget依赖发生变化时让子组件实现更新的
3.应该在didChangeDependencies()中做什么?
一般来说,子widget很少会重写此方法,因为在依赖改变后framework也都会调用build()方法。但是,如果你需要在依赖改变后执行一些昂贵的操作,比如网络请求,这时最好的方式就是在此方法中执行,这样可以避免每次build()都执行这些昂贵操作。
4.dependOnInheritedWidgetOfExactType和getElementForInheritedWidgetOfExactType的区别
//定义一个便捷方法,方便子树中的widget获取共享数据
static ShareDataWidget 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;
}
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
查看代码可以发现dependOn方法比getElementFor多了一个注册到ancertor和dependOnInheritedElement,而dependOnInheritedElement方法中执行了ancestor.updateDependencies方法,正是这个方法让State的didChangeDependen方法被调用
二、Provide正是通过InheritedWidget实现的数据共享
这里可参考大神的跨组件状态共享加深理解:https://book.flutterchina.club/chapter7/provider.html
Flutter的Provide重要的有以下几个点:
1.ChangeNotifierProvider 变更通知提供者,也是订阅者 Comsumer基本都会在订阅者的子widget,因为订阅者需要依赖共享数据
2.Comsumer 消费者,就是消费共享数据的
3.ChangeNotifier 变更通知者,就是当依赖的model数据变化时,通知到各个监听者 这里往往需要共享的数据,某某Model 会extends继承ChangeNotifier
这里给个baseWidget 使用了Provide的示例,代码如下:
import 'package:business/base/base_viewmodel.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class BaseWidget<T extends ChangeNotifier> extends StatefulWidget {
final Widget Function(BuildContext context, T model, Widget child) builder;
final T model;
final Widget child;
final Function(T) onModelReady;
BaseWidget({
Key key,
this.builder,
this.model,
this.child,
this.onModelReady, loginService,
}) : super(key: key);
_BaseWidgetState<T> createState() => _BaseWidgetState<T>();
}
class _BaseWidgetState<T extends ChangeNotifier> extends State<BaseWidget<T>> {
T model;
@override
void initState() {
model = widget.model;
if (widget.onModelReady != null) {
widget.onModelReady(model);
}
super.initState();
}
@override
void dispose() {
if(model != null){
if(model is BaseViewModel){
BaseViewModel m = model as BaseViewModel;
m.isDisposed = true;
}
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>(
create: (context) => model,
child: Consumer<T>(
builder: widget.builder,
child: widget.child,
),
);
}
}
注意:这里有一点就是onModelReady的model参数就是BaseWidget的model域。同时builder 的构造,(context,model,child){} 的model也是和onModelReady是同一个model数据。因为下面这个
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>(
create: (context) => model,
child: Consumer<T>(
builder: widget.builder,
child: widget.child,
),
);
}
浙公网安备 33010602011771号