FlutterのBLoCパターンについての説明

Flutterの状態管理手法のBLoCパターンを学習したのでアウトプットする。

# 重要な前提
**知識面については[こちらの記事](https://medium.com/flutter-jp/bloc-provider-70e869b11b2f)を読めばほぼ解決します。**私はQiita書き終わってから気づきました。


# 本記事が提供するもの
- サンプルを動かしながらBLoCパターンを学習する方法として、[Getting Started with the BLoC Pattern](
https://www.raywenderlich.com/4074597-getting-started-with-the-bloc-pattern)を紹介する。

これで終了なのだが、せっかくなので私が学んだことをアウトプットしてみる。

# 前提
- 現在BLoCパターンは、Flutterの状態管理手法として**最適とは考えられていない**。
- 実用では[Provider](https://pub.dev/packages/provider), [RiverPod](https://pub.dev/packages/riverpod)等を用いるのが主流であると思う。
- 状態管理手法の学習は[InheritedWidget](https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html)使用から始めるのがよいのだと思う。InheritedWidgetをラップしたのが前述のProviderである
- BLoCパターンを体系的に学習する際は本記事ではなく、[こちら](https://qiita.com/kabochapo/items/8738223894fb74f952d3)を参照するのがよい。

# BLoCパターンについての私の理解
- BLoCパターンはWidget Treeとビジネスロジック（BLoC）をきれいに分離できてよい。
- 分離の仕組みを以下に記す。

## BLoCとWidgetTreeの関係
- ボタンを押す等の操作時にWidgetからBLoCに情報を伝える
- BLoCがその情報をWidgetに伝える
- （情報を受け取ったWidgetがStreamBuilder等で表示を更新する）

![BLoC全体](https://cdn-ak.f.st-hatena.com/images/fotolife/s/sudame/20190422/20190422191730.png)
出典: [すめだブログ FlutterのBLoCパターンについて得た知見](https://sudame.hatenablog.com/entry/2019/04/24/235702)

## BLoCの中身
- Sinkで情報を受取る
- Streamで情報を流す

![BLoC](https://koenig-media.raywenderlich.com/uploads/2020/08/04-BLoC-diagram-1.png)
出典: [Getting Started with the BLoC Pattern](
https://www.raywenderlich.com/4074597-getting-started-with-the-bloc-pattern)

## サンプル: 最もシンプルなBLoCパターン活用例
以上で説明したように、BLoCそのものは単純な仕組みである。最も単純なBLoC利用サンプルを見つけたので紹介する。

- [Flutter DemoアプリをBLoCパターンで書き直してみた](https://qiita.com/k-nasa/items/f3ec0140feb9e57fcb6a)

ただしアプリの構成が複雑になるとBLoCを利用する工夫が必要になってきて、そこが複雑になりうる。


# BLoCProvider = BLoCパターンを利用する場合の工夫
上に述べたものが恐らくBLoCパターンの全貌であるが、Widget Treeの階層がある場合は、以下の問題に対処する必要がある。

配下のWidgetからBLoCを利用できるようにするため、以下の対応が必要になる。

- あるWidgetでBLoCを保持する
- 配下のWidgetからBLoCがアクセス可能にする

また、あるWidgetでBLoCを保持するため、以下の対応が必要になる。

- BLoCを保持するWidgetが破棄された際に、BloC内のstreamをcloseする

そこでBLoCProviderと呼ばれる仕組みが必要になる。
上述の[シンプルな例](https://qiita.com/k-nasa/items/f3ec0140feb9e57fcb6a)はBloCを使うのは1階層のStatelessWidget内なので、ここで挙げた対応は不要であり、BLoCProviderは必要ない。

## BLoCProvider
BLoCを使う配下の階層を持つ構成では、以下の処理をするような仕組みが必要になる。

- あるWidgetでBLoCを保持する
- 配下のWidgetからBLoCへのアクセスを可能にする
- BLoCを保持するWidgetが破棄された際に、BloC内のstreamをcloseする

[チュートリアル](
https://www.raywenderlich.com/4074597-getting-started-with-the-bloc-pattern)ではStatefulWidgetである`BlocProvider` クラスがそれを担っている。この `BlocProvider` クラスは、以下の方法で問題に対応している。


- プロパティとしてBLoCを保持する
- [`findAncestorWidgetOfExactType`](https://api.flutter.dev/flutter/widgets/BuildContext/findAncestorWidgetOfExactType.html) で配下のWidgetからアクセス可能にする
- `dispose()` 内で`StreamController`を`close()`する

`dispose()` を利用するためにStatefulWidgetにしたというわけだ。


## 計算量O(N)の解決
上述の方法の問題点として、`findAncestorWidgetOfExactType` の計算量がO(N)であることが挙げられる。これによってWidgetTreeの規模が大きくなるとパフォーマンスが悪くなるのだ。

これを解決して、BlocProviderにO(1)でアクセスするには、さらにInheritedWidgetを組み込んだ仕組みにするとよい。

そのためのライブラリとして [`bloc_provider`](https://pub.dev/packages/bloc_provider)がある。
また、以下の用にProviderと併用する方法もあるようだ。
[Flutter BLoC+Providerとマルチプロバイダーのサンプル(戻るボタンのフックで苦労した話)](https://qiita.com/kasa_le/items/33e62b09905a7c635f16)

# まとめ
- BLoCパターンの本質は、[この記事](https://qiita.com/k-nasa/items/f3ec0140feb9e57fcb6a)により最短で把握できる。
- BLoCパターンの実用は[このチュートリアル](https://www.raywenderlich.com/4074597-getting-started-with-the-bloc-pattern)でサンプルを動かしながら把握できる。
- これに加えて、[この記事](https://qiita.com/kasa_le/items/33e62b09905a7c635f16)や[このライブラリ](https://pub.dev/packages/bloc_provider)を参考に計算量の解決をするとよい。


# さらにまとめ
- [こちらの記事](https://medium.com/flutter-jp/bloc-provider-70e869b11b2f)を読めば全部解決できることに気づいた。
- この記事にあるように、現在のスタンダードはBLoCではなくprovider + ChangeNotifierと考えてよいと思う。ただしこちらの詳しい解説は本稿の範囲外とする。