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;
  }
});
相关推荐
小小竹子6 分钟前
前端vue-实现富文本组件
前端·vue.js·富文本
小白小白从不日白15 分钟前
react hooks--useReducer
前端·javascript·react.js
下雪天的夏风27 分钟前
TS - tsconfig.json 和 tsconfig.node.json 的关系,如何在TS 中使用 JS 不报错
前端·javascript·typescript
diygwcom39 分钟前
electron-updater实现electron全量版本更新
前端·javascript·electron
Hello-Mr.Wang1 小时前
vue3中开发引导页的方法
开发语言·前端·javascript
程序员凡尘1 小时前
完美解决 Array 方法 (map/filter/reduce) 不按预期工作 的正确解决方法,亲测有效!!!
前端·javascript·vue.js
编程零零七5 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
(⊙o⊙)~哦7 小时前
JavaScript substring() 方法
前端
无心使然云中漫步7 小时前
GIS OGC之WMTS地图服务,通过Capabilities XML描述文档,获取matrixIds,origin,计算resolutions
前端·javascript
Bug缔造者7 小时前
Element-ui el-table 全局表格排序
前端·javascript·vue.js