基于Provider状态管理用来实现MVVM框架,采用的是 ViewModel 继承于 ChangeNotifier,数据请求,数据处理在ViewModel中进行处理,然后再通过 ChangeNotifierProvider 提供给子Widget数据,ViewModel数据刷新通过调用 notifyListeners() 来通知Widget进行刷新,Widget 通过 Provider.of 、Consumer、Selector 来监听数据变化重新 build 从而达到更新UI的目的
1.文件结构
建对应项目的总文件目录,下面放api(存放数据请求),model(模型),viewModel(数据处理),widget(该项目公共或封装的控件),page(页面)
可以新建一个page文件,将页面都放在page文件里面
2.构建viewModel文件,继承于 ChangeNotifier,使用provider进行状态管理
在viewModel中声明要用到的属性,声明数据状态改变之后界面会刷新的方法,同时在里面进行网络请求管理,获取请求数据,在回调中处理对应的请求数据,在需要刷新界面的地方调用notifyListeners()方法
class FeedbackHomeViewModel extends ChangeNotifier{
FeedbackList processingList;
FeedbackList resolvedList;
int _processPage = 0;
int _resolvedPage = 0;
List typeList;
String customerServiceTel;
bool hasMore(int status) {
return status == STATUS_PROCESSING
? (_processPage * 10 < (processingList?.total ?? 0))
: (_resolvedPage * 10 < (resolvedList?.total ?? 0));
}
Future refresh(int status) {
status == STATUS_PROCESSING ? _processPage = 0 : _resolvedPage = 0;
return _fetchFeedbackList(status, pageNo: 1).then((_) {
status == STATUS_PROCESSING ? _processPage ++ : _resolvedPage ++;
});
}
Future loadMore(int status) {
var page = status == STATUS_PROCESSING ? _processPage : _resolvedPage;
return _fetchFeedbackList(status, pageNo: page + 1).then((_) {
status == STATUS_PROCESSING ? _processPage ++ : _resolvedPage ++;
});
}
Future _fetchFeedbackList(int status, {int pageNo = 1, int pageSize = 10}) {
return FeedbackApi.fetchFeedbackList(status, pageNo: pageNo, pageSize: pageSize)
.then((json) {
var resultList = FeedbackList.fromJson(json);
var feedbackList = status == STATUS_PROCESSING ? processingList : resolvedList;
if(pageNo > 1) {
resultList.data.insertAll(0, feedbackList.data);
}
status == STATUS_PROCESSING ? processingList = resultList : resolvedList = resultList;
notifyListeners();
}).catchError((error) {
print(error);
});
}
Future fetchHotLine() {
return FeedbackApi.fetchHotline().then((value) {
customerServiceTel = HotLine.fromJson(value).customerServiceTel;
notifyListeners();
}).catchError((error) => print(error));
}
void fetchFeedbackType() async {
return FeedbackApi.fetchFeedbackType()
.then((json) {
typeList = json.map((value) => FeedbackType.fromJson(value)).toList();
notifyListeners();
});
}
}
如果provider数据需要从page页面传入的时候,或者是本地数据需要进行修改的时候,同样声明私有,可以声明一个对应的方法,修改属性值。
class CounterModel with ChangeNotifier {
int _count = 0;
int get value => _count;
// // ///选中的执行人
TaskEmployeModel _taskSelectExecutor;
void increment() {
_count++;
notifyListeners();
}
TaskEmployeModel get taskSelectExecutor => _taskSelectExecutor;
set taskSelectExecutor(TaskEmployeModel value) {
_taskSelectExecutor = value;
notifyListeners();
}
}
3.page界面的使用
初始化并获取请求数据
final _viewModel = FeedbackHomeViewModel();
@override
void initState() {
super.initState();
_viewModel.fetchFeedbackType();
_viewModel.fetchHotLine();
_viewModel.refresh(STATUS_PROCESSING);
_viewModel.refresh(STATUS_RESOLVED);
}
在创建页面的时候,采用provider进行管理,create传入对应的viewmodel
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => _viewModel,
child: Scaffold(
body: Container()));
}
读状态的时候有Consumer和Selector模式,Consumer是监听provider中的所有数据,只要provider中的数据发生了改变,就会重新进行build,然后刷新界面;Selector可以更加细致化的监听provider中对应的具体的数据,只有当对应的具体数据发生改变的时候,才会重新build,刷新界面,这边推荐使用Selector来读取数据,更加细致化的管理,降低无效build次数
Consumer获取数据
Consumer(
builder: (_, provider, __) {
return Column(
children: [
Expanded(
child: ListView.builder(
itemCount: provider?.warehouseList?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
var brand = provider.warehouseList[index];
return ListTile(
onTap: () {
widget.onSelected(brand);
},
title: Text(brand.warehouseName),
);
}),
),
],
);
},
),
Selector读取数据
其中Selector中的<>需要指定对应的provider和监听的数据类型,否则会报错
Selector(
builder: (_, value, __) => _buildTaskType(context),
selector: (_, taskCreateProvider) =>
taskCreateProvider.taskSelectType),
修改状态
FloatingActionButton(
onPressed: () =>
Provider.of(context, listen: false).increment(),
),
onRefresh: () async {
Provider.of(context, listen: false)
.refresh(status);
},
多个provider的时候,在build初始化的时候,可以选择使用MultiProvider进行管理操作
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => model1,
),
ChangeNotifierProvider(
create: (context) => model2,
)
],
child: Consumer2(
builder: widget.builder,
child: widget.child,
));
}
}
其中model1 和model2 是对应的provider管理,Consumer2和Selector2,支持两个provider,最多能支持到6个
这样使用了MVVM模式,在page页面只要使用对应的数据即可,在viewModel中将对应的数据处理好,或者提供改变数据状态的方法,是的可以更好的管理数据状态,更加的清晰易读,在复杂的场景的可以更好的处理逻辑