BLoC — とは何か、FlutterにおけるBusiness Logic Component

著者: IT Sectr 公開日: 2026-02-19 読了時間: 7 分

BLoC(Business Logic Component)— Googleが2018年のDartConfで発表したFlutter用の状態管理パターンです。BLoCはリアクティブストリーム(Stream)を介してビジネスロジックとユーザーインターフェースを分離します。UIがEventを送信し、BLoCがそれを処理してStream経由で新しいStateを返します。pub.devによると、flutter_blocパッケージは11,000以上のいいねを獲得し、何千ものFlutterアプリケーションで使用されています。

主なポイント

  • Event — アクションを記述する入力信号:ボタン押下、データ読み込み
  • State — UIの出力状態:データ読み込み済み、エラー、読み込み中
  • Bloc — Eventを受け取りStream経由でStateを返すメインクラス
  • Cubit — EventなしのBlocの簡略版、関数を直接呼び出す
  • BlocProvider — Blocをウィジェットツリーに注入するFlutterウィジェット

BLoCとは?

BLoC(Business Logic Component)— Flutter用のアーキテクチャパターンで、ビジネスロジックをUIから分離した独立したクラスに抽出します。BLoCはイベントのストリーム(Event)を介して入力データを受け取り、状態のストリーム(State)を介して出力データを生成します。プレゼンテーション層(Widget)はStateストリームにサブスクライブしてUIをレンダリングするだけで、ビジネスロジックを直接実行することはありません。

BLoCの概念はリアクティブプログラミングとObserverパターンに基づいています。各BLoCコンポーネントは、明確な契約を持つ独立したモジュールです。既知のEventのセット(何が起こり得るか)と既知のStateのセット(何が表示され得るか)があります。開発者はUIから「誤って」状態を変更することはできません — 特定のEventを介してのみ可能です。これにより、コードは予測可能でテスト可能になります。

Flutter Community 2025の調査によると、BLoCはProviderに次いでFlutterの状態管理ソリューションで人気第2位です。主な利点:強い型付け、ロジックの分離、組み込みのStreamサポート、豊富なユーティリティエコシステム(BlocProvider、BlocListener、BlocSelector)。

BLoCのアーキテクチャ:Event → Bloc → State

BLoCアーキテクチャは3つのエンティティを中心に構築されています:Event(入力)、Bloc(ハンドラ)、State(出力)。Widgetはadd()メソッドを介してEventを送信します。BlocはmapEventToStateまたはon<Event>メソッドでEventを受け取り、ビジネスロジックを実行し、yieldを介して新しいStateを発行します。WidgetはStreamを介してStateを受け取り、再構築します。

Dart
abstract class CounterEvent {}

class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0);

  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    if (event is Increment) {
      yield state + 1;
    } else if (event is Decrement) {
      yield state - 1;
    }
  }
}

タイプセーフティ: BlocはEventとStateの2つの型でパラメータ化されています。Dartコンパイラは、Widgetが宣言されたEventのみを呼び出し、Blocが宣言されたStateのみを返すことをチェックします。「不明なアクション」のような実行時エラーは排除されます。

CloseとDispose: BlocはCloseableインターフェースを実装します。ウィジェットが破棄されると、Blocは自動的にclose()メソッドを介してStreamを閉じます。リアクティブサブスクリプションのリークは不可能です — BlocProviderがBlocのライフサイクルを管理し、ルートまたはページにバインドします。

BlocとCubit:比較

Cubitは、flutter_bloc 6.0パッケージで導入された、EventなしのBlocの簡略化された実装です。CubitはEventクラスの代わりに直接メソッドを宣言します:increment()fetchData()。内部的には、Cubitは同じStreamベースのメカニズムを使用しますが、Event層を隠します。これにより、単純なシナリオではボイラープレートが40〜50%削減されます。

特性BlocCubit
Eventクラス必須不要
ボイラープレート高い低い
アクション追跡Eventタイプ経由メソッド名のみ
最適な用途複雑なシナリオ単純な状態
分析Eventによる自動手動

Cubitを選ぶ場合: 2〜3のバリアントがある状態(loading、loaded、error)、単純なフォーム、カウンター、UI状態(開く/閉じる)。Blocを選ぶ場合: 複数のアクションを持つ複雑なビジネスロジック:注文処理、認証、データ同期。BlocはEventを通じて各アクションの詳細なトレースを提供します — すべての呼び出しはBlocObserverに記録されます。

BlocObserver — アプリケーション内のすべてのBlocとCubitを追跡するグローバルオブザーバーです。Event、State、エラー、遷移をログに記録できます。1つのインスタンスを接続するだけです:Bloc.observer = AppBlocObserver()、アプリケーション全体の状態追跡が集中管理されます。

BlocProviderとBlocBuilder

BlocProviderflutter_blocのInheritedWidgetで、子ウィジェットにBlocを提供します。ウィジェットが初期化されると、BlocProviderはBlocを作成し、破棄されるとclose()を介して自動的に閉じます。BlocProviderはMaterialAppレベル(グローバルBloc)または特定のルートレベル(ローカルBloc)に配置できます。

Dart
BlocProvider(
  create: (context) => CounterBloc(),
  child: Column(
    children: [
      BlocBuilder<CounterBloc, int>(
        builder: (context, state) => Text('$state'),
      ),
      ElevatedButton(
        onPressed: () => context.read<CounterBloc>().add(Increment()),
        child: Text('+'),
      ),
    ],
  ),
)

BlocBuilder — 新しいStateごとにUIを再構築するウィジェット。BlocListener — 副作用用(UIを再構築せずにStateを1回処理):SnackBarを表示、別の画面に移動。BlocConsumer — 再構築と副作用の両方が必要な場合のBuilderとListenerの組み合わせ。BlocSelector — Stateの特定のフィールドが変更された場合のみ選択的に再構築します。

MultiBlocProvider — ネストレベルを増やさずにネストされたBlocProviderを使用するためのウィジェット。10〜15個のBlocを持つFlutterアプリケーションは、ルートレベルでMultiBlocProviderを使用して、アプリケーション全体で利用可能なすべてのBloc(AuthenticationBloc、CartBloc、SettingsBloc)を登録します。

BLoCのテスト

BLoCのテストは、Flutterウィジェットなしで分離して行われます。Dartパッケージflutter_testbloc_testパッケージをインポートするだけです。テストシナリオ:Blocを作成し、Eventを追加し、Stateを確認します。blocTest — build → act → expectのシーケンスを自動化するユーティリティです。

Dart
blocTest<CounterBloc, int>(
  'emits [1] when Increment is added',
  build: () => CounterBloc(),
  act: (bloc) => bloc.add(Increment()),
  expect: () => [1],
)

モッキング: リポジトリまたはAPIに依存するBlocは、mocktailを介してモックでテストされます。リポジトリは抽象化レベルでモックされ、Blocはコンストラクタを介してモックされた依存関係を受け取ります。Hydrated Bloc — ローカルストレージへの自動状態保存/復元のための拡張機能。HydratedBlocStorageと一時ファイルストレージを使用してテストされます。

本番環境でのBLoC

フォルダとファイル: BLoCを使用した典型的なFlutterプロジェクト構造:bloc/counter_bloc.dartbloc/counter_event.dartbloc/counter_state.dart。30画面以上の場合は、機能ベースのグループ化が推奨されます:features/auth/bloc/features/cart/bloc/。各Blocは別ファイル、各EventとStateは別ファイルまたはBlocと同じファイルに配置します。

パフォーマンス: BLoCは空のStreamにオーバーヘッドを作成しません。BlocBuilderbuildWhenを使用して再構築をフィルタリングします — 特定の条件が変更された場合のみウィジェットが更新されます。Closeにより、非アクティブなBlocがメモリを消費しないことが保証されます。Flutter DevToolsによると、BLoCはバンドルサイズに1%未満しか追加しません。

Providerからの移行: BLoCは同じプロジェクト内でProviderと簡単に共存できます。段階的な移行:最初に最も複雑なProviderをBlocに置き換え、次に残りを置き換えます。BlocProviderはProviderツリーと互換性があります:古いウィジェットはProviderを、新しいウィジェットはBlocProviderを、同じアプリケーション内で使用できます。

よくある質問

FlutterでBLoCはProviderとどう違うのですか?

BLoCはビジネスロジックの分離と強い型付けにEvent + Streamを使用します。Providerは単純な依存性注入とChangeNotifierのためのInheritedWidgetのラッパーです。BLoCは複数の状態を持つ複雑なシナリオに適しており、ProviderはローカルなUI状態に適しています。BLoCはより多くのボイラープレートを必要としますが、Eventを通じて完全なトレーサビリティを提供します。

Hydrated Blocとは?

Hydrated Blochydrated_blocパッケージの拡張機能で、最後のStateをローカルストレージ(デフォルトではHive)に自動的に保存します。アプリケーションが再起動すると、Blocは初期状態ではなく保存された状態を復元します。これにより、手動で保存を呼び出さなくても永続性の問題が解決されます:ログイン、カート、設定がセッション間で自動的に保存されます。

BLoCでエラーを処理する方法は?

BLoCのエラーは、mapEventToStateまたはon<Event>内でtry-catchを使用して処理されます。エラーが発生すると、BlocはエラーStateを返します:yield LoadError(error.message)。UIでは、BlocListenerまたはBlocConsumerがStateのエラータイプをチェックし、SnackBarまたはダイアログを表示します。BlocObserverは未処理の例外をすべてグローバルにログに記録します。

BLoCは他のフレームワークでも使用できますか?

BLoCはDart StreamとFlutterウィジェットを使用するため、Flutter固有のパターンです。Event → Bloc → Stateの概念はAngularDartServer-side Dartに適応できますが、主要なエコシステム(BlocProvider、BlocBuilder、BlocObserver)はFlutterに依存しています。React Nativeの場合はReduxまたはMobXを、SwiftUIの場合はCombine + MVVMを使用してください。

BlocとCubitのどちらを選ぶべきですか?

Cubit — 単純な状態(カウンター、トグル、2〜3フィールドのフォーム)向け。Bloc — 複雑なロジック(ニュースフィード、注文処理、認証)向け。主なルール:分析やデバッグのために各アクション(Event)のトレースが必要な場合はBlocを選択します。状態を変更するメソッドで十分な場合はCubitを選択します。両方のパターンは同じプロジェクト内で共存できます。

まとめ

  • BLoC — Event → Stream → StateによるFlutter状態管理パターン
  • Event — アクション(押す、読み込む)、State — 反応(データ、エラー、読み込み中)
  • Cubit — Eventなしの簡略版、最大50%少ないボイラープレート
  • BlocProvider — 自動close付きでBlocをウィジェットツリーに注入
  • BlocObserver — アプリケーション内の全BlocとCubitのグローバル監視
  • Hydrated Bloc — Hiveによる自動状態永続化
  • blocTest — Flutterから分離されたBlocの単体テスト用ユーティリティ

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください