這是我參與8月更文挑戰的第21天,活動詳情檢視:8月更文挑戰
前言
React
開發者都知道Redux
在開發大中型 Web 應用的重要性, Flutter
作為 React
的高仿者,連 setState
都搬過來了,自然也需要把 Redux
也搬過來——好東西要一起分享。
Redux 簡介
Redux
是一個單向資料流的狀態管理架構,通過它能夠簡化應用開發、測試和維護。Flutter的 Redux 相關外掛地址:
- redux:Flutter 中需要的全部
Redux
元件,例如Store
,Reducer
和Middleware
。 - flutter_redux:Flutter 專屬的外掛包,在 redux 基礎上進行了封裝,使得在 Flutter 中應用更為簡便,例如
StoreProvider
(為元件提供Store
的基礎Widget
),StoreBuilder
(從StoreProvider
中接收Store
的元件)和StoreConnector
(用於替換StoreBuilder
,可以將Store
轉換為一個ViewModel
來構建元件樹,一旦Store
中的State
改變了就可以重建StoreConnector
)。
在 Redux
中,通過 Store
儲存了代表整個應用的狀態物件 State
。每一個應用事件(不管是使用者發起的或外部驅動)都被認為是一個 Action
。Action
通過 Reducer
進行排程,然後 Reducer
根據 Action
來更新 Store
中的狀態。而一旦 Store
中的狀態物件 State
改變了,對應的檢視 View
就會反映狀態的變化。
Redux
的最大特點就是可以將大部分元件進行解耦,使得 UI介面的重新整理更容易。而且,核心的業務邏輯都在 Reducer
函式中。Reducer
接收一個 Action
和當前的狀態,然後返回一個新的狀態物件。因此,使得單元測試更容易,我們只需要測試 Reducer
就可以測試狀態業務邏輯對不對。
Redux 中介軟體
上面僅僅是 Redux
的第一印象,但是如果要進行非同步操作,例如從介面載入資料怎麼辦?這個時候就需要引入新的元件,稱之為中介軟體(Middleware)。
中介軟體就是在Reducer
前處理 Action
的元件。它接收當前的狀態物件和要被排程的Action
,然後執行程式碼(如介面通訊,資料訪問等)。最終,中介軟體可能繼續使用原先的 Action
,或一個不同的 Action
,甚至什麼都不做。有了中介軟體後,Redux
的流程圖就變成了下面這樣:
上程式碼
接下來以一個簡單的記數應用為例,來演示 Redux
的基本應用。首先我們構建Redux
的三要素。
// counter_actions.dart,目前為空,後續可以增加業務程式碼
// 計數器增加
class CounterAddAction {}
//計數器減少
class CounterSubAction {}
//counter_state.dart
class CounterState {
int count = 0;
CounterState(this.count);
}
// counter_reducer.dart
CounterState counterReducer(CounterState state, dynamic action) {
if (action.runtimeType == CounterAddAction) {
return CounterState(state.count + 1);
}
if (action.runtimeType == CounterSubAction) {
return CounterState(state.count - 1);
}
return state;
}
複製程式碼
可以看到,核心業務都在 reducer
裡面,reducer
根據 action
的型別更新state
,然後返回新的state
物件。action 是一個動態型別,如果簡單的操作也可以是列舉,但是如果要攜帶新的資料或業務邏輯(例如,我們可以每次點選可以改變增加或減少的數量)使用物件會更方便。
Redux
的 Reducer
定義形式如下,即接收舊的 state
和 action
引數,然後返回新的 state
。
typedef Reducer<State> = State Function(State state, dynamic action)
複製程式碼
接下來是頁面邏輯的實現了。頁面邏輯有點類似。如果一個 Store 需要被下級元件使用,那麼就要定義在上一級元件中。這裡我們比較簡單,直接定義在當前頁面即可。關鍵的部分有以下部分:
build
的子元件使用StoreProvider
包裹,以便共享store
。- 需要監聽
store
變化的元件使用StoreConnector
的builder
構建,並且可以使用converter
方法將store
轉成元件需要的資料(即將狀態資料轉成ViewModel
),這裡只是簡單地將計數值轉為了String
。在StoreConnector
方法中構建的元件,一旦狀態物件發生改變後就會通知重新整理。 - 按鈕事件:在按鈕中執行狀態物件的
dispatch
操作,以便排程對應的reducer
完成狀態更新。dispatch
方法定義如下,可以看到實際上dispatch
是一組排程,這裡只是呼叫了第一個方法,這意味著其實可以執行一系列的排程操作。
dynamic dispatch(dynamic action) {
return _dispatchers[0](action);
}
複製程式碼
class CounterPage extends StatelessWidget {
CounterPage({Key? key}) : super(key: key);
final store =
Store<CounterState>(counterReducer, initialState: CounterState(0));
@override
Widget build(BuildContext context) {
return StoreProvider(
store: store,
child: Scaffold(
appBar: AppBar(
title: Text('Redux Counter'),
),
body: Center(
child: StoreConnector<CounterState, String>(
converter: (store) => store.state.count.toString(),
builder: (context, count) {
return Text(
count,
style: TextStyle(color: Colors.blue, fontSize: 30),
);
},
),
),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
FloatingActionButton(
child: Icon(Icons.arrow_upward),
onPressed: () {
store.dispatch(CounterAddAction());
}),
FloatingActionButton(
backgroundColor: Colors.red,
child: Icon(
Icons.arrow_downward,
),
onPressed: () {
store.dispatch(CounterSubAction());
}),
],
),
),
);
}
}
複製程式碼
執行效果
執行效果如下所示。
總結
本篇介紹了 Redux
的基本概念和一個簡單的示例,通常來說,使用Redux
的一大好處是整個狀態管理的業務模式都是一致的,都必須具備 Action
,Store
,Reducer
這三個環節,這樣其實也能夠間接規範程式碼結構。同時,有了 flutter_redux
外掛的適配,在 Flutter
中使用Redux
也更為簡便。
我是島上碼農,微信公眾號同名,這是Flutter 入門與實戰的專欄文章,對應原始碼請看這裡:Flutter 入門與實戰專欄原始碼。
??:覺得有收穫請點個贊鼓勵一下!
?:收藏文章,方便回看哦!
?:評論交流,互相進步!