Flutter的不少靈感來自於React,它的設計思想是數據與視圖分離,由數據映射渲染視圖。因此在Flutter中,它的Widget是immutable的,而它的動態部分所有放到了狀態(State)中。因而狀態管理天然便成了咱們密切關注的對象。react
在以前咱們已經討論了關於在flutter中使用scoped_model進行狀態管理的應用。文章發出後,有許多同窗都在問我,到底redux和scoped到底誰更好。git
這個系列將會從這幾個狀態管理方案進行深刻研究:github
因此今天要和你們介紹的是在flutter中使用Redux進行狀態管理。 我但願各位在閱讀這篇文章以前,先仔細思考如下這幾個問題。編程
ok,咱們開始正式的介紹redux。redux
在咱們一開始構建應用的時候,也許很簡單。咱們有一些狀態,直接把他們映射成視圖就能夠了。這種簡單應用可能並不須要狀態管理。性能優化
可是隨着功能的增長,你的應用程序將會有幾十個甚至上百個狀態。這個時候你的應用應該會是這樣。markdown
Wow,這是什麼鬼。咱們很難再清楚的測試維護咱們的狀態,由於它看上去實在是太複雜了!並且還會有多個頁面共享同一個狀態,例如當你進入一個文章點贊,退出到外部縮略展現的時候,外部也須要顯示點贊數,這時候就須要同步這兩個狀態。這時候,咱們便迫切的須要一個架構來幫助咱們理清這些關係,狀態管理框架應運而生。架構
Redux是一種單向數據流架構,能夠輕鬆開發,維護和測試應用程序。app
這裏咱們以一個最簡單的CountApp舉例。簡單介紹flutter_redux/redux的用法。該項目完整代碼已上傳Github。框架
這是一個在不一樣頁面使用Redux共享狀態信息的app。這兩個頁面都依賴於一個數字,這個數字會隨着咱們按下按鈕的次數而增長。
咱們剛纔介紹了Redux的流程,狀態是由reducer生成並儲存在Store裏面的。Store更新狀態的時候,並不是更改原來的狀態對象,而是直接將reducer生成的新的狀態對象替換掉老的狀態對象。因此,咱們的狀態應該是immutable的。
import 'package:meta/meta.dart'; /** * State中全部屬性都應該是隻讀的 */ @immutable class CountState{ int _count; get count => _count; CountState(this._count); } 複製代碼
可能各位最開始接觸的時候對Action還會摸不着頭腦。action究竟是什麼?View如何發出action。其實,action只是咱們對狀態進行操做方法的一個代號而已。在咱們這個應用中,惟一的一個功能就是讓count的值+1,因此咱們這裏只有一個action。
/** * 定義操做該State的所有Action * 這裏只有增長count一個動做 */ enum Action{ increment } 複製代碼
reducer是咱們的狀態生成器,它接收一個咱們原來的狀態,而後接收一個action,再匹配這個action生成一個新的狀態。
/** * reducer會根據傳進來的action生成新的CountState */ CountState reducer(CountState state,action){ //匹配Action if(action == Action.increment){ return CountState(state.count+1); } return state; } 複製代碼
Store接收一個reducer,以及初始化State,咱們想用Redux管理全局的狀態的話,須要將store儲存在應用的入口才行。而在應用打開時要先初始化一次應用的狀態。因此在State中添加一個初始化的函數。
//這段代碼寫在State中 CountState.initState(){ _count = 0;} 複製代碼
//應用頂層 void main() { final store = Store<CountState>(reducer, initialState: CountState.initState()); runApp(new MyApp(store)); } 複製代碼
flutter_redux提供了一個很棒的widget叫作StoreProvider,它的用法也很簡單,接收一個store,和child Widget。
class MyApp extends StatelessWidget { final Store<CountState> store; MyApp(this.store); @override Widget build(BuildContext context) { return StoreProvider<CountState>( store: store, child: new MaterialApp( title: 'Flutter Demo', theme: new ThemeData( primarySwatch: Colors.blue, ), home: TopScreen(), ), ); } } 複製代碼
這裏建議你們把實際代碼對照下面的解釋一塊兒看。
StoreConnector<CountState,int>( converter: (store) => store.state.count, builder: (context, count) { return Text( count.toString(), style: Theme.of(context).textTheme.display1, ); }, ), 複製代碼
要想獲取store咱們須要使用StoreConnector<S,ViewModel>。StoreConnector可以經過StoreProvider找到頂層的store。並且可以在state發生變化時rebuilt Widget。
咱們這個應用在第二個頁面中,經過點擊floatingActionButton發出了action,並通知reducer生成了新的狀態。
floatingActionButton: StoreConnector<CountState,VoidCallback>( converter: (store) { return () => store.dispatch(Action.increment); }, builder: (context, callback) { return FloatingActionButton( onPressed: callback, child: Icon(Icons.add), ); }, ), 複製代碼
以上即是在flutter中使用redux共享狀態信息的所有內容。
咱們的StoreConnector可以將store提取出信息並轉化成ViewModel,這裏實際上是有一個性能優化的點的。咱們這裏的例子很是簡單,它的ViewModel就只是一個int的值,當咱們ViewModel很複雜的時候,咱們可使用StoreConnector的distinct屬性進行性能優化。使用方法很簡單:須要咱們在ViewModel中重寫[==] and [hashCode] 方法,而後把distinct屬性設爲true。
Redux提供了一種簡單的方法來更新應用程序的狀態以響應同步操做。可是,它缺乏處理異步代碼的工具。咱們如何應對異步相應呢。
這裏就須要一個interrupt來處理異步請求,而後再發出新的action通知reducer生成新的State了。 這裏有brianegan大神寫的另一個幫助在flutter中使用redux處理異步請求的庫redux_thunk。我會在以後的文章中詳細介紹如何在redux中處理異步操做。
咱們發現,redux的確可以在flutter中很好的工做。在react中數據是沒有上行能力的,因此經過數據單向流動造成一個環來進行狀態管理。看上去彷佛並無把flutter中的優點徹底發揮出來。在這個簡單的例子中咱們也能夠看出,使用redux仍是稍微有些麻煩的,用的很差,可能會陷入redux地獄。學習成本偏高也是它的一大痛點。
固然,redux這套狀態管理架構已經比較成熟,假如您已經習慣redux,也可以快速經過flutter_redux輕鬆構建屬於您的狀態管理應用。
那麼你如今如何看待redux呢?
本次所用到的代碼已經上傳Github: github.com/Vadaski/Flu…
這篇文章參考瞭如下資料
你能在這些地方瞭解更多關於flutter-redux
若是您對flutter_redux還有任何見解或者文章的建議或者文章中有任何不對之處,歡迎在下方評論區以及個人郵箱1652219550a@gmail.com留言,我會在24小時內與您聯繫!
按理說下一章咱們將探索BLoC在Flutter中的實踐,而BLoC很是Reactive Programming,因此我決定先讓你們瞭解一些dart:Stream的知識再介紹它,因此下一篇文章咱們會介紹Stream以及流式編程,敬請關注。