Flutter | 狀態管理探索篇——Redux(二)

前言

Flutter的不少靈感來自於React,它的設計思想是數據與視圖分離,由數據映射渲染視圖。因此在Flutter中,它的Widget是immutable的,而它的動態部分所有放到了狀態(State)中。因而狀態管理天然便成了咱們密切關注的對象。react

在以前咱們已經討論了關於在flutter中使用scoped_model進行狀態管理的應用。文章發出後,有許多同窗都在問我,到底redux和scoped到底誰更好。git

這個系列將會從這幾個狀態管理方案進行深刻研究:github

  • Scoped_model
  • redux
  • BLoC
  • 對比總結篇

因此今天要和你們介紹的是在flutter中使用Redux進行狀態管理。 我但願各位在閱讀這篇文章以前,先仔細思考如下這幾個問題。編程

  • 什麼是redux
  • redux給咱們了什麼好處,咱們爲何要使用它
  • 它的基本思想是什麼
  • redux是否真的適合咱們

ok,咱們開始正式的介紹redux。redux

Redux

爲何須要狀態管理

在咱們一開始構建應用的時候,也許很簡單。咱們有一些狀態,直接把他們映射成視圖就能夠了。這種簡單應用可能並不須要狀態管理。性能優化

可是隨着功能的增長,你的應用程序將會有幾十個甚至上百個狀態。這個時候你的應用應該會是這樣。markdown

Wow,這是什麼鬼。咱們很難再清楚的測試維護咱們的狀態,由於它看上去實在是太複雜了!並且還會有多個頁面共享同一個狀態,例如當你進入一個文章點贊,退出到外部縮略展現的時候,外部也須要顯示點贊數,這時候就須要同步這兩個狀態。

這時候,咱們便迫切的須要一個架構來幫助咱們理清這些關係,狀態管理框架應運而生。架構

redux是什麼

Redux是一種單向數據流架構,能夠輕鬆開發,維護和測試應用程序。app

  • 咱們在Redux中,全部的狀態都儲存在Store裏。這個Store會放在App頂層。
  • View拿到Store儲存的狀態(State)並把它映射成視圖。View還會與用戶進行交互,用戶點擊按鈕滑動屏幕等等,這時會由於交互須要數據發生改變。
  • Redux讓咱們不能讓View直接操做數據,而是經過發起一個action來告訴Reducer,狀態得改變啦。
  • 這時候Reducer接收到了這個action,他就回去遍歷action表,而後找到那個匹配的action,根據action生成新的狀態並把新的狀態放到Store中。
  • Store丟棄了老的狀態對象,儲存了新的狀態對象後,就通知全部使用到了這個狀態的View更新(相似setState)。這樣咱們就可以同步不一樣view中的狀態了。

Lets do it!

這裏咱們以一個最簡單的CountApp舉例。簡單介紹flutter_redux/redux的用法。該項目完整代碼已上傳Github框架

這是一個在不一樣頁面使用Redux共享狀態信息的app。這兩個頁面都依賴於一個數字,這個數字會隨着咱們按下按鈕的次數而增長。

第一步:添加依賴

咱們這裏使用了redux/flutter_redux庫,它們都是由Brian Egan大神編寫的。其中flutter_redux是用來簡化redux的使用的。

第二步:建立State

咱們剛纔介紹了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還會摸不着頭腦。action究竟是什麼?View如何發出action。其實,action只是咱們對狀態進行操做方法的一個代號而已。在咱們這個應用中,惟一的一個功能就是讓count的值+1,因此咱們這裏只有一個action。

/** * 定義操做該State的所有Action * 這裏只有增長count一個動做 */
enum Action{
  increment
}
複製代碼

第四步:建立reducer

reducer是咱們的狀態生成器,它接收一個咱們原來的狀態,而後接收一個action,再匹配這個action生成一個新的狀態。

/** * reducer會根據傳進來的action生成新的CountState */
CountState reducer(CountState state,action){
  //匹配Action
    if(action == Action.increment){
      return CountState(state.count+1);
    }
    return state;
}
複製代碼

第五步:建立store

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));
}
複製代碼

第六步:將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(),
      ),
    );
  }
}
複製代碼

第六步:在子頁面中獲取Store中的state

這裏建議你們把實際代碼對照下面的解釋一塊兒看。

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。

  • 首先這裏須要強制聲明類型,S表明咱們須要從store中獲取什麼類型的state,ViewModel指的是咱們使用這個State時的實際類型。
  • 而後咱們須要聲明一個converter<S,ViewModel>,它的做用是將Store轉化成實際ViewModel將要使用的信息,好比咱們這裏實際上要使用的是count,因此這裏將count提取出來。
  • builder是咱們實際根據state建立Widget的地方,它接收一個上下文context,以及剛纔咱們轉化出來的ViewModel,因此咱們就只須要把拿到的count放進Text Widget中進行渲染就行了。

第七步:發出action

咱們這個應用在第二個頁面中,經過點擊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),
          );
        },
      ),
複製代碼
  • 一樣,咱們仍是使用StoreConnector<S,ViewModel>。這裏因爲是發出了一個動做,因此是VoidCallback。
  • store.dispatch發起一個action,任何中間件都會攔截該操做,在運行中間件後,操做將被髮送到給定的reducer生成新的狀態,並更新狀態樹。

以上即是在flutter中使用redux共享狀態信息的所有內容。

Q&A

ViewModel性能優化

咱們的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嗎

咱們發現,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以及流式編程,敬請關注。

相關文章
相關標籤/搜索