Flutter 狀態(tài)管理的實(shí)現(xiàn)
一、什么是狀態(tài)管理
大到整個app的狀態(tài),用戶使用app是登錄狀態(tài),還是游客狀態(tài);小到一個按鈕的狀態(tài),按鈕是點(diǎn)擊選中狀態(tài)還是未點(diǎn)擊狀態(tài)等等,這些都是狀態(tài)管理。
二、命令式編程和聲明式編程狀態(tài)管理的區(qū)別
iOS是如何管理狀態(tài)的,一般都是獲取這個控件然后設(shè)置你想要的狀態(tài) 當(dāng)你的 Flutter 應(yīng)用的狀態(tài)發(fā)生改變時(例如,用戶在設(shè)置界面中點(diǎn)擊了一個開關(guān)選項)你改變了狀態(tài),這將會觸發(fā)用戶界面的重繪。去改變用戶界面本身是沒有必要的(例如 widget.setText )—你改變了狀態(tài),那么用戶界面將重新構(gòu)建。
三、狀態(tài)管理中的聲明式編程思維
Flutter 應(yīng)用是 聲明式 的,這也就意味著 Flutter 構(gòu)建的用戶界面就是應(yīng)用的當(dāng)前狀態(tài)。
一旦你的界面狀態(tài)發(fā)生改變,就會觸發(fā)界面的重新繪制,繪制出你想要的界面,而不是像iOS的OC語言那樣去獲取需要改變狀態(tài)的控件,然后修改它
四、短時 (ephemeral) 和應(yīng)用 (app) 狀態(tài)的區(qū)別
Flutter中的狀態(tài)管理又分為短時狀態(tài)和應(yīng)用狀態(tài)。
短時狀態(tài),就是在單個頁面需要保持的狀態(tài),比如頁面數(shù)據(jù)加載到了第幾頁,關(guān)注按鈕是已關(guān)注還是未關(guān)注等,都是在單個頁面需要保持的狀態(tài)。widget樹中其他部分不需要訪問這種狀態(tài)。不需要去序列化這種狀態(tài),這種狀態(tài)也不會以復(fù)雜的方式改變。換句話說,不需要使用狀態(tài)管理架構(gòu)(例如 ScopedModel, Redux)去管理這種狀態(tài)。你需要用的只是一個 StatefulWidget。
在下方你可以看到一個底部導(dǎo)航欄中當(dāng)前被選中的項目是如何被被保存在 _MyHomepageState 類的 _index 變量中。在這個例子中,_index 是一個短時狀態(tài)。
class MyHomepage extends StatefulWidget { @override _MyHomepageState createState() => _MyHomepageState(); } class _MyHomepageState extends State<MyHomepage> { int _index = 0; @override Widget build(BuildContext context) { return BottomNavigationBar( currentIndex: _index, onTap: (newIndex) { setState(() { _index = newIndex; }); }, // ... items ... ); } }
在這里,使用 setState() 和一個變量就能達(dá)到管理狀態(tài)的目的。你的 app 中的其他部分不需要訪問 _index。這個變量只會在 MyHomepage widget 中改變。而且,如果用戶關(guān)閉并重啟這個 app,_index會被重置而不會繼續(xù)保持原來的狀態(tài)。
應(yīng)用狀態(tài),如果你想在你的應(yīng)用中的多個部分之間共享一個非短時的狀態(tài),并且在用戶會話期間保留這個狀態(tài),我們稱之為應(yīng)用狀態(tài)(有時也稱共享狀態(tài))。 應(yīng)用狀態(tài)的一些例子:
1、用戶選項
2、登錄信息
3、一個社交應(yīng)用中的通知
4、一個電商應(yīng)用中的購物車
5、一個新聞應(yīng)用中的文章已讀/未讀狀態(tài)
五、共享狀態(tài)管理
在 Flutter 中,一般是將存儲狀態(tài)的對象置于 widget 樹中對應(yīng) widget 的上層,當(dāng)它發(fā)生改變的時候,它對應(yīng)的widget會從上層開始重構(gòu)。因?yàn)檫@個機(jī)制,所以 widget 無需考慮生命周期的問題—它只需要針對 上層存儲數(shù)據(jù)的對象 聲明所需顯示內(nèi)容即可。當(dāng)內(nèi)容發(fā)生改變的時候,舊的 widget 就會消失,完全被新的 widget 替代。 Flutter原生提供了兩個方法來管理共享狀態(tài):
5.1 --InheritedWidget
class ADCounterWidget extends InheritedWidget { // 1. 共享的數(shù)據(jù) final int counter; // 2. 定義構(gòu)造方法 ADCounterWidget({this.counter, Widget child}): super(child: child); // 3. 找到當(dāng)前Widget樹中最近的InheritedWidget static ADCounterWidget of(BuildContext context) { // 沿著Element樹, 去找到最近的ADCounterElement, 從Element中取出Widget對象 return context.dependOnInheritedWidgetOfExactType(); } // 4. 要不要回調(diào)State中的didChangeDependencies方法 @override bool updateShouldNotify(ADCounterWidget oldWidget) { return oldWidget.counter != counter; } }
上面定義了一個of方法,該方法通過context開始去查找父級的HYDataWidget
updateShouldNotify方法是對比新舊HYDataWidget,是否需要對更新相關(guān)依賴的Widget
class HYHomePage extends StatefulWidget { @override _HYHomePageState createState() => _HYHomePageState(); } class _HYHomePageState extends State<HYHomePage> { int data = 100; @override Widget 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++; }); }, ), ); } }
創(chuàng)建HYDataWidget,并且傳入數(shù)據(jù)(這里點(diǎn)擊按鈕會修改數(shù)據(jù),并且出發(fā)重新build)
5.2 --Provider
Provider庫有三個主要用到的類:
- ChangeNotifier:真正數(shù)據(jù)(狀態(tài))存放的地方
- ChangeNotifierProvider:Widget樹中提供數(shù)據(jù)(狀態(tài))的地方,會在其中創(chuàng)建對應(yīng)的ChangeNotifier
- Consumer:Widget樹中需要使用數(shù)據(jù)(狀態(tài))的地方
第一步 在程序的最頂層創(chuàng)建自己的ChangeNotifier
- 將ChangeNotifierProvider放到了頂層,這樣方便在整個應(yīng)用的任何地方可以使用CounterProvider
- 在ChangeNotifier中創(chuàng)建一個私有的_counter,并且提供了getter和setter
- 在setter中我們監(jiān)聽到_counter的改變,就調(diào)用notifyListeners方法,通知所有的Consumer進(jìn)行更新
void main() { runApp(ChangeNotifierProvider( create: (context) => CounterProvider(), child: MyApp(), )); } class CounterProvider extends ChangeNotifier { int _counter = 100; intget counter { return _counter; } set counter(int value) { _counter = value; notifyListeners(); } }
第二步 在首頁中使用Consumer引入和修改狀態(tài)
- 在body中使用Consumer,Consumer需要傳入一個builder回調(diào)函數(shù),當(dāng)數(shù)據(jù)發(fā)生變化時,就會通知依賴數(shù)據(jù)的Consumer重新調(diào)用builder方法來構(gòu)建
- 在floatingActionButton中使用Consumer,當(dāng)點(diǎn)擊按鈕時,修改CounterNotifier中的counter數(shù)據(jù)
class HYHomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("列表測試"), ), body: Center( child: Consumer<CounterProvider>( builder: (ctx, counterPro, child) { return Text("當(dāng)前計數(shù):${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方法有三個參數(shù):
- context,每個build方法都會有上下文,目的是知道當(dāng)前樹的位置
- ChangeNotifier對應(yīng)的實(shí)例,也是我們在builder函數(shù)中主要使用的對象
- child,目的是進(jìn)行優(yōu)化,如果builder下面有一顆龐大的子樹,當(dāng)模型發(fā)生改變的時候,我們并不希望重新build這顆子樹,那么就可以將這顆子樹放到Consumer的child中,在這里直接引入即可(注意我案例中的Icon所放的位置)
第四步 創(chuàng)建一個新的頁面,在新的頁面中修改數(shù)據(jù)
class SecondPage extends StatelessWidget { @override Widget 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), ), ); } }
到此這篇關(guān)于Flutter 狀態(tài)管理的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Flutter 狀態(tài)管理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android 軟鍵盤自動彈出與關(guān)閉實(shí)例詳解
這篇文章主要介紹了Android 軟鍵盤自動彈出與關(guān)閉實(shí)例詳解的相關(guān)資料,為了用戶體驗(yàn)應(yīng)該自動彈出軟鍵盤而不是讓用戶主動點(diǎn)擊輸入框才彈出,這里舉例說明該如何實(shí)現(xiàn),需要的朋友可以參考下2016-12-12Android開發(fā)之SeekBar基本使用及各種美觀樣式示例
這篇文章主要介紹了Android開發(fā)之SeekBar基本使用及各種美觀樣式,結(jié)合實(shí)例形式分析了Android SeekBar控件的功能及樣式改變相關(guān)操作技巧,需要的朋友可以參考下2019-03-03Android中fragment與activity之間的交互(兩種實(shí)現(xiàn)方式)
本篇文章主要介紹了Android中fragment與activity之間的交互(兩種實(shí)現(xiàn)方式),相信對大家學(xué)習(xí)會有很好的幫助,需要的朋友一起來看下吧2016-12-12如何使用Mock修改Android設(shè)備上的features
這篇文章主要介紹了如何使用Mock修改Android設(shè)備上的features,想了解Mock的同學(xué)可以參考下2021-04-04Android GridView擴(kuò)展仿微信微博發(fā)圖動態(tài)添加刪除圖片功能
這篇文章主要為大家詳細(xì)介紹了Android GridView擴(kuò)展仿微信微博發(fā)圖動態(tài)添加刪除圖片功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-05-05Android App中的GridView網(wǎng)格布局使用指南
GridView布局所實(shí)現(xiàn)的就是類似于九宮格的矩陣界面效果,下面整理了Android App中的GridView網(wǎng)格布局使用指南,包括分割線的添加與自定義GridView的實(shí)現(xiàn)等技巧,需要的朋友可以參考下2016-06-06Android實(shí)現(xiàn)循環(huán)平移動畫示例
這篇文章主要介紹了Android實(shí)現(xiàn)循環(huán)平移動畫示例,本文講解實(shí)現(xiàn)用一張背景圖做循環(huán)從左往右平移動畫,需要的朋友可以參考下2015-06-06神經(jīng)網(wǎng)絡(luò)API、Kotlin支持,那些你必須知道的Android 8.1預(yù)覽版和Android Studio 3.0新特
這篇文章主要介紹了神經(jīng)網(wǎng)絡(luò)API、Kotlin支持,那些你必須了解的Android 8.1預(yù)覽版和Android Studio 3.0新特性,需要的朋友可以參考下2017-10-10