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)的,一般都是獲取這個控件然后設(shè)置你想要的狀態(tài) 當(dāng)你的 Flutter 應(yīng)用的狀態(tài)發(fā)生改變時(例如,用戶在設(shè)置界面中點擊了一個開關(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() 和一個變量就能達到管理狀態(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)。因為這個機制,所以 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ù)(這里點擊按鈕會修改數(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進行更新
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)點擊按鈕時,修改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)的實例,也是我們在builder函數(shù)中主要使用的對象
- child,目的是進行優(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)管理的實現(xiàn)的文章就介紹到這了,更多相關(guān)Flutter 狀態(tài)管理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android RecyclerView顯示Item布局不一致解決辦法
這篇文章主要介紹了Android RecyclerView顯示Item布局不一致解決辦法的相關(guān)資料,需要的朋友可以參考下2017-07-07
Android如何獲取屏幕、狀態(tài)欄及標(biāo)題欄的高度詳解
在日常開發(fā)中,經(jīng)常會遇到獲取屏幕高度、狀態(tài)欄高度等需求,所以下面這篇文章就給大家總結(jié)介紹了關(guān)于Android如何獲取屏幕、狀態(tài)欄及標(biāo)題欄高度的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友們可以參考下。2017-10-10
Android使用viewpager實現(xiàn)畫廊式效果
這篇文章主要為大家詳細介紹了Android使用viewpager實現(xiàn)畫廊式效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-08-08
Android—基于微信開放平臺v3SDK開發(fā)(微信支付填坑)
這篇文章主要介紹了Android—基于微信開放平臺v3SDK開發(fā)(微信支付填坑),具有一定的參考價值,有需要的可以了解一下。2016-11-11
Flutter學(xué)習(xí)LogUtil封裝與實現(xiàn)實例詳解
這篇文章主要為大家介紹了Flutter學(xué)習(xí)LogUtil封裝與實現(xiàn)實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09

