官方刷新框架provider及其实现原理。
- Provider:实现数据改变时,对应的局部widget自动刷新。
- 响应式的实现。解决InheritedWidget由上而下的传递方式,实现model改变,widget自动刷新
provider的使用
- 定义对象并继承ChangeNotifier,在变化的时候发送通知方法notifyListeners:
class OrgInfo extends ChangeNotifier {
List<OrgModel> orgs;
OrgModel curorg;
OrgInfo({this.orgs, this.curorg});
void setOrgInfo(List<OrgModel> orgsParams, OrgModel curorgParams) {
orgs = orgsParams;
curorg = curorgParams;
notifyListeners();
}
//当被剔除组织时,移除列表数据
void removeOrgInfo(String orgId) {
for (var orgModel in orgs) {
if (orgModel.orgId == orgId) {
orgs.remove(orgModel);
break;
}
}
notifyListeners();
}
void addOrgModel(OrgModel orgModel) {
orgs.add(orgModel);
notifyListeners();
}
}
- 在app初始化时注册model:
class _MyAppState extends State<MyApp> {
...
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: UserManager.instance.orgInfo),
],
child: MaterialApp(
...);
}
}
- 在需要的自动局部的刷新使用Consumer,控件精确的越细越好:
Widget build(BuildContext context) {
return Column(
children: [
Expanded(
child: MediaQuery.removePadding(
removeTop: true,
context: context,
child: Consumer<OrgInfo>(
builder: (context, OrgInfo orgInfo, _) => ListView.builder(
shrinkWrap: true,
itemBuilder: (BuildContext context, int index) {
if (index == 0) {
return orgHeader(
orgInfo.orgs[widget.selectIndex].orgName);
}
return cell(index - 1);
},
itemCount: orgInfo.orgs.length + 1,
),
))),
SizedBox(
height: 200,
)
],
);
}
原理
其中model对象继承ChangeNotifier,被观察者对象,当model对象改变时,调用notifyListeners通知观察者刷新。
通知如何与widget结合实现自动刷新。
InheritedWidget
参考文章:
数据共享(InheritedWidget)
关于跨组件传递数据,你只需要记住这三招
案例:点击按钮,刷新子widget计数
如果不使用InheritedWidget:正常做法定义一个count变量,然后当参数传递到子widget。
如果使用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),
)
],
),
),
);
}
}
- ShareDataWidget继承InheritedWidget的模型对象,_TestWidget是InheritedWidgetTestRoute的子类。点击父类中的按钮,_TestWidget数据自动刷新。
小结:
- 在父widget中state构造ShareDataWidget,子widget的state可以ShareDataWidget.of(context).data获取数据,避免了数据传递。
- 数据自上而下传递。
- setState也能刷新子widget。使用ShareDataWidget继承InheritedWidget的模型对象,子类的didChangeDependencies也会刷新。
- dependOnInheritedWidgetOfExactType 子state会调用didChangeDependencies;getElementForInheritedWidgetOfExactType 子state不会调用didChangeDependencies。
缺点
- setState会刷新所有widget
- InheritedWidget也会全部刷新
Provider
InheritedProvider
// 一个通用的InheritedWidget,保存任需要跨组件共享的状态
class InheritedProvider<T> extends InheritedWidget {
InheritedProvider({@required this.data, Widget child}) : super(child: child);
//共享状态使用泛型
final T data;
@override
bool updateShouldNotify(InheritedProvider<T> old) {
//在此简单返回true,则每次更新都会调用依赖其的子孙节点的`didChangeDependencies`。
return true;
}
}
InheritedProvider是继承InheritedWidget对象,dependOnInheritedWidgetOfExactType时会刷新子类的didChangeDependencies。
ChangeNotifier实现
class ChangeNotifier implements Listenable {
List listeners=[];
@override
void addListener(VoidCallback listener) {
//添加监听器
listeners.add(listener);
}
@override
void removeListener(VoidCallback listener) {
//移除监听器
listeners.remove(listener);
}
void notifyListeners() {
//通知所有监听器,触发监听器回调
listeners.forEach((item)=>item());
}
... //省略无关代码
}
ChangeNotifier把方法添加到数组中,notifyListeners遍历调用。观察者实现。
ChangeNotifierProvider
class ChangeNotifierProvider<T extends ChangeNotifier> extends StatefulWidget {
ChangeNotifierProvider({
Key key,
this.data,
this.child,
});
final Widget child;
final T data;
//定义一个便捷方法,方便子树中的widget获取共享数据
static T of<T>(BuildContext context) {
final type = _typeOf<InheritedProvider<T>>();
final provider = context.dependOnInheritedWidgetOfExactType<InheritedProvider<T>>();
return provider.data;
}
@override
_ChangeNotifierProviderState<T> createState() => _ChangeNotifierProviderState<T>();
}
class _ChangeNotifierProviderState<T extends ChangeNotifier> extends State<ChangeNotifierProvider<T>> {
void update() {
//如果数据发生变化(model类调用了notifyListeners),重新构建InheritedProvider
setState(() => {});
}
@override
void didUpdateWidget(ChangeNotifierProvider<T> oldWidget) {
//当Provider更新时,如果新旧数据不"==",则解绑旧数据监听,同时添加新数据监听
if (widget.data != oldWidget.data) {
oldWidget.data.removeListener(update);
widget.data.addListener(update);
}
super.didUpdateWidget(oldWidget);
}
@override
void initState() {
// 给model添加监听器
widget.data.addListener(update);
super.initState();
}
@override
void dispose() {
// 移除model的监听器
widget.data.removeListener(update);
super.dispose();
}
@override
Widget build(BuildContext context) {
return InheritedProvider<T>(
data: widget.data,
child: widget.child,
);
}
}
- 泛型T表示数据InheritedProvider data,继承自ChangeNotifier。被观察者
- ChangeNotifierProvider data的观察者,T data监听update,当数据发生变化时,update方法调用setState,创建InheritedProvider,刷新child。
- 实现InheritedProvider data与widget child的绑定。
data与UI的关系
data是被观察者,ChangeNotifierProvider是data的观察者;data改变时,ChangeNotifierProvider监听到变化,调用setState,构建InheritedProvider刷新UI。实现data与UI的绑定。
总结
- setState能刷新widget子树,刷新范围太大,并且需要把数据对象传递到子类。
- InheritedWidget不用传递数据对象,通过context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();获取父类的数据。但是刷新范围大,只能由上而下传递。
-
Provider可以实现局部刷新。只要数据对象改变,UI能自动变化,实现响应式编程。屏蔽刷新逻辑,实现响应式数据与UI的绑定。无论是子类或父类改变数据都能刷新绑定的UI。
参考文章: