Riverpod 的正确姿势一

1、Riverpod干什么的

  1. 主要是用来管理状态的,对局部Widget进行重建,而不会对整个页面的节点重建,类似Android的liveData
  2. 引用相同的 Provider ,可以实现多个布局页面同时修改同一个被观察的数据,做到同步修改

2、为什么使用Riverpod 而不使用Provider

优点

  1. 不需要去考虑BuildContext的传递跟生命周期
  2. 全局定义ProviderScope,通过 InheritedWidget 寻找BuildContxt

缺点

  1. 没有了Notify主动通知,必须修改被观察的引用才能收到通知
  2. 所有节点都必须继承他自带的 ConsumerStatefulWidget 或者 ConsumerWidget ,前者类似StatefulWidget,后者类似 StatelessWidget,对整个结构改变较大,这里建议封装一个Base,后期可以灵活修改

3、使用

  1. 添加依赖
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、注意

  1. 只订阅对象中的单个属性变化,需要借助select
csharp 复制代码
ref.watch(goodsAllProvider.select((value) => value.number));
  1. 对于FutureProvider 修饰的,如过需要获取,需要借助future
ini 复制代码
final newProducts = await ref.read(vmGoods.goodsListProvider(pageNum).future);
  1. 对于不想创建复杂ViewModle对象,不想使用StateNotifierProvider,直接使用StateProvider 去声明的对象,可以通过state去直接修改
ini 复制代码
ref.read(goodsAllProvider.notifier).state = [...goodsList, ...newProducts];

4、FutureProvider 是可以直接监听页面的,他自己存在加载中、成功、失败的状态,但是不能配合分页一起使用,因为不能持久化列表的所有数据,每次只能获取祖新的,需要搭配StateProvider 去持久化所有数据使用

6、具体示例

  1. 分页查询
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();
}
  1. 复杂的类的状态 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);
});
  1. 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(),
          ),);
}
  1. StreamProvider
csharp 复制代码
final numberStreamProvider = StreamProvider<int>((ref) async* {
  for (int i = 0; i < 10; i++) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;
  }
});
相关推荐
我要洋人死36 分钟前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人1 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人1 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR1 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596931 小时前
前端预览word、excel、ppt
前端·word·excel
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風6 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#