Flutter-BLoC-第三講

Silence_Zhou發表於2019-04-02

本篇已同步到 個人部落格 ,歡迎常來。

[TOC]

本文為 《Flutter Bloc Package》 的譯文,原文地址,若轉載本譯文請註明出處。

image.png

在使用Flutter工作一段時間之後,我決定建立一個軟體包以幫助我經常使用的東西:BLoC模式。 對於那些不熟悉BLoC模式的人來說,它是一種設計模式,有助於將表示層與業務邏輯分開。你在這裡瞭解更多。

使用BLoC模式可能具有挑戰性,因為需要建立對Streams和Reactive Programming的理解。但它的核心是BLoC非常簡單:

BLoC 將event流作為輸入,並將它們轉換為state流作為輸出。

image.png

我們現在可以在bloc的dart包的幫助下使用這種強大的設計模式。

該軟體包抽象了模式的反應方面,允許開發人員專注於將事件(event)轉換為狀態(state)。

讓我們從定義這些術語開始......

詞彙表

Events

Events 是Bloc的輸入。它們通常是UI事件,例如按鈕按下。Events被分發(dispatched)並且被轉換為States。

States

States 是Bloc的輸出。表示元件可以監聽狀態流 並根據給定狀態重繪其自身的部分(BlocBuilder有關詳細資訊,請參閱參考資料)。

Transitions

Transitions 發生在 呼叫mapEventToState之後 但在更新了bloc的state之前 排程了一個Event

現在我們瞭解事件和狀態,我們可以看一下Bloc API。

BLOC API

mapEventToState

當一個類繼承Bloc時,必須實現 mapEventToState 方法, 該函式將傳入事件作為引數。 只要UI層觸發一個事件,就會呼叫 mapEventToState。 mapEventToState 必須將該event轉換為新state,並以UI層使用的Stream形式返回新狀態。

dispatch

dispatch 是一個 接受 event 並觸發 mapEventToState 的方法。 可以從表示層呼叫dispatch 或 從Bloc內部(見例子)並通知Bloc一個新 event。

initialState

initialState是處理任何事件之前的狀態(在mapEventToState被呼叫之前)。 如果未實現,則為initialState null。

transform

transform是一個 在呼叫mapEventToState之前 可以重寫以轉換 Stream . 這允許使用distinct() 和 debounce() 的操作。

onTransition

onTransition 是一個 每次 transform 發生時都可以重寫以進行處理 的方法。 排程新event 並呼叫mapEventToState時發生transition。 onTransition 在更新 bloc 狀態之前 被呼叫。 這是新增特定於塊的日誌記錄/分析的好地方

讓我們建立一個counter bloc!

enum CounterEvent { increment, decrement }

class CounterBloc extends Bloc<CounterEvent, int> {
  @override
  int get initialState => 0;

  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    switch (event) {
      case CounterEvent.decrement:
        yield currentState - 1;
        break;
      case CounterEvent.increment:
        yield currentState + 1;
        break;
    }
  }
}
複製程式碼

建立一個BLoC 需要作如下操作:

  • 定義所有 event 和 state
  • 繼承Bloc
  • 重寫 initialState 和 mapEventToState

在這種情況下,我們的 events 是CounterEvents ,states 是 integers

CounterBloc 轉換 CounterEvents 為 integers。

我們可以通過 dispatch 來 通知CounterBloc 事件

void main() {
  final counterBloc = CounterBloc();

  counterBloc.dispatch(CounterEvent.increment);
  counterBloc.dispatch(CounterEvent.decrement);
}
複製程式碼

為了觀察狀state 的 轉換(Transitions),我們可以重寫onTransition。

enum CounterEvent { increment, decrement }

class CounterBloc extends Bloc<CounterEvent, int> {
  @override
  int get initialState => 0;
  
  @override
  void onTransition(Transition<CounterEvent, int> transition) {
    print(transition);
  }

  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    switch (event) {
      case CounterEvent.decrement:
        yield currentState - 1;
        break;
      case CounterEvent.increment:
        yield currentState + 1;
        break;
    }
  }
}
複製程式碼

現在,每當發出(dispatch)一個CounterEvent我們的Bloc將響應一個新的integer狀態,我們將看到一個transition被輸出到控制檯。

現在讓我們使用Flutter構建一個UI,並使用flutter_bloc 包將UI連線到我們的CounterBloc。

BlocBuilder

BlocBuilder是一個Flutter小部件,它需要一個Bloc和一個構建器函式。 BlocBuilder處理構建視窗小部件以響應新state。 BlocBuilder與StreamBuilder非常相似,但它有一個更簡單的API來減少所需的樣板程式碼量。

BlocProvider

BlocProvider是一個Flutter小部件,它通過 BlocProvider.of(context)為其子女提供了一個bloc。 它用作依賴注入(DI)小部件, 這樣一個bloc例項 可以被提供給子樹中的多個小部件。

現在讓我們構建 counter App

class App extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _AppState();
}

class _AppState extends State<App> {
  final CounterBloc _counterBloc = CounterBloc();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: BlocProvider<CounterBloc>(
        bloc: _counterBloc,
        child: CounterPage(),
      ),
    );
  }

  @override
  void dispose() {
    _counterBloc.dispose();
    super.dispose();
  }
}

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final CounterBloc _counterBloc = BlocProvider.of<CounterBloc>(context);

    return Scaffold(
      appBar: AppBar(title: Text('Counter')),
      body: BlocBuilder<CounterEvent, int>(
        bloc: _counterBloc,
        builder: (BuildContext context, int count) {
          return Center(
            child: Text(
              '$count',
              style: TextStyle(fontSize: 24.0),
            ),
          );
        },
      ),
      floatingActionButton: Column(
        crossAxisAlignment: CrossAxisAlignment.end,
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          Padding(
            padding: EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: Icon(Icons.add),
              onPressed: () {
                _counterBloc.dispatch(CounterEvent.increment);
              },
            ),
          ),
          Padding(
            padding: EdgeInsets.symmetric(vertical: 5.0),
            child: FloatingActionButton(
              child: Icon(Icons.remove),
              onPressed: () {
                _counterBloc.dispatch(CounterEvent.decrement);
              },
            ),
          ),
        ],
      ),
    );
  }
}
複製程式碼

該App小部件是StatefulWidget,負責建立和銷燬 CounterBloc。 它讓 CounterBloc 使用 BlocProvider 小部件可用於 CounterPage 小部件。

CounterPage小部件是StatelessWidget, 它使用BlocBuilder重建UI以響應CounterBloc的狀態變化。

此時,我們已經成功地將我們的表示層與業務邏輯層分開。請注意,CounterPage視窗小部件對使用者點選按鈕時發生的情況一無所知。小部件只是告訴CounterBloc使用者按下了遞增或遞減按鈕。

有關更多示例和詳細文件,請檢視官方集團文件

相關連結:

bloc dart包

flutter_bloc包

flutter_bloc使用官方文件

相關文章