1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

わたしのFlutterお勉強用Advent Calendar 2020

Day 3

flutter_blocを使ってみる その3

Last updated at Posted at 2020-12-03

flutter_blocのExampleの説明の続きです。「+」「-」ボタンを押した際に使われるBLoCのコードを解説します。

BLoCの説明

「+」「-」ボタンを押すと中央の数値が増減しますが、これはCounterBlocを使っています。

動作の説明

  • ユーザーがボタンを押す等の作業により、UIがBLoCにEventを送る
  • 呼び出されたBLoCのメソッドがStateを変更して、その変更を通知する。
  • 新しいStateを通知として受け取ったUIが再描画する。

Cubitと異なるのはUIがメソッドを呼び出すのではなく、Eventを送ることです。通常Eventは複数種類あり、その中のひとつを送ります。
今回のExampleでは「+」ボタンを押すと CounterEvent.increment、「-」を押すとCounterEvent.decrementを送ります。このExampleではCounterEventenumになっています。

BLoCの中身の動作の説明

  • int型初期値0のStateを持ちます。
  • UIから受け取ったイベントに応じてmapEventToState()メソッドがStateを変更して通知します。

コードの説明

BLoCのコードはこのようになっています。

// Eventをenumで定義、incrementとdecrementの2つを持つ。
enum CounterEvent {
  increment,
  decrement
}

class CounterBloc extends Bloc<CounterEvent, int> {
  // 初期値を0に設定する
  CounterBloc() : super(0);

  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    // Eventの種類によってStateを変更して通知する
    switch (event) {
      case CounterEvent.decrement:
        yield state - 1;
        break;
      case CounterEvent.increment:
        yield state + 1;
        break;
      default:
        addError(Exception('unsupported event'));
    }
  }
}

UIのコードの抜粋です。

class CounterPage extends StatelessWidget {
// ...省略
// Stateを受け取って再描画する。
body: BlocBuilder<CounterBloc, int>(
  builder: (_, count) {
    return Center(
      child: Text('$count', style: Theme.of(context).textTheme.headline1),
    );
  },
),
// ...省略
// FloatingActionButtonを押すとCounterEvent.increment(or decrement)が送られる
FloatingActionButton(
  child: const Icon(Icons.add),
  onPressed: () => context.read<CounterBloc>().add(CounterEvent.increment),
),
//...省略

もう少し詳しい説明

flutter_blocにてBLoCはEventのStreamを受け取り、StateのStreamを通知(broadcast)します。コードをみると mapEventToState() メソッドがStreamを受け取りStreamを返していることがわかると思います。
これは例えばデータ取得の際に、まずLoadingState、次に取得結果に応じてSuccessStateまたはErrorStateを通知するというように、複数のStateを順次通知するような挙動を前提としています。

コードで書くと以下のようになります。Loading中はViewにスピナーを表示しておいて、データが取得できたらリストを表示する、というようなことができますね。

if (event is FetchData) {
  yield LoadingState();
  try {
    final data = await repository.getData();
    yield SuccessState(data);
  } catch (error) {
    yield ErrorState(error);
  }
}

BLoCとCubitとの違い

これまでにBLoCとCubitの挙動を説明したので、両者の違いも説明できるかなと思います。

  • BLoCは複数種別のEventを受け取ることができるが、Cubitは複数種別を扱えない
    • ExampleではCubitはColorThemeの切り替えのみ、BLoCは数値のインクリメント、デクリメントの2種類のイベントを使っている
  • BLoCは連続したEventをStreamで受け取り都度StateをStreamで返すが、CubitはひとつのEventを処理する
    • BLoCのmapEventToState()はStream型のメソッドだが、Cubitのemit()はvoidとなっている

こんな感じで使い分けるといいと思います。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?