日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

flutter 局部状态和全局状态区别_Flutter状态管理

發布時間:2025/4/5 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 flutter 局部状态和全局状态区别_Flutter状态管理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Flutter狀態管理

狀態管理是聲明式編程非常重要的一個概念,我們在前面介紹過Flutter是聲明式編程的,也區分聲明式編程和命令式編程的區別。
這里,我們就來系統的學習一下Flutter聲明式編程中非常重要的狀態管理

一. 為什么需要狀態管理?

1.1. 認識狀態管理

很多從命令式編程框架(Android或iOS原生開發者)轉成聲明式編程(Flutter、Vue、React等)剛開始并不適應,因為需要一個新的角度來考慮APP的開發模式。

Flutter作為一個現代的框架,是聲明式編程的:

在編寫一個應用的過程中,我們有大量的State需要來進行管理,而正是對這些State的改變,來更新界面的刷新:

1.2. 不同狀態管理分類

1.2.1. 短時狀態Ephemeral state

某些狀態只需要在自己的Widget中使用即可

  • 比如我們之前做的簡單計數器counter
  • 比如一個PageView組件記錄當前的頁面
  • 比如一個動畫記錄當前的進度
  • 比如一個BottomNavigationBar中當前被選中的tab

這種狀態我們只需要使用StatefulWidget對應的State類自己管理即可,Widget樹中的其它部分并不需要訪問這個狀態。

這種方式在之前的學習中,我們已經應用過非常多次了。

1.2.2. 應用狀態App state

開發中也有非常多的狀態需要在多個部分進行共享

  • 比如用戶一個個性化選項
  • 比如用戶的登錄狀態信息
  • 比如一個電商應用的購物車
  • 比如一個新聞應用的已讀消息或者未讀消息

這種狀態我們如果在Widget之間傳遞來、傳遞去,那么是無窮盡的,并且代碼的耦合度會變得非常高,牽一發而動全身,無論是代碼編寫質量、后期維護、可擴展性都非常差。

這個時候我們可以選擇全局狀態管理的方式,來對狀態進行統一的管理和應用。

1.2.3. 如何選擇不同的管理方式

開發中,沒有明確的規則去區分哪些狀態是短時狀態,哪些狀態是應用狀態。

  • 某些短時狀態可能在之后的開發維護中需要升級為應用狀態。

但是我們可以簡單遵守下面這幅流程圖的規則:

針對React使用setState還是Redux中的Store來管理狀態哪個更好的問題,Redux的issue上,Redux的作者Dan Abramov,它這樣回答的:

The rule of thumb is: Do whatever is less awkward

經驗原則就是:選擇能夠減少麻煩的方式。

二. 共享狀態管理

2.1. InheritedWidget

InheritedWidget和React中的context功能類似,可以實現跨組件數據的傳遞。

定義一個共享數據的InheritedWidget,需要繼承自InheritedWidget

  • 這里定義了一個of方法,該方法通過context開始去查找祖先的HYDataWidget(可以查看源碼查找過程)
  • updateShouldNotify方法是對比新舊HYDataWidget,是否需要對更新相關依賴的Widget
class HYDataWidget extends InheritedWidget {final int counter;HYDataWidget({this.counter, Widget child}): super(child: child);static HYDataWidget of(BuildContext context) {return context.dependOnInheritedWidgetOfExactType();}@overridebool updateShouldNotify(HYDataWidget oldWidget) {return this.counter != oldWidget.counter;} }

創建HYDataWidget,并且傳入數據(這里點擊按鈕會修改數據,并且重新build)

class HYHomePage extends StatefulWidget {@override_HYHomePageState createState() => _HYHomePageState(); }class _HYHomePageState extends State<HYHomePage> {int data = 100;@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("InheritedWidget"),),body: HYDataWidget(counter: data,child: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[HYShowData()],),),),floatingActionButton: FloatingActionButton(child: Icon(Icons.add),onPressed: () {setState(() {data++;});},),);} }

在某個Widget中使用共享的數據,并且監聽

2.2. Provider

Provider是目前官方推薦的全局狀態管理工具,由社區作者Remi Rousselet 和 Flutter Team共同編寫。

使用之前,我們需要先引入對它的依賴,截止這篇文章,Provider的最新版本為4.0.4:

dependencies:provider: ^4.0.4

2.2.1. Provider的基本使用

在使用Provider的時候,我們主要關心三個概念:

  • ChangeNotifier:真正數據(狀態)存放的地方
  • ChangeNotifierProvider:Widget樹中提供數據(狀態)的地方,會在其中創建對應的ChangeNotifier
  • Consumer:Widget樹中需要使用數據(狀態)的地方

我們先來完成一個簡單的案例,將官方計數器案例使用Provider來實現:

第一步:創建自己的ChangeNotifier

我們需要一個ChangeNotifier來保存我們的狀態,所以創建它

  • 這里我們可以使用繼承自ChangeNotifier,也可以使用混入,這取決于概率是否需要繼承自其它的類
  • 我們使用一個私有的_counter,并且提供了getter和setter
  • 在setter中我們監聽到_counter的改變,就調用notifyListeners方法,通知所有的Consumer進行更新
class CounterProvider extends ChangeNotifier {int _counter = 100;int get counter {return _counter;}set counter(int value) {_counter = value;notifyListeners();} }

第二步:在Widget Tree中插入ChangeNotifierProvider

我們需要在Widget Tree中插入ChangeNotifierProvider,以便Consumer可以獲取到數據:

  • 將ChangeNotifierProvider放到了頂層,這樣方便在整個應用的任何地方可以使用CounterProvider
void main() {runApp(ChangeNotifierProvider(create: (context) => CounterProvider(),child: MyApp(),)); }

第三步:在首頁中使用Consumer引入和修改狀態

  • 引入位置一:在body中使用Consumer,Consumer需要傳入一個builder回調函數,當數據發生變化時,就會通知依賴數據的Consumer重新調用builder方法來構建;
  • 引入位置二:在floatingActionButton中使用Consumer,當點擊按鈕時,修改CounterNotifier中的counter數據;
class HYHomePage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("列表測試"),),body: Center(child: Consumer<CounterProvider>(builder: (ctx, counterPro, child) {return Text("當前計數:${counterPro.counter}", style: TextStyle(fontSize: 20, color: Colors.red),);}),),floatingActionButton: Consumer<CounterProvider>(builder: (ctx, counterPro, child) {return FloatingActionButton(child: child,onPressed: () {counterPro.counter += 1;},);},child: Icon(Icons.add),),);} }

Consumer的builder方法解析:

  • 參數一:context,每個build方法都會有上下文,目的是知道當前樹的位置
  • 參數二:ChangeNotifier對應的實例,也是我們在builder函數中主要使用的對象
  • 參數三:child,目的是進行優化,如果builder下面有一顆龐大的子樹,當模型發生改變的時候,我們并不希望重新build這顆子樹,那么就可以將這顆子樹放到Consumer的child中,在這里直接引入即可(注意我案例中的Icon所放的位置)

步驟四:創建一個新的頁面,在新的頁面中修改數據

class SecondPage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("第二個頁面"),),floatingActionButton: Consumer<CounterProvider>(builder: (ctx, counterPro, child) {return FloatingActionButton(child: child,onPressed: () {counterPro.counter += 1;},);},child: Icon(Icons.add),),);} }

2.2.2. Provider.of的弊端

事實上,因為Provider是基于InheritedWidget,所以我們在使用ChangeNotifier中的數據時,我們可以通過Provider.of的方式來使用,比如下面的代碼:

Text("當前計數:${Provider.of<CounterProvider>(context).counter}",style: TextStyle(fontSize: 30, color: Colors.purple), ),

我們會發現很明顯上面的代碼會更加簡潔,那么開發中是否要選擇上面這種方式了?

  • 答案是否定的,更多時候我們還是要選擇Consumer的方式。

為什么呢?因為Consumer在刷新整個Widget樹時,會盡可能少的rebuild Widget。

方式一:Provider.of的方式完整的代碼:

  • 當我們點擊了floatingActionButton時,HYHomePage的build方法會被重新調用。
  • 這意味著整個HYHomePage的Widget都需要重新build
class HYHomePage extends StatelessWidget {@overrideWidget build(BuildContext context) {print("調用了HYHomePage的build方法");return Scaffold(appBar: AppBar(title: Text("Provider"),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Text("當前計數:${Provider.of<CounterProvider>(context).counter}",style: TextStyle(fontSize: 30, color: Colors.purple),)],),),floatingActionButton: Consumer<CounterProvider>(builder: (ctx, counterPro, child) {return FloatingActionButton(child: child,onPressed: () {counterPro.counter += 1;},);},child: Icon(Icons.add),),);} }

方式二:將Text中的內容采用Consumer的方式修改如下:

  • 你會發現HYHomePage的build方法不會被重新調用;
  • 設置如果我們有對應的child widget,可以采用上面案例中的方式來組織,性能更高;
Consumer<CounterProvider>(builder: (ctx, counterPro, child) {print("調用Consumer的builder");return Text("當前計數:${counterPro.counter}",style: TextStyle(fontSize: 30, color: Colors.red),); }),

2.2.3. Selector的選擇

Consumer是否是最好的選擇呢?并不是,它也會存在弊端

  • 比如當點擊了floatingActionButton時,我們在代碼的兩處分別打印它們的builder是否會重新調用;
  • 我們會發現只要點擊了floatingActionButton,兩個位置都會被重新builder;
  • 但是floatingActionButton的位置有重新build的必要嗎?沒有,因為它是否在操作數據,并沒有展示;
  • 如何可以做到讓它不要重新build了?使用Selector來代替Consumer

我們先直接實現代碼,在解釋其中的含義:

floatingActionButton: Selector<CounterProvider, CounterProvider>(selector: (ctx, provider) => provider,shouldRebuild: (pre, next) => false,builder: (ctx, counterPro, child) {print("floatingActionButton展示的位置builder被調用");return FloatingActionButton(child: child,onPressed: () {counterPro.counter += 1;},);},child: Icon(Icons.add), ),

Selector和Consumer對比,不同之處主要是三個關鍵點:

  • 關鍵點1:泛型參數是兩個
    • 泛型參數一:我們這次要使用的Provider
    • 泛型參數二:轉換之后的數據類型,比如我這里轉換之后依然是使用CounterProvider,那么他們兩個就是一樣的類型
  • 關鍵點2:selector回調函數
    • 轉換的回調函數,你希望如何進行轉換
    • S Function(BuildContext, A) selector
    • 我這里沒有進行轉換,所以直接將A實例返回即可
  • 關鍵點3:是否希望重新rebuild
    • 這里也是一個回調函數,我們可以拿到轉換前后的兩個實例;
    • bool Function(T previous, T next);
    • 因為這里我不希望它重新rebuild,無論數據如何變化,所以這里我直接return false;

這個時候,我們重新測試點擊floatingActionButton,floatingActionButton中的代碼并不會進行rebuild操作。

所以在某些情況下,我們可以使用Selector來代替Consumer,性能會更高。

2.2.4. MultiProvider

在開發中,我們需要共享的數據肯定不止一個,并且數據之間我們需要組織到一起,所以一個Provider必然是不夠的。

我們在增加一個新的ChangeNotifier

import 'package:flutter/material.dart';class UserInfo {String nickname;int level;UserInfo(this.nickname, this.level); }class UserProvider extends ChangeNotifier {UserInfo _userInfo = UserInfo("why", 18);set userInfo(UserInfo info) {_userInfo = info;notifyListeners();}get userInfo {return _userInfo;} }

如果在開發中我們有多個Provider需要提供應該怎么做呢?

方式一:多個Provider之間嵌套

  • 這樣做有很大的弊端,如果嵌套層級過多不方便維護,擴展性也比較差
runApp(ChangeNotifierProvider(create: (context) => CounterProvider(),child: ChangeNotifierProvider(create: (context) => UserProvider(),child: MyApp()),));

方式二:使用MultiProvider

runApp(MultiProvider(providers: [ChangeNotifierProvider(create: (ctx) => CounterProvider()),ChangeNotifierProvider(create: (ctx) => UserProvider()),],child: MyApp(), )); 備注:所有內容首發于公眾號,之后除了Flutter也會更新其他技術文章,TypeScript、React、Node、uniapp、mpvue、數據結構與算法等等,也會更新一些自己的學習心得等,歡迎大家關注

總結

以上是生活随笔為你收集整理的flutter 局部状态和全局状态区别_Flutter状态管理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。