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