概述
通过上一章节我们对Provider有了一个基本的了解,这一章节我们来说说Provider的8种提供者以及他们的使用区别,并结合MVVM模式应用到实际的项目中。
MVVM简介
MVVM架构分为M(Model)、V(View)、VM(ViewModel)三个部分,他们分别处理自己的分工,在View和Model之间使用ViewModel作为中介者,使View和Model不受业务逻辑影响。
- Model: 模型层,处理Api数据、模型相关业务
- View: 视图层,UI呈现、使用者互动等。
- ViewModel: 视图模型,处理逻辑、将数据绑定给View展示。
- 原理
当界面需要展示数据时,View跟ViewModel绑定,ViewModel向Model取得数据后,在ViewModel处理对应的业务逻辑,然后将数据处理,最后通过View更新并展示。 - 优点
1). 易于变更需求,降低耦合
2).权责分工明确
3).方便测试 - 缺点
1).文件数量增加
2).bug定位较为不易
3).数据绑定消耗资源
1.Provider
Provider是最基本的Provider组件,可以使用它为组件树中的任何位置提供值,但是当该值更改的时候,它并不会更新UI。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class StudentModel{
int score = 0;
void changeScore(){
score = 100;
}
}
class ProviderDemo extends StatefulWidget{
const ProviderDemo({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return ProviderDemoState();
}
}
class ProviderDemoState extends State<ProviderDemo>{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Provider(
create:(_) => StudentModel(),
child: Scaffold(
appBar: AppBar(title: const Text('ProviderDemo'),),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Consumer<StudentModel>(
builder: (_, userModel, child) {
return Text(userModel.score.toString(),
style: const TextStyle(
color: Colors.red,
fontSize: 30
)
);
},
),
Consumer<StudentModel>(
builder: (_, userModel, child) {
return Padding(
padding: const EdgeInsets.all(20),
child: ElevatedButton(
onPressed: (){
userModel.changeScore();
},
child: const Text("change score"),
),
);
},
),
],
),
),
),
);
}
}
2.ChangeNotifierProvider
它跟Provider组件不同,ChangeNotifierProvider会监听模型对象的变化,而且当数据改变时,它也会重建Consumer(消费者)(可自行查看ChangeNotifierProvider)。
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wyl_flutter/ProviderDemo/collection_goods_page.dart';
import 'package:wyl_flutter/ProviderDemo/goods_model.dart';
import 'package:wyl_flutter/ProviderDemo/goods_view_model.dart';
class ChangeNotifierProviderDemo extends StatefulWidget{
const ChangeNotifierProviderDemo({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return ChangeNotifierProviderDemoState();
}
}
class ChangeNotifierProviderDemoState extends State<ChangeNotifierProviderDemo>{
@override
Widget build(BuildContext context) {
// TODO: implement build
return ChangeNotifierProvider(
create: (_) => GoodsViewModel(),
child: Scaffold(
appBar: AppBar(title: const Text('ChangeNotifierProviderDemo'),actions: [
// GestureDetector(
// child: const Icon(Icons.more_horiz),
// onTap: (){
// Navigator.push(context, MaterialPageRoute(builder: (context)=> const CollectionGoodsPage()) );
// //CollectionGoodsPage
// },
// )
],),
body: Consumer<GoodsViewModel>(
builder: (_,vm,child){
if (vm.loading) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemBuilder: (ctx,index){
GoodsModel m = vm.dataList[index];
return Row(
children: [
Padding(
padding: const EdgeInsets.all(15),
child: CachedNetworkImage(
imageUrl: m.img!,
width: 100,
),
),
Padding(
padding: const EdgeInsets.all(15),
child: Text(m.title!,),
),
],
);
},
itemCount: vm.dataList.length,
);
},
),
),
);
}
}
3.FutureProvider
简单来说,FutureProvider用于提供在组件树中准备好使用其值时可能尚未准备好的值,主要是确保空值不会传递给任何子组件,而且FutureProvider有一个初始值,子组件可以使用该Future值并告诉子组件使用新的值来进行重建。
- 注意:
1).FutureProvider只会重建一次
2).默认显示初始值
3).然后显示Future值
4).最后不会再次重建
4.StreamProvider
StreamProvider提供流值,是围绕StreamBuilder,所提供的值会在传入的时候替换掉新值。和FutureProvider一样,主要的区别在于值会根据多次触发重新构建UI。
5.MultiProvider
在上面的例子中我们都只是返回了一个提供者,在实际开发过程中肯定会有多个提供者,我们虽然可以采用嵌套的方式来解决,但是这样无疑是混乱的,可读性级差。这个时候强大的MultiProvder就产生了.
class NameModel with ChangeNotifier{
String? name = '张三';
Future<void> changeName() async {
name = "李四";
notifyListeners();
}
}
class HeightModel with ChangeNotifier{
double? height = 170;
Future<void> changeName() async {
height = 180;
notifyListeners();
}
}
- 嵌套设置
return ChangeNotifierProvider<UserModel1>(
create: (_) => NameModel(),
child: ChangeNotifierProvider<UserModel4>(
create: (_) => HeightModel(),
child: MutiProviderDemo(),
),
);
- 使用MultiProvider
return MultiProvider(
providers: [
ChangeNotifierProvider< NameModel >(create: (_) => NameModel()),
ChangeNotifierProvider< HeightModel >(create: (_) => HeightModel()),
/// 添加更多
],
child: child: MutiProviderDemo(),
);
6.ProxyProvider
当我们有多个模型的时候,会有模型依赖另一个模型的情况,在这种情况下,我们可以使用ProxyProvider从另一个提供者获取值,然后将其注入到另一个提供者中.
7.ChangeNotifierProxyProvider
和ProxyProvider原理一样,唯一的区别在于它构建和同步ChangeNotifier的ChangeNotifierProvider,当提供者数据变化时,将会重构UI。
return MultiProvider(
providers: [
Provider(create: (_) => BookModel()),
ChangeNotifierProxyProvider<BookModel, BookManagerModel>(
create: (_) => BookManagerModel(BookModel()),
update: (_, bookModel, bookManagerModel) => BookManagerModel(bookModel),
)
],
child: MaterialApp(
home:MyHomePage(title: 'Flutter Demo Home Page'),
),
);
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:wyl_flutter/ProviderDemo/collection_goods_page.dart';
import 'package:wyl_flutter/ProviderDemo/goods_model.dart';
import 'package:wyl_flutter/ProviderDemo/goods_view_model.dart';
class CollectionViewModel with ChangeNotifier {
// 依赖_goodsViewModel
final GoodsViewModel _goodsViewModel;
// 获取数据所有的ID
List<int>? _goodIds;
// 构造函数
CollectionViewModel(this._goodsViewModel, {CollectionViewModel? colletionModel})
: _goodIds = colletionModel?._goodIds ?? [];
// 获取所有的数据
List<GoodsModel> get goods => _goodIds!.map((id) => _goodsViewModel.getById(id)).toList();
// 根据索引获取数据
GoodsModel getByPosition(int position) => goods[position];
// 获取长度
int get length => _goodIds?.length ?? 0;
// 添加/删除
void addFaves(GoodsModel goods) {
if(_goodIds!.contains(goods.id)){
goods.isCollected = false;
_goodIds!.remove(goods.id!);
}else{
goods.isCollected = true;
_goodIds!.add(goods.id!);
}
notifyListeners();
}
}
class ChangeNotifierProxyProviderDemo extends StatefulWidget{
const ChangeNotifierProxyProviderDemo({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return ChangeNotifierProxyProviderDemoState();
}
}
class ChangeNotifierProxyProviderDemoState extends State<ChangeNotifierProxyProviderDemo>{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(title: const Text('ChangeNotifierProxyProviderDemo'),actions: [
GestureDetector(
child: const Icon(Icons.more_horiz),
onTap: (){
Navigator.push(context, MaterialPageRoute(builder: (context)=> const CollectionGoodsPage()) );
//CollectionGoodsPage
},
)
],),
body: Consumer2<GoodsViewModel,CollectionViewModel>(
builder: (_,vm,vm2,child){
return ListView.builder(
itemBuilder: (ctx,index){
GoodsModel m = vm.dataList[index];
return Row(
children: [
Padding(
padding: const EdgeInsets.all(15),
child: CachedNetworkImage(
imageUrl: m.img!,
width: 100,
),
),
Padding(
padding: const EdgeInsets.all(15),
child: Text(m.title!,),
),
Expanded(
child: GestureDetector(
child: Container(
alignment: Alignment.centerRight,
// color: Colors.yellow,
padding: EdgeInsets.only(right: 15),
width: 100,
height: 60,
child: m.isCollected!
? Icon(Icons.favorite, color: Colors.red,)
: Icon(Icons.favorite_border),
),
onTap: ()=> vm2.addFaves(m),
//vm.CollectGoods(m)
)
)
],
);
},
itemCount: vm.dataList.length,
);
},
),
);
}
}
8.ListenableProxyProvider
ListenableProxyProvider是ListenableProvider的一个变体,但是在使用上和ChangeNotifierProxyProvider基本一致。因为ChangeNotifierProxyProvider继承ListenableProxyProvider。
总结
1).说到底Provider的状态实现其实就是观察者模式,观察者是ui,被观察者是model用来处理数据并调用notifyListeners通知ui的更新,等待垂直信号的到来渲染。
2).常用的是ChangeNotifierProvider、MultiProvider、ChangeNotifierProxyProvider,关于其他的提供者可根据自己的实际应用场景来。