1、Riverpod干什么的
- 主要是用来管理状态的,对局部Widget进行重建,而不会对整个页面的节点重建,类似Android的liveData
- 引用相同的 Provider ,可以实现多个布局页面同时修改同一个被观察的数据,做到同步修改
2、为什么使用Riverpod 而不使用Provider
优点
- 不需要去考虑BuildContext的传递跟生命周期
- 全局定义ProviderScope,通过 InheritedWidget 寻找BuildContxt
缺点
- 没有了Notify主动通知,必须修改被观察的引用才能收到通知
- 所有节点都必须继承他自带的 ConsumerStatefulWidget 或者 ConsumerWidget ,前者类似StatefulWidget,后者类似 StatelessWidget,对整个结构改变较大,这里建议封装一个Base,后期可以灵活修改
3、使用
- 添加依赖
makefile
flutter_riverpod: ^2.5.1
2、具体使用
csharp
步骤一
void main() {
// 1 ProviderScope 包裹MyApp!
runApp(const ProviderScope(child: MyApp()));
}
ini
步骤二 创建一个最简单的StateProvider
final goodsAllProvider = StateProvider<int>((ref) => []);
scala
步骤三
//** 当前的页面必须继承ConsumerStatefulWidget
class ShopClassifyGoodsPage extends ConsumerStatefulWidget {
@override
ConsumerState<ConsumerStatefulWidget> createState() {
return _ShopClassifyGoodsPageState();
}
}
//** State 必须继承 ConsumerState
class _ShopClassifyGoodsPageState extends ConsumerState<ShopClassifyGoodsPage> {
@override
Widget build(BuildContext context) {
//监听改变
var count = ref.watch(goodsAllProvider);
return GestureDetector(
onTap: (){
count++;
},
child: Container(child:Text("count"),));
}
}
3、各种 Provider
-
Provider :只存储 不可变 的值或对象,最简单的状态提供者,只对外提供访问状态值的接口,外部无法对状态值进行修改。
-
StateProvider:简单的状态更新
-
StateNotifierProvider:可以增加ViewModle,对具体观察者进行操作,不过么有主动通知的功能,只能通过从新设置应用,做到主动更新
-
FutureProvider :处理 异步操作 ,如:从网络请求数据数据,它会再Future完成时通知其观察者。通常与 autoDispose 修饰符一起使用。
-
StreamProvider :处理 基于流的异步数据,监听一个Stream,并在新数据到达前通知其观察者。
4、方法介绍 WidgetRef
-
watch() :监听Provider,当状态改变时,使用 watch() 的 Widget 会自动重建。
-
read() :只读取Provider的当前状态,状态改变,Widget不会重建。
-
listen() :通常用于在 build() 中监听Provider,当状态改变时,会调用设置的监听器,监听器会在idget重建时自动移除。
-
listenManual() :通常在 State.initState() 或其它生命周期中监听Provider,此方法返回一个 ProviderSubscription 对象,可以使用它来停止监听close(),或者读取Provider的当前状态。
-
refresh() :立即使Provider的当前状态无效,重新计算并返回新值,常用于触发异步Proivder的重新获取数据,如:下拉刷新、错误重试 等场景。
-
invalidate() :使Provider的当前状态无效,然后在下一次读取provider或者下一帧时,Provider会被重新计算。refresh() 是同步的,它是 异步 的,没有返回值。
-
exists() :判断 Provider 是否已经初始化。
5、注意
- 只订阅对象中的单个属性变化,需要借助select
csharp
ref.watch(goodsAllProvider.select((value) => value.number));
- 对于FutureProvider 修饰的,如过需要获取,需要借助future
ini
final newProducts = await ref.read(vmGoods.goodsListProvider(pageNum).future);
- 对于不想创建复杂ViewModle对象,不想使用StateNotifierProvider,直接使用StateProvider 去声明的对象,可以通过state去直接修改
ini
ref.read(goodsAllProvider.notifier).state = [...goodsList, ...newProducts];
4、FutureProvider 是可以直接监听页面的,他自己存在加载中、成功、失败的状态,但是不能配合分页一起使用,因为不能持久化列表的所有数据,每次只能获取祖新的,需要搭配StateProvider 去持久化所有数据使用
6、具体示例
- 分页查询
dart
class VMGoods {
//其他请求正常走
final goodsAllProvider = StateProvider<List<GoodsBean>>((ref) => []);
final goodsListProvider = FutureProvider.family<List<GoodsBean>, int>((ref, pageNum) async {
await Future.delayed(const Duration(seconds: 1));
var list = await AppUtil.getGoodsList();
var shopBean = ref.read(shopProvider.notifier).state;
HashMap<String, int>.fromIterables(shopBean.goodsListCart.map((e) => e.id ?? ""), shopBean.goodsListCart.map((e) => e.num ?? 0)).forEach((key, value) {
list.where((element) => element.id == key).firstOrNull?.num = value;
});
return list;
});
}
ini
//局部的ViewModle
VmGoods vmGoods =VmGoods()
Widget build(BuildContext context) {
goodsList = ref.watch(vmGoods.goodsAllProvider);
}
ini
_onRefresh() async {
pageNum = 1;
final newProducts = await ref.read(vmGoods.goodsListProvider(pageNum).future);
ref.read(vmGoods.goodsAllProvider.notifier).state = newProducts;
_refreshController.refreshCompleted();
}
_onLoading() async {
pageNum++;
final newProducts = await ref.read(vmGoods.goodsListProvider(pageNum).future);
ref.read(vmGoods.goodsAllProvider.notifier).state = [...goodsList, ...newProducts];
_refreshController.loadComplete();
}
- 复杂的类的状态 StateNotifierProvider
scala
class VMShop extends BaseNotifier<VMShopBean> {
VMShop(Ref ref) : super(VMShopBean(), ref: ref);
addGoodsToCart(GoodsBean? goodsBean) {
state = VMShopBean.fromJson(state.toJson());
}
subGoods(GoodsBean? goodsBean) {
state = VMShopBean.fromJson(state.toJson());
}
int getCount() {
return 0;
}
}
final shopProvider = StateNotifierProvider<VMShop, VMShopBean>((ref) {
return VMShop(ref);
});
- FutureProvider 直接更改页面状态
dart
final goodsListProvider = FutureProvider.family<List<GoodsBean>, int>((ref, pageNum) async {
await Future.delayed(const Duration(seconds: 1));
var list = await AppUtil.getGoodsList();
var shopBean = ref.read(shopProvider.notifier).state;
HashMap<String, int>.fromIterables(shopBean.goodsListCart.map((e) => e.id ?? ""), shopBean.goodsListCart.map((e) => e.num ?? 0)).forEach((key, value) {
list.where((element) => element.id == key).firstOrNull?.num = value;
});
return list;
});
csharp
Widget build(BuildContext context) {
final goodsListAsyncValue = await ref.read(goodsListProvider(pageNum));
return Container(
child:goodsListAsyncValue?.when(
data: (data) {
//这是列表的Item,就不加了
return Container();
},
error: (error, stack) => const LoadingErrorWidget(),
loading: () => const LoadingWidget(),
),);
}
- StreamProvider
csharp
final numberStreamProvider = StreamProvider<int>((ref) async* {
for (int i = 0; i < 10; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
});