Flutter中各种Consumer的区别和使用场景

在项目中,遇到了各种Consumer,刚开始的时候还不知道他们有什么区别,后面通过一些开发实践逐渐摸清楚了他们之间的区别和使用场景。
首先,这些Consumer来源于riverpod这个库,主要是提升开发者对provider的使用,便于获取provider,实现相关的状态管理。

1. ConsumerWidget

是一个StatefulWidget,但是不需要我们去实现相关的Widget和State。内部已经帮我们封装实现好了。使用时,只要继承ConsumerWidget,实现Widget build(BuildContext context, WidgetRef ref);函数即可。然后通过ref获取需要的provider

2. Consumer

是对ConsumerWidget的进一步封装。对于一些Widget我们不想去新建一个类来继承ConsumerWidget,那我们可以使用Consumer来包裹Widget。
例子:

Consumer(
    builder: (context, ref, child) {
        final value = ref.watch(helloWorldProvider);
        return Text(value);
    },
);

3. HookConsumerWidget

HookConsumerWidget是HookWidget和ConsumerWidget的结合。HookWidget的主要作用是在封装好的StateLessWidget,实现一些需要initState或者dispose回调的一些方法,比如AnimationController或者是做一些缓存。
而ConsumerWidget虽然是StatefulWidget,但是没办法回调initState和dispose,所以通过将HookConsumerWidget,就可以在使用Consumer的时候,实现一些需要initState和dispose的逻辑。

@override
Widget build(BuildContext context, WidgetRef ref) {
  // 初始化一个变量,并且只会执行一次
  final count = useState(initValue);
  // 改变变量的值,并且会刷新widget,相当于setState
  count.value++;

  useEffect({
    // 做一些initState的操作
    // 这里的执行只有Widget第一次build的时候才会执行
    return method.call();// 这个方法在widget dispose的时候会执行。
  })

  // 一条语句可以实现animationController的初始化和自动dispose
  final animationCtrl =
        useAnimationController(duration: 300.milliseconds, initialValue: 0);
}

更多Hook用法参考:Hook的用法

4. HookConsumer

HookConsumer就是对HookConsumerWidget的进一步封装,对于一些不需要新建一个类去继承HookConsumerWidget的,那就直接用HookConsumer。就如同Consumer和ConsumerWidget之间的关系一样。

5. RiverpodConsumer

是内部自己实现的一个HookConsumer,传入listenable和builder可以实现provider的监听和rebuild。对于只需要监听单一变量时,使用RiverpodConsumer是比较方便的,但是如果一个widget需要监听多个变量,对这些变量做出对应的widget更新,那是不建议使用RiverpodConsumer进行多层嵌套的。因为RiverpodConsumer实际内部还是用Consumer实现的,而Consumer实际是一个StatefulWidget,所以RiverpodConsumer的嵌套实际上就是StatefulWidget的嵌套,这个是损耗性能并且是没必要的操作。还降低了代码的可读性。

6. WidgetRef的监听

WidgetRef中一共有4种调用provider的方式,分别是read, listen, watch和refresh,其中前3种比较常见和易用。read是获取provider的当前状态,如果后续provider发生改变了,那么获得的值并不会更新。并且,当provider是AutoDispose的,那么在read完这个provider之后,provider就会被dispose,即使当前页面没有销毁,provider也同样会被dispose。
而listen和watch是可以监听provider的变化。他们的区别是,listen是监听provider的变化,然后在回调函数中做对应的逻辑处理。而watch是监听provider的变化,然后让·ref对应的widget自动重建。
所以,当我们使用watch的时候应该尽可能的让底层的ref去做监听,来避免大量widget的重建。并且监听的时候尽量使用provider.select((value) => value.member)监听provider中的某个变量的变化。来避免provider其他不相关的变量发生变化引起不必要的重建。
另外,当我们监听provider中的集合时,如果是集合中的元素发生变化(增删改),通过provider.select((value) => value.collection)是没办法监听到的,此时只能ref.watch(provider)监听整个provider来获取集合元素的变化。除非是在集合元素发生变化后,重新对集合进行赋值,那就可以监听select
对provider中的对象同理。如果是修改对象的某个成员变量,只监听该对象是无法获得该对象的某个成员变量的变化。需要监听该对象的成员变量。

你可能感兴趣的:(Flutter中各种Consumer的区别和使用场景)