Flutter中各種Consumer的區(qū)別和使用場(chǎng)景

在項(xiàng)目中,遇到了各種Consumer,剛開始的時(shí)候還不知道他們有什么區(qū)別,后面通過一些開發(fā)實(shí)踐逐漸摸清楚了他們之間的區(qū)別和使用場(chǎng)景。
首先,這些Consumer來源于riverpod這個(gè)庫(kù),主要是提升開發(fā)者對(duì)provider的使用,便于獲取provider,實(shí)現(xiàn)相關(guān)的狀態(tài)管理。

1. ConsumerWidget

是一個(gè)StatefulWidget,但是不需要我們?nèi)?shí)現(xiàn)相關(guān)的Widget和State。內(nèi)部已經(jīng)幫我們封裝實(shí)現(xiàn)好了。使用時(shí),只要繼承ConsumerWidget,實(shí)現(xiàn)Widget build(BuildContext context, WidgetRef ref);函數(shù)即可。然后通過ref獲取需要的provider

2. Consumer

是對(duì)ConsumerWidget的進(jìn)一步封裝。對(duì)于一些Widget我們不想去新建一個(gè)類來繼承ConsumerWidget,那我們可以使用Consumer來包裹Widget。
例子:

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

3. HookConsumerWidget

HookConsumerWidget是HookWidget和ConsumerWidget的結(jié)合。HookWidget的主要作用是在封裝好的StateLessWidget,實(shí)現(xiàn)一些需要initState或者dispose回調(diào)的一些方法,比如AnimationController或者是做一些緩存。
而ConsumerWidget雖然是StatefulWidget,但是沒辦法回調(diào)initState和dispose,所以通過將HookConsumerWidget,就可以在使用Consumer的時(shí)候,實(shí)現(xiàn)一些需要initState和dispose的邏輯。

@override
Widget build(BuildContext context, WidgetRef ref) {
  // 初始化一個(gè)變量,并且只會(huì)執(zhí)行一次
  final count = useState<T>(initValue);
  // 改變變量的值,并且會(huì)刷新widget,相當(dāng)于setState
  count.value++;

  useEffect({
    // 做一些initState的操作
    // 這里的執(zhí)行只有Widget第一次build的時(shí)候才會(huì)執(zhí)行
    return method.call();// 這個(gè)方法在widget dispose的時(shí)候會(huì)執(zhí)行。
  })

  // 一條語句可以實(shí)現(xiàn)animationController的初始化和自動(dòng)dispose
  final animationCtrl =
        useAnimationController(duration: 300.milliseconds, initialValue: 0);
}

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

4. HookConsumer

HookConsumer就是對(duì)HookConsumerWidget的進(jìn)一步封裝,對(duì)于一些不需要新建一個(gè)類去繼承HookConsumerWidget的,那就直接用HookConsumer。就如同Consumer和ConsumerWidget之間的關(guān)系一樣。

5. RiverpodConsumer

是內(nèi)部自己實(shí)現(xiàn)的一個(gè)HookConsumer,傳入listenable和builder可以實(shí)現(xiàn)provider的監(jiān)聽和rebuild。對(duì)于只需要監(jiān)聽單一變量時(shí),使用RiverpodConsumer是比較方便的,但是如果一個(gè)widget需要監(jiān)聽多個(gè)變量,對(duì)這些變量做出對(duì)應(yīng)的widget更新,那是不建議使用RiverpodConsumer進(jìn)行多層嵌套的。因?yàn)镽iverpodConsumer實(shí)際內(nèi)部還是用Consumer實(shí)現(xiàn)的,而Consumer實(shí)際是一個(gè)StatefulWidget,所以RiverpodConsumer的嵌套實(shí)際上就是StatefulWidget的嵌套,這個(gè)是損耗性能并且是沒必要的操作。還降低了代碼的可讀性。

6. WidgetRef的監(jiān)聽

WidgetRef中一共有4種調(diào)用provider的方式,分別是read, listen, watch和refresh,其中前3種比較常見和易用。read是獲取provider的當(dāng)前狀態(tài),如果后續(xù)provider發(fā)生改變了,那么獲得的值并不會(huì)更新。并且,當(dāng)provider是AutoDispose的,那么在read完這個(gè)provider之后,provider就會(huì)被dispose,即使當(dāng)前頁面沒有銷毀,provider也同樣會(huì)被dispose。
而listen和watch是可以監(jiān)聽provider的變化。他們的區(qū)別是,listen是監(jiān)聽provider的變化,然后在回調(diào)函數(shù)中做對(duì)應(yīng)的邏輯處理。而watch是監(jiān)聽provider的變化,然后讓·ref對(duì)應(yīng)的widget自動(dòng)重建。
所以,當(dāng)我們使用watch的時(shí)候應(yīng)該盡可能的讓底層的ref去做監(jiān)聽,來避免大量widget的重建。并且監(jiān)聽的時(shí)候盡量使用provider.select((value) => value.member)監(jiān)聽provider中的某個(gè)變量的變化。來避免provider其他不相關(guān)的變量發(fā)生變化引起不必要的重建。
另外,當(dāng)我們監(jiān)聽provider中的集合時(shí),如果是集合中的元素發(fā)生變化(增刪改),通過provider.select((value) => value.collection)是沒辦法監(jiān)聽到的,此時(shí)只能ref.watch(provider)監(jiān)聽整個(gè)provider來獲取集合元素的變化。除非是在集合元素發(fā)生變化后,重新對(duì)集合進(jìn)行賦值,那就可以監(jiān)聽select。
對(duì)provider中的對(duì)象同理。如果是修改對(duì)象的某個(gè)成員變量,只監(jiān)聽該對(duì)象是無法獲得該對(duì)象的某個(gè)成員變量的變化。需要監(jiān)聽該對(duì)象的成員變量。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容