介绍一下数据与Widget之间传递,数据改变-->触发Widget刷新
1.0 所需工具
Provider
2.0 Provider
Provider 会在Widget和Model之间建立监听,以确保数据发生变化时刷新Widget
3.0 必要步骤:
Model 要继承ChangeNotifier
Widget要监听Model数据变化
Provider 是flutter推荐使用的状态管理工具,具体原理这里不展开分析,下面介绍一下如何快速使用,达到一个真实业务的场景.
4.0 Model创建
// 重要: model必须继承ChangeNotifier
class TestClass with ChangeNotifier {
var count = 1;
action() {
++count;
//数据变化后腰通过 notifyListeners() 通知所有监听者
notifyListeners();
}
}
4.1 监听Model
Model监听有两种方式
通过addListener方式
通过Provider.of(context)
4.1.1 通过 addListener 监听数据改变
当数据改变并且调用 notifyListeners() 这里会收到回调
model.addListener(() {
//Do something
});
4.1.2 通过 Provider.of(context) 方式监听
这里看到在build方法中我们获取Provider传入的Model.并没有调用addListener.这里解释一下.
通过查看 Provider.of 源码中知道, Provider.of通过 context 将本次构建的build方法绑定,并监听数据改变. 所以在数据变化后将自动调用 build 方法进行刷新.
@override
Widget build(BuildContext context) {
final model = Provider.of(context);
return Container()
}
5.1 向下传递完整示例
下面将一个Model向下传递的完整实例贴出来.
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class TestClass with ChangeNotifier {
var count = 1;
action() {
++count;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context)=>TestClass(),
child: MaterialApp(
theme: ThemeData.dark(),
home: FirstScreen(),
),
);
}
}
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final model = Provider.of(context);
return Scaffold(
appBar: AppBar(
title: Text('Provider'),
),
body: Container(
child: Center(child: Text("${model.count}",style: TextStyle(fontSize: 50),),),
),
floatingActionButton: FloatingActionButton(
onPressed: () => model.action(),
child: Icon(Icons.add),
),
);
}
}
5.2 组件内部通过Model控制数据刷新 完整示例
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class TestClass with ChangeNotifier {
var count = 1;
action() {
++count;
notifyListeners();
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark(),
home: FirstScreen(),
);
}
}
class FirstScreen extends StatelessWidget {
final TestClass source=TestClass();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Provider'),
),
body:ChangeNotifierProvider(
create: (context) => source,
child: Consumer(
child: Container(),
builder: (context, model, child) {
return Container(
child: Center(child: Text("${model.count}",style: TextStyle(fontSize: 50),),),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => source.action(),
child: Icon(Icons.add),
),
);
}
}
6.0 Provider封装
由5.2 数据内部控制刷新示例看出,实际代码写起来比较麻烦.所以封装了一下. 只需要传入必要参数就可以.
6.1 封装Widget
class RefreshWidget extends StatelessWidget {
RefreshWidget(
this.builder, {
@required this.source,
this.child,
});
final T source;
final Function(BuildContext context, T value, Widget child) builder;
final Widget child;
static Builder(
Function(BuildContext context, T value, Widget child) builder) {
return builder;
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(
value: source,
child: Consumer(
child: child,
builder: (context, model, child) {
return builder(context, model, child);
},
),
);
}
}
6.2 封装后使用对比
封装前
ChangeNotifierProvider(
create: (context) => source,
child: Consumer(
child: Container(),
builder: (context, model, child) {
return Container(
child: Center(child: Text("${model.count}",style: TextStyle(fontSize: 50),),),
);
},
),
),
封装后
RefreshWidget(
(context, value, child) => Container(
child: Center(
child: Text(
"${value.count}",
style: TextStyle(fontSize: 50),
),
),
),
source: source),
封装后我们只需要关心下面三点,对与新手比较友好,不用纠结太多
builder构建方法(自动补全)
source数据model
model 类型
直接使用builder方法没有方法提示, 这点很不友好.
封装后将builder放到首位, 会将builder方法直接补全, 这才是我最终的目的, 可以有效减少control+c/v的次数