flutter_blocのExampleの説明の続きです。「+」「-」ボタンを押した際に使われるBLoCのコードを解説します。
BLoCの説明
「+」「-」ボタンを押すと中央の数値が増減しますが、これはCounterBloc
を使っています。
動作の説明
- ユーザーがボタンを押す等の作業により、UIがBLoCにEventを送る。
- 呼び出されたBLoCのメソッドがStateを変更して、その変更を通知する。
- 新しいStateを通知として受け取ったUIが再描画する。
Cubitと異なるのはUIがメソッドを呼び出すのではなく、Eventを送ることです。通常Eventは複数種類あり、その中のひとつを送ります。
今回のExampleでは「+」ボタンを押すと CounterEvent.increment
、「-」を押すとCounterEvent.decrement
を送ります。このExampleではCounterEvent
はenum
になっています。
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となっている
- BLoCの
こんな感じで使い分けるといいと思います。