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