介绍一下数据与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<T>(context)
4.1.1 通过 addListener 监听数据改变
当数据改变并且调用 notifyListeners() 这里会收到回调
model.addListener(() {
//Do something
});
4.1.2 通过 Provider.of<T>(context) 方式监听
这里看到在build
方法中我们获取Provider
传入的Model
.并没有调用addListener
.这里解释一下.
通过查看 Provider.of
源码中知道, Provider.of
通过 context 将本次构建的build方法绑定,并监听数据改变. 所以在数据变化后将自动调用 build 方法进行刷新.
@override
Widget build(BuildContext context) {
final model = Provider.of<TestClass>(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<TestClass>(
create: (context)=>TestClass(),
child: MaterialApp(
theme: ThemeData.dark(),
home: FirstScreen(),
),
);
}
}
class FirstScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final model = Provider.of<TestClass>(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<TestClass>(
create: (context) => source,
child: Consumer<TestClass>(
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<T extends ChangeNotifier> 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<T>(
Function(BuildContext context, T value, Widget child) builder) {
return builder;
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>.value(
value: source,
child: Consumer<T>(
child: child,
builder: (context, model, child) {
return builder(context, model, child);
},
),
);
}
}
6.2 封装后使用对比
封装前
ChangeNotifierProvider<TestClass>(
create: (context) => source,
child: Consumer<TestClass>(
child: Container(),
builder: (context, model, child) {
return Container(
child: Center(child: Text("${model.count}",style: TextStyle(fontSize: 50),),),
);
},
),
),
封装后
RefreshWidget<TestClass>(
(context, value, child) => Container(
child: Center(
child: Text(
"${value.count}",
style: TextStyle(fontSize: 50),
),
),
),
source: source),
封装后我们只需要关心下面三点,对与新手比较友好,不用纠结太多
- builder构建方法(自动补全)
- source数据model
- <T> model 类型
直接使用builder方法没有方法提示, 这点很不友好.
封装后将builder放到首位, 会将builder方法直接补全, 这才是我最终的目的, 可以有效减少control+c/v的次数
代码很少直接贴完整Demo出来
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class TestClass with ChangeNotifier {
var count = 1;
action() {
++count;
notifyListeners();
}
}
class MyWidget extends StatelessWidget {
var _value = TestClass();
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
child:
Text('Hello, World! Click here~~~', style: Theme.of(context).textTheme.headline4),
onTap: _value.action,
),
RefreshWidget<TestClass>(
RefreshWidget.b((context, value, child) => Container(
child: Center(
child: Text(
"${value.count}",
style: TextStyle(fontSize: 50),
),
),
)),
source: _value)
]);
}
}
class RefreshWidget<T extends ChangeNotifier> 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 b<T>(
Function(BuildContext context, T value, Widget child) builder) {
return builder;
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<T>.value(
value: source,
child: Consumer<T>(
child: child,
builder: (context, model, child) {
return builder(context, model, child);
},
),
);
}
}