MENU

編集中【Flutter】Bloc/Cubit の状態管理をまとめる flutter_bloc編

flutter_bloc_library

この記事を読むのに必要な時間は約 17 分です。

この記事ではBloc/Cubit の使い方についてBlocライブラリの内容を元にまとめています。後編です笑

前編でBlocとCubitのクラスについて説明しています。先にそちらを見ていただくと理解が深まると思います。

Blocライブラリのドキュメントはこちら。

それではflutter_blocのWidgetを紹介していこうと思います!

目次

Bloc Widget

BlocBuilder

BlocBuilder はBloc とbuilder を必要とするFlutterのWidget です。
BlocBuilder はstate が新しく更新された際にBlocBuilder でラップされているWidget をビルドします。

BlocBuilder<BlocA, BlocAState>(
  builder: (context, state) {
    // BlocA's state に紐づくWidget をここに記入します。
  }
)

単一のWIdgetにスコープされ、BlocProvider と現在の BuildContext からアクセスできない bloc を提供したい場合にのみ、bloc プロパティを指定します。

BlocBuilder<BlocA, BlocAState>(
  bloc: blocA, // ローカルなbloc インスタンスをここで指定できます。
  builder: (context, state) {
  }
)

buildWhenプロパティは、前のBlocの状態と現在のBlocの状態を引数に取り、ブール値を返します。
buildWhen が true を返すと、builder が状態とともに呼び出され、ウィジェットが再構築されます。
buildWhen が false を返すと、builder は状態付きで呼び出されず、再構築は行われません。

BlocBuilder<BlocA, BlocAState>(
  buildWhen: (previousState, state) {
    // stateに紐づくWidgetを再構築するかどうかを決めるbool値をここで返します。
  },
  builder: (context, state) {
  }
)

BlocListener

BlocListenerはFlutterのWidgetで、BlocWidgetListenerとBlocを必要とし、Blocの状態変化に応してlistenerを呼び出します。ナビゲーション、SnackBarの表示、ダイアログの表示など、状態の変化に対して一度だけ発生する必要がある機能に対して使われるべきものです。
listenerは、BlocBuilderのbuilderとは異なり、状態が変わるたびに一度だけ呼ばれる(初期状態は含まれない)void関数です。

BlocListener<BlocA, BlocAState>(
  bloc: blocA,
  listener: (context, state) {
  },
  child: Container()
)

listner関数が呼び出されるタイミングを細かく制御するため、listenWhenを提供することができます。listenWhenは、前のブロックの状態と現在のブロックの状態を取り、ブール値を返します。
listenWhenがtrueを返した場合、listnerはstateとともに呼び出されます。
listenWhenがfalseを返した場合、リスナーはステートとともに呼び出しません。

BlocListener<BlocA, BlocAState>(
  listenWhen: (previousState, state) {

  },
  listener: (context, state) {

  },
  child: Container(),
)

BlocProvider

BlocProviderはFlutterのWIdgetで、BlocProvider.of(context)を介して子プロセスにBlocを提供します。これは依存性注入(DI)ウィジェットとして使われ、サブツリー内の複数のウィジェットに1つのBlocのインスタンスを提供することができます。
つまり、Child A()の中でBlocBuilderなどを呼び出した際にBlocのインスタンスを与えることができます。

ほとんどの場合、BlocProviderは、サブツリーの残りの部分で利用できるようになる新しいブロックを作成するために使用されるべきです。

BlocProvider(
  create: (BuildContext context) => BlocA(),
  child: ChildA(),
);

デフォルトでは、BlocProviderはblocを遅延して作成します。
つまり、BlocProvider.of(context)によってblocが検索されたときに、createが実行されます。

createがすぐに実行されるようにするには、lazyをfalseに設定すればOK。
BlocProviderは、ブロックを作成する役割を担い、Blocを閉じる処理を自動的に行います。

BlocProvider(
  lazy: false,
  create: (BuildContext context) => BlocA(),
  child: ChildA(),
);

BlocProvider を使って、ウィジェットツリーの新しい部分に既存のBlocを提供することができます。
これは、既存のBlocを新しいルートで利用できるようにするときによく使われます。
この場合、BlocProviderはBlocを作成しないので、自動的にBlocを閉じません。

BlocProvider.value(
  value: BlocProvider.of<BlocA>(context),
  child: ScreenA(),
);

ChildAかScreenAからBlocAを取得することができます。

// with extensions
context.read<BlocA>();

// without extensions
BlocProvider.of<BlocA>(context)

Blocパラメータを省略すると、BlocListenerは自動的にBlocProviderと現在のBuildContextを使用してProviderの供給したBlocの検索を行います。

BlocListener<BlocA, BlocAState>(
  listener: (context, state) {
    //  BlocAの stateに紐づく処理を行う
  },
  child: Container(),
)

BlocProvider と現在の BuildContext からはアクセスできないBlocを提供したい場合にのみ、blocプロパティに指定します。

BlocConsumer

BlocConsumerは、新しい状態に反応するためにbuilderとlistenerの両方を使えるハイブリッドです。BlocConsumerは、UIの再構築とブロックの状態変化に対する他のリアクションを実行する必要がある場合にのみ使用する必要があります。BlocConsumerは、必須のBlocWidgetBuilderとBlocWidgetListener、そしてオプションでbloc、BlocBuilderCondition、BlocListenerConditionを指定できます。

Blocパラメータを省略すると、BlocConsumerは自動的にBlocProviderと現在のBuildContextを使用して検索を行います。

BlocConsumer<BlocA, BlocAState>(
  listener: (context, state) {

  },
  builder: (context, state) {

  }
)

オプションの listenWhen と buildWhen を実装することで、リスナーとビルダーが呼ばれるタイミングをより細かく制御することができます。listenWhenとbuildWhenは、ブロックの状態が変わるたびに呼び出され、それぞれ、前のステートと現在のステートを受け取り、ビルダーやリスナー関数が呼び出されるかどうかを決定するboolを返さなければならない。以前の状態は、BlocConsumerが初期化されたときのブロックの状態に初期化されます。listenWhenとbuildWhenはオプションで、実装されていない場合、デフォルトはtrueです。

BlocConsumer<BlocA, BlocAState>(
  listenWhen: (previous, current) {

  },
  listener: (context, state) {

  },
  buildWhen: (previous, current) {

  },
  builder: (context, state) {

  }
)

RepositoryProvider

RepositoryProviderはFlutterのWidgetで、RepositoryProvider.of(context)を介して子プロセスにリポジトリを提供するものです。これは依存性注入(DI)ウィジェットとして使われ、リポジトリの単一のインスタンスをサブツリー内の複数のウィジェットに提供することができます。

BlocProvider は bloc を提供するために使用されるべきで、RepositoryProvider はリポジトリにのみ使用されるべきです。

RepositoryProvider(
  create: (context) => RepositoryA(),
  child: ChildA(),
);

BlocSelector

BlocSelectorはBlocBuilderに似たFlutterのWidgetですが、開発者は現在のBlocの状態に基づいて新しい値を選択することで、更新をフィルタリングすることが可能です。選択された値が変化しない場合は、不要なビルドを防ぐことができます。BlocSelectorがbuilderを再度呼び出すべきかどうかを正確に判断するために、選択された値は不変である必要があります。

blocパラメータが省略された場合、BlocSelectorは自動的にBlocProviderと現在のBuildContextを使用して検索を行います。

BlocSelector<BlocA, BlocAState, SelectedState>(
  selector: (state) {
    // return selected state based on the provided state.
  },
  builder: (context, state) {
    // return widget here based on the selected state.
  },
)

拡張機能

context.read

context.read()は、T型の最も近い祖先インスタンスを検索します。機能的にはBlocProvider.of(context)と同等です。context.readは、onPressedコールバック内でイベントを追加するために、blocインスタンスを取得するのに最もよく使用されています。

コールバックでイベントを追加するには、context.readを使用します。

onPressed() {
  context.read<CounterBloc>().add(CounterIncrementPressed()),
}

エラーが起きるため下記のようにbuildメソッド内でcontext.readを使用して状態を取得することは避けた方がいいようです。

@override
Widget build(BuildContext context) {
  final state = context.bloc<MyBloc>().state;
  return Text('$state');
}

context.watch

context.read() と同様に、 context.watch() は T 型の最も近い祖先のインスタンスを提供しますが、インスタンスの変更もlistenします。機能的には BlocProvider.of(context, listen: true) と同じでです。
提供された T 型の Object が変更されると、 context.watch が再構築のトリガーとなります。

Widget build(BuildContext context) {
  return MaterialApp(
    home: Scaffold(
      body: BlocBuilder<MyBloc, MyState>(
        builder: (context, state) {
//状態が変更されたときTextだけがリビルドされます。l
          return Text(state.value);
        },
      ),
    ),
  );
}

また、Builderを使用してリビルドの範囲を指定することもできます。

@override
Widget build(BuildContext context) {
  return MaterialApp(
    home: Scaffold(
      body: Builder(
        builder: (context) {
          // Whenever the state changes, only the Text is rebuilt.
          final state = context.watch<MyBloc>().state;
          return Text(state.value);
        },
      ),
    ),
  );
}

カウンターアプリでのCubit実装例

カウンターアプリでの基本的なCubitの実装を下記の記事で行ってます。

具体的な使い方を知りたい方はぜひご覧ください!

天気アプリでのRepository&Cubit実装例

天気アプリでのRepositoryを使ったCubitの実装を下記の記事で行ってます。

こちらも合わせてご覧ください!

まとめ

前編に引き続きBlocアーキテクチャについて、主要なWidgetを説明しました。

今後は具体的アプリ実装例をたくさん記事にしていけたらと思います!

ではまた!

カテゴリー

よかったらシェアしてね!
  • URLをコピーしました!

コメント

コメントする

目次