Flutterの状態管理いろいろ比較 〜グローバル変数StateからBLoCパターンまで〜

Flutterをやっていて、ずっと悩み続けるのが状態管理だと思います。

この記事では、いろいろな状態管理手法を使って同じ機能を実装し、それぞれの方法のメリット・デメリットをまとめていこうと思います。

Flutterアプリとして動作させる性質上どうしてもコードが多少長くなって読むのが面倒になりますので

記事内で取り上げるのは重要部分だけにとどめ、コード全体はGithubにあげておきますので適宜参照してください。Flutterプロジェクトまるごと上がっているのでcloneやダウンロードすれば動きます。

それぞれの実装パターンは`my_app_x.dart`という形でファイル別になってます(`x`に数字が入る)

https://github.com/agajo/flutter_state_management_article



#作るアプリの構造

![kakusan2018-06-01T23-59.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85139/e485e605-6865-de09-d02e-82b4a2182ffd.gif)

![Qiita記事用Flutter Widgetツリー (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85139/c968df5d-e267-9ca6-4f65-2a48fbe98467.png)


こんな感じにします。

##機能
`button`を押すと、`count`が1ずつ増えていきます。それだけです。


`button`と`count`はWidgetツリー内の離れた位置にあるので、状態管理手法が必要になります。

`WidgetD`が「WidgetD」という文字を出力しており、この文字は変更されません。

##各ウィジェットの役割
###MaterialAppとScaffold
MaterialAppとScaffoldはアプリとしての体裁を整えるためにあるだけで大して意味はないです。

###MyAppX
MyAppX以下の部分は実装が毎回代わり、

MyApp1→グローバルキーを利用する方法
MyApp2→setState()用関数を渡していく方法

のような感じで、番号を変えながら色々なパターンを作ってそれぞれScaffoldの下にくっつけ、比較してみます。

###WidgetA〜WidgetD
WidgetA〜WidgetDは、build関数内に`print('WidgetA is built.');`のように書いておき、build関数が走るたびにコンソールにメッセージが出るようにします。これで各Widgetがリビルドされたかどうかがわかります。



#方針

状態管理手法を決めた所で、できるコードは一通りではありません。ですが、ある程度方針は決めておきましょう。

## WidgetA〜WidgetDはできるだけリビルドされないようにする。

理想的には、リビルドされる必要があるのは`count`部分の文字だけです。Widgetでいうと`Text`になります。

ですが、それがやりにくい場合は、リビルドされるWidgetを極力少なくします。

## `const`は使わない

今回は、それぞれの構造に対して何がリビルドされるのが把握したいので、`const`は使わないことにします。

`const`を使うことでも無用なリビルドを防ぐことができるので、普段は使える限りどんどん使うべきですが

今回は、構造のおかげでリビルドを防げたのか、それとも`const`のおかげなのか、わからなくなるとまずいので、`const`は使わないことにします。


では前置きはここまでにして、各種構造の比較に参りましょう。

#0. グローバル変数としてStateを持つ(非推奨)

`WidgetC`を`StatefulWidget`にして、その`State`をグローバル変数にします。

そうすれば、どこからでもアクセスできますから、`WidgetA`から遠隔でアクセスして更新するのも楽勝ですね。

コードはこんな感じ。

```dart:my_app_0.dart
class WidgetC extends StatefulWidget {
  WidgetC({this.child});
  final Widget child;

  @override
  // グローバル変数であるStateを返す
  _WidgetCState createState() => _widgetCState;
}

// Stateをグローバル変数として持つ
_WidgetCState _widgetCState = _WidgetCState();

class _WidgetCState extends State<WidgetC> {
  int _count = 0;
  void increment() => setState(() => _count++);
  @override
  Widget build(BuildContext context) {
    print('WidgetCState is built.');
    return Column(
      children: <Widget>[
        Text(_count.toString()),
        widget.child,
      ],
    );
  }
}
```

`WidgetC`の`child`には、親からあらかじめ作った`WidgetD`のインスタンスを渡しておくことで、`WidgetC`のリビルド時に`WidgetD`までリビルドされるのを防ぎます。

操作する側の`WidgetA`はこんな感じ

```dart:my_app_0.dart
class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('WidgetA is built.');
    return RaisedButton.icon(
      icon: Icon(Icons.plus_one),
      label: Text('plus 1'),
      onPressed: () {
        // グローバル変数 _widgetCStateが持つ更新関数を直接呼ぶ
        _widgetCState.increment();
      },
    );
  }
}
```

##メリット

 - リビルドされるのはWidgetCのStateだけ
 - 何をやってるかわかりやすい
 - どこからでも自由に触れる

##デメリット

 - どこからでも触れる→誰が触ってるかわからない→デバッグ困難
 - グローバル変数は不要になってもずっと保持されてパフォーマンスがイマイチ
 - 一般的なグローバル変数のデメリットそのまま

グローバル変数を使うとコードがしっちゃかめっちゃかになりデバッグが困難になります。残念。

#1. 更新対象にグローバルキーを持たせる(非推奨)

Flutterには`Key`という概念がありまして、これを使ってWidgetのインスタンスを区別することができます。

GlobalKeyはアプリ全体を通してユニークであることが保証され、逆にGlobalKeyから、それを付与されているWidgetにアクセスできます。

```dart:my_app_1.dart
// WidgetCに渡すためのGlobalKeyを生成しておく
GlobalKey<_WidgetCState> _globalKey = GlobalKey<_WidgetCState>();
// (中略)
        // WidgetCに生成したGlobalKeyを渡しておく
        WidgetB(child: WidgetC(child: _widgetD, key: _globalKey)),
```
```dart:my_app_1.dart
class WidgetA extends StatelessWidget {
// (中略)
      onPressed: () {
        // GlobalKeyを使って、更新関数を直接呼ぶ
        _globalKey.currentState.increment();
      },

```

##メリット

 - どこからでも触れる
 - リビルドされるのはWidgetCのStateだけ

##デメリット

 - 誰が触ってるかわからない→デバッグ困難
 - GlobalKey自体の動作コストが高い

ということでグローバル変数のデメリットが解決してません。

#2. ancestorStateOfType(非推奨)

グローバルなんたらと名のつくものをむやみに使うのはあまり良くないということがわかりました。

ちゃんとWidgetツリーをたどって状態管理をすることにしましょう。

ということで、ツリー内のどこかで状態を持つ必要があります。状態というのは、今回の場合は、ボタンを押した回数ですね。

ボタンからも、表示用Widgetからもアクセスできなきゃいけないので、MyAppXの下に持つことにします。


![Qiita記事用Flutter Widgetツリー (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85139/065abc47-dc06-be05-c30d-2b251edd082c.png)

`MyAppX`の直下にあるWidget`Hoge`に持ってもらうことにします。

`Hoge`はStatelessWidgetだったりStatefulWidgetだったりします。

また、`Hoge`が直接`_counter`変数を持つこともあれば、`_counter`を含む別のクラスを持つこともあります。


今回は、`Hoge`は`StatefulWidget`にして、`_counter`を直接持つことにしましょう。

`WidgetC`は単体でリビルドされるわけではないので、`StatelessWidget`にしておきます。

```dart:my_app_2.dart
class HogeStatefulWidget extends StatefulWidget {
  @override
  _HogeStatefulWidgetState createState() => _HogeStatefulWidgetState();
}

class _HogeStatefulWidgetState extends State<HogeStatefulWidget> {
  // WidgetAとWidgetDのコンストラクタはbuild()内で呼ばないので、setState()時にリビルドされない
  final WidgetA _widgetA = WidgetA();
  final WidgetD _widgetD = WidgetD();
  int _counter = 0;
  void increment() => setState(() => _counter++);
// (略)
```

`WidgetA`はなんとかしてこの`increment()`を呼べば良いわけです。

そこで、`context.ancestorStateOfType()`というメソッドを使います。

これは、contextの地点からWidgetツリーを先祖に向かって遡って、該当する型を持つ最初のStateを見つけて返します。

Flutterでは、このメソッドを呼ぶためのメソッドを、見つける対象であるStateの方に`of`という名前で持たせるのが一般的でしょう。

```dart:my_app_2.dart
class _HogeStatefulWidgetState extends State<HogeStatefulWidget> {
  //(中略)
  // 与えられた「context」の地点からWidgetツリーを遡ってこのStateを見つけてくるstatic関数
  static _HogeStatefulWidgetState of(BuildContext context) =>
      context.ancestorStateOfType(TypeMatcher<_HogeStatefulWidgetState>());
}
```

呼ぶ側はこう。

```dart:my_app_2.dart
class WidgetA extends StatelessWidget {
//(略)
      // ここからWidgetツリーを遡ってStateを探し、increment()を呼びます。
      onPressed: () => _HogeStatefulWidgetState.of(context).increment(),
//(略)
}
```

##メリット

 - グローバル変数を使っていない
 - Widgetツリーを遡ってStateを見つけに行く、という動作が理解しやすい

##デメリット

 - Widgetツリーを遡るのに`O(N)`のコストがかかる
 - 中間層の`WidgetB`までリビルドされる
 - 呼ぶ側のWidgetが別ファイルに定義されている場合、Stateの型を指定するためにimportが必要になるので、Stateの定義をプライベートにできない

中間層の`WidgetB`のリビルドは、うまくやれば防げるのかもしれないですが、思いつきませんでした。

この方法がFlutter関連の文章で推奨されているのを見たことがないです。

一番致命的なデメリットは1つ目の`O(N)`のコストがかかる所です。

`O(1)`のコストでアクセスできる方法がある(後述)のだから、それを使いましょうということです。

#3. 更新用メソッドをコンストラクタ経由で順次渡していく

「素朴なsetState()パターン」のような言い方でたまに言及されるのがこのやり方です。

`Hoge`Widgetは引き続き`StatefulWidget`とします。

一つ前のやり方(`2.`)の時は、`WidgetA`の側から上に向かって遡って`increment()`にアクセスしましたが、今回は上から下に`increment()`そのものを渡してしまいます。

ということは`WidgetA`は`increment()`を受け取れるような形につくっておく必要があります。

```dart:my_app_3.dart
class WidgetA extends StatelessWidget {
  // WidgetAはコンストラクタでincrement関数を受け取れる
  WidgetA({this.incrementer});
  final void Function() incrementer;
  @override
  Widget build(BuildContext context) {
    print('WidgetA is built.');
    return RaisedButton.icon(
      //(略)
      // すでに受け取っているincrement関数を呼ぶだけ
      onPressed: () => incrementer(),
    );
  }
}
```

親である`Hoge`の側はこんな感じ

```dart:my_app_3.dart
class _HogeStatefulWidgetState extends State<HogeStatefulWidget> {
  // _widgetAに最初からincrement関数を渡すのは不可能なので、initState内で初期化する
  // (↑ initializerの中ではstaticメソッドしか渡せないから)
  WidgetA _widgetA;
  void increment() => setState(() => _counter++);
  void initState() {
    super.initState();
    // WidgetAのコンストラクタにincrement関数を渡しておく
    _widgetA = WidgetA(incrementer: increment);
  }
  //(関係ない所略)
```

##メリット

 - `O(1)`で更新メソッドにアクセス可能

##デメリット

 - 中間層`WidgetB`もリビルドされる
 - `Hoge`からボタンまでの全ての中間Widgetに、コンストラクタで更新用メソッドを渡していく必要があり地獄

Widgetツリーがある程度深くなるととてもやってられないです。

先ほどと同じく、中間層`WidgetB`をリビルドしない方法があるかもしれませんが思いつかなかったです。


#4. InheritedWidget

Flutterで理解に苦しむ要素の一つ、InheritedWidgetです。

InheritedWidgetには、データをWidgetツリーの下層に流して`O(1)`でアクセスできるようにする機能と、変更通知機能があります。

下層では、データの変更通知を受け取ってリビルドを実行することができるわけです。

理解したくていろいろ読んでるけどよくわからないという人は、次のポイントを押さえるといいと思います。

 - InheritedWidgetを継承したクラスを自分で作って使う。
 - 一度作られたInheritedWidgetは不変
 - 状態更新の時は、新しいInheritedWidgetが作られて古いものと置き換えられる
 - 置き換えの際に古いものとの比較が行われ、更新を通知するかどうか判断される
 - その判断基準は自分で書く(`updateShouldNotify`)

ということで、InheritedWidgetを作ったり交換したりする係を`HogeStatefulWidget`とその`State`にやってもらいます。

```dart:my_app_4.dart
class _HogeStatefulWidgetState extends State<HogeStatefulWidget> {
//(略)
  @override
  Widget build(BuildContext context) {
    // setStateが呼ばれるたびにこのbuild関数が呼ばれ、新しい_HogeInheritedWidgetが作られて古いものと交換される
    return _HogeInheritedWidget(
      counter: _counter,
      child: Column(
        children: <Widget>[
          _widgetA,
          _widgetB,
        ],
      ),
    );
  }
}

//肝心のInheritedWidget
class _HogeInheritedWidget extends InheritedWidget {
  _HogeInheritedWidget({Widget child, this.counter}) : super(child: child);
  final int counter;
  @override
  bool updateShouldNotify(_HogeInheritedWidget oldWidget) =>
      oldWidget.counter != counter;
  static _HogeInheritedWidget of(BuildContext context) =>
      context.inheritFromWidgetOfExactType(_HogeInheritedWidget);
}
//(略)

class WidgetC extends StatelessWidget {
//(略)
  @override
  Widget build(BuildContext context) {
//(略)
        // ここでWidgetCが_HogeInheritedWidgetのリビルド対象として登録される
        Text(_HogeInheritedWidget.of(context).counter.toString()),
      ],
    );
  }
}
```

##メリット

 - 中間層を経由してデータを渡さなくても下層からいきなりStateにアクセスできる
 - O(1)でそれができる
 - 中間層の`WidgetB`がリビルドされない！

##デメリット

 - わかりにくい。

##更新用メソッドもInheritedWidgetで渡せる

これよく見ると実は、「更新用メソッドをコンストラクタで順次渡していく」という面倒がまったく解決してません。

それに関しては、`_HogeInheritedWidget`に`_HogeStatefulWidgetState`そのもの(`this`)を持たせて、次のように呼び出せば解決します。

```dart
(context
.ancestorInheritedElementForWidgetOfExactType(_HogeInheritedWidget)
        .widget as _HogeInheritedWidget)
    .hogeStatefulWidgetState
    .increment();
```

呼び出しのコードが長くなるのが嫌な場合は`_HogeInheritedWidget`の側に`of2`などとしてもう一つ`of`を定義しておけば良いです。`of`の引数を増やして呼び出すメソッドを分ける方法もあります。

githubのソースコードの`my_app_4_2.dart`にそのバージョンを作りましたのでご確認ください。


(参考にした記事:[InheritedWidget/InheritedModelとは何か](https://qiita.com/ko2ic/items/d7b744f19f213ef1e647))

なお、`InheritedWidget`の使い方について詳しく説明した記事を書きましたので是非ご覧ください。

[InheritedWidgetの目的と使い方【Flutter】](https://qiita.com/agajo/items/375d5415cb79689a925c)


#5. ChangeNotifier

Flutterには、`ChangeNotifier`という仕組みがあります。

ChangeNotifierは`Widget`**ではありません**。状態管理クラスとして使います。

ということで、長らく`StatefulWidget`だった`Hoge`を`StatelessWidget`に戻し、状態管理クラスとしてChangeNotifierを継承したクラスを持たせましょう。

![Qiita記事用Flutter Widgetツリー (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85139/065abc47-dc06-be05-c30d-2b251edd082c.png)

これは先ほどと同じ図ですが、今回は生の`_counter`ではなく、状態管理クラスを持つわけですね。

Widgetツリーの下層からこの状態管理クラスにアクセスする方法としては、また以前のように、状態管理クラスをコンストラクタで渡していくことにしましょう。

```dart:my_app_5.dart
class HogeWidget extends StatelessWidget {
  // 状態管理クラスとしてChangeNotifierを継承したクラスを持つ
  final _HogeChangeNotifier _hogeChangeNotifier = _HogeChangeNotifier();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        // 下層にはコンストラクタを経由して渡していく
        WidgetA(_hogeChangeNotifier),
        WidgetB(_hogeChangeNotifier),
      ],
    );
  }
}

// 肝心のChangeNotifier
class _HogeChangeNotifier extends ChangeNotifier {
  int _counter = 0;
  void increment() {
    _counter++;
    // increment()が呼ばれると、Listenerたちに変更を通知する
    notifyListeners();
  }
}
```

更新通知を受け取るのは`WidgetC`だけでいいので、`WidgetC`を`StatefulWidget`にして、その更新用関数を`addListener`で`_HogeChangeNotifier`に登録します。

```dart:my_app_5.dart
class WidgetC extends StatefulWidget {
  WidgetC(this._hogeChangeNotifier, {this.child});
  final _HogeChangeNotifier _hogeChangeNotifier;
//(略)
}

class _WidgetCState extends State<WidgetC> {
  void rebuildC() => setState(() {});
  @override
  Widget build(BuildContext context) {
    // listnerとしてリビルドを登録したいので、WidgetCをStatefulWidgetにしてsetStateをlistner登録
    widget._hogeChangeNotifier.addListener(rebuildC);
    return Column(
      children: <Widget>[
        Text(widget._hogeChangeNotifier._counter.toString()),
  //(略)

  // 古い_WidgetCStateが捨てられるたびに、登録した古いlistenerを解除しておく
  @override
  dispose() {
    widget._hogeChangeNotifier.removeListener(rebuildC);
    super.dispose();
  }
}
```

##メリット

 - 中間層`WidgetB`がリビルドされない
 - `addListener`と`notifyListeners`を使って、通知を受け取る人や通知を送るタイミングの操作が自由自在

##デメリット
 - ChangeNotifierを渡す方法は別途考える必要がある



「ChangeNotifierを渡す方法」としては`InheritedWidget`が使えると思います。でも、`InheritedWidget`自体に変更通知機能があることを考えると、`ChangeNotifier + InheritedWidget`という組み合わせに大きなメリットがあるかは疑問…。



#6. `ChangeNotifier`を`Provider`で下層に渡す(本命)

下の公式解説記事でも「**あなたがFlutterの入門者だったり、他の方法を採用する強い動機が別に無いなら、この方法を使うべきでしょう。**」と書いてあるほどの大本命です。(2019年9月3日現在)

[Simple app state management](https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple)

この方法にはProviderパッケージが必要になるので`pubspec.yaml`に加えておいてください。

https://pub.dev/packages/provider#-installing-tab-

`ChangeNotifierProvider`を使って、状態管理クラスである`ChangeNotifier`を`Hoge`から下層に渡します。

`InheritedWidget`の時と同様に、下層からはどこからでもアクセスできるので、コンストラクタ経由で渡していく必要はありません。

```dart:my_app_6.dart
class HogeWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // ここからChangeNotifierを下層に渡す
    return ChangeNotifierProvider<_HogeChangeNotifier>(
      builder: (_) => _HogeChangeNotifier(),
      child: Column(
        children: <Widget>[
          WidgetA(),
          WidgetB(),
        ],
      ),
    );
  }
}
```

`_HogeChangeNotifier`自体は先ほどと同じです。

コンストラクタで受け取らなくていいので`WidgetA`や`WidgetB`の設計もシンプルになります。

```dart:my_app_6.dart
class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton.icon(
      // Provider経由でincrement関数を呼ぶ。listen:falseにより、こちらはリビルドされない。
      onPressed: () =>
          Provider.of<_HogeChangeNotifier>(context, listen: false).increment(),
    );
  }
}
```

また、`WidgetC`も`StatelessWidget`でよくなります。

代わりに`WidgetC`の内部で`Consumer`を使を使います。

`Consumer`は、`ChangeNotifierProvider`から渡された`ChangeNotifier`を受け取り、`notifyListeners()`に応答して、自分の子孫をリビルドします。

明示的な`addListener()`は不要です。

```dart:my_app_6.dart
class WidgetC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        // このConsumerの傘下だけがリビルドされる。
        Consumer<_HogeChangeNotifier>(
            builder: (_, _HogeChangeNotifier hoge, __) =>
                Text(hoge._counter.toString())),
        WidgetD(),
      ],
    );
  }
}
```
##メリット

 - `notifyListeners`を使って、通知を送るタイミングの操作が自由自在
 - リビルド範囲を最小限に抑えられる。`WidgetC`すらリビルドされない
 - 状態管理クラスに下層のどこからでもアクセスできる

##デメリット

 - 特になし。


#7. `BLoC`

Flutterの状態管理を調べてると出てくるのがこのBLoCパターンです。

状態管理クラスを使うという点では`5.`や`.6`に似ています。

状態管理クラスとして`ChangeNotifier`ではなく`BLoC`と呼ばれている仕組みを使うのがBLoCパターンです。

`ChangeNotifier`がFlutterの機能なのに対し、`BLoC`で用いる`Stream`はDartの機能なので、他のDartプロジェクトとコードが共有できるというメリットもあります。(現状、他のDartフレームワークなんてAngularDartくらいしかありませんが…)

まずは`.5`と同様に、状態管理クラスをコンストラクタで渡していく方法を見てみましょう。

`Hoge`WidgetがまたStatefulWidgetになりました。今回は`dispose`を実装するため、ちょっと長くなってます。

```dart:my_app_7.dart
// Blocが不要になった時にdispose()を呼ぶため、Stateのdispose関数を利用する。そのためのStatefulWidget
class HogeWidget extends StatefulWidget {
  @override
  _HogeWidgetState createState() => _HogeWidgetState();
}

class _HogeWidgetState extends State<HogeWidget> {
  final _HogeBloc _hogeBloc = _HogeBloc();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        // 下位層のコンストラクタに随時Blocを渡していく
        WidgetA(_hogeBloc),
        WidgetB(_hogeBloc),
      ],
    );
  }

  @override
  dispose() {
    _hogeBloc.dispose();
    super.dispose();
  }
}
```

肝心のBLoCはこちら。

```dart:my_app_7.dart
class _HogeBloc {
  _HogeBloc() {
    // 初期値は最初に流しておく
    _counterController.add(_counter);
    // incrementにnullが投げ入れられたら、_counterを1増やしてStreamに流す
    _incrementController.stream.listen((_) {
      _counter++;
      _counterController.add(_counter);
    });
  }

  int _counter = 0;
  // 各種Controllerと、Stream・Sinkのgetterを定義
  final StreamController<void> _incrementController = StreamController<void>();
  final BehaviorSubject<int> _counterController = BehaviorSubject<int>();
  Stream<int> get counter => _counterController.stream;
  StreamSink<void> get increment => _incrementController.sink;

  // Controllerをcloseするためdisposeメソッドも必要
  void dispose() {
    _incrementController.close();
    _counterController.close();
  }
}
```

入力側となる`WidgetA`はこんな感じ

```dart:my_app_7.dart
class WidgetA extends StatelessWidget {
  WidgetA(this._hogeBloc);
  final _HogeBloc _hogeBloc;
  @override
  Widget build(BuildContext context) {
    return RaisedButton.icon(
      // 受け取っているHogeBlocのincrementというSinkにnullを投げ入れる
      onPressed: () => _hogeBloc.increment.add(null),
    );
  }
}
```

出力を受け取る`WidgetC`はこんな感じ。

BLoCの出力である`Stream`に応答して`StreamBuilder`が中身をリビルドします。

```dart:my_app_7.dart
class WidgetC extends StatelessWidget {
  WidgetC(this._hogeBloc);
  final _HogeBloc _hogeBloc;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        // このStreamBuilderの傘下だけがリビルドされる
        StreamBuilder(
          // 受け取っているHogeBlocのStreamに応答してリビルド
          stream: _hogeBloc.counter,
          builder: (_, AsyncSnapshot snapshot) =>
              Text(snapshot.data.toString()),
        ),
        // (略)
}
```


##メリット

 - 出力側のことを気にせず好きなタイミングで`Sink`にデータを入力できる
 - リビルド範囲を最小限に抑えられる
 - Flutter以外のDartプロジェクトとコードが共有できる

##デメリット

 - BLoCの受け渡し方法を別途考える必要がある
 - StreamとSinkの理解が必要
 - BLoCをdisposeするためだけにStatefulWidgetが必要
 - IDEで定義にジャンプするとStreamに飛んでしまい、処理自体に飛べない(処理自体はStreamのリスナーを登録した所に書いてある)

#8. `BLoC`を`Provider`で下層に渡す(本命)

ということで、やっぱり状態管理クラスを下層に渡すならProviderです。

やはりproviderパッケージを使用します。

今回は`ChangeNotifierProvider`ではなく単なる`Provider`を使います。

BLoCクラス自体は先ほどと同じです。

`Hoge`Widgetはまた`StatelessWidget`になります。

`dispose`はどうすんの？ と思ったかもしれませんが、それも`Provider`がうまいことやってくれます。


```dart:my_app_8.dart
class HogeWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // ここのProviderからBLoCを下層に渡します
    return Provider<_HogeBloc>(
      builder: (_) => _HogeBloc(),
      dispose: (_, _HogeBloc bloc) => bloc.dispose(),
      child: Column(//(略)
}
```

`dispose`を登録したので、この`Provider`がWidgetツリーから除去される際にきちんと`dispose`を呼んでくれます。

入力側の`WidgetA`はこんな感じ

```dart:my_app_8.dart
class WidgetA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton.icon(
      // Provider経由で、HogeBlocのincrementというSinkにnullを投げ入れる
      onPressed: () => Provider.of<_HogeBloc>(context).increment.add(null),
    );
  }
}
```

出力を受け取る`WidgetC`はこんな感じ。

```dart:my_app_8.dart
class WidgetC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        StreamBuilder(
          // Provider経由で受け取っているStreamに応答してリビルド
          stream: Provider.of<_HogeBloc>(context).counter,
          builder: (_, AsyncSnapshot snapshot) =>
              Text(snapshot.data.toString()),
        ),
        WidgetD(),
      ],
    );
  }
}
```
`StreamBuilder`に渡す`Stream`の入手方法が変わっただけですね。


##メリット

 - 出力側のことを気にせず好きなタイミングで`Sink`にデータを入力できる
 - リビルド範囲を最小限に抑えられる
 - Flutter以外のDartプロジェクトとコードが共有できる
 - 状態管理クラスに下層のどこからでもアクセスできる


##デメリット

 - StreamとSinkの理解が必要
 - コード量増えがち
 - IDEで定義にジャンプするとStreamに飛んでしまい、処理自体に飛べない(処理自体はStreamのリスナーを登録した所に書いてある)


# 9. (追記)ValueNotifierとValueListenableProvider

ValueNotifierは、その中に`value`という変数を一つ持ち、それが変更された場合に自動検知してそれを周囲に伝えるという機能があります。

Providerを使わない場合は、ValueListenableBuilderというWidgetでValueNotifierを(コンストラクタ経由やInheritedWidget経由など、なんらかの方法で)受け取り、変更に応答してリビルドされるようにできますが、

代わりに、ValueListenableProviderというWidgetを使うと、ValueNotifierの中身である`value`を直接子孫に渡すことかできます。`value`が変更された場合は子孫Widgetもリビルドされます。

```dart:my_app_9.dart
class HogeWidget extends StatelessWidget {
  final _HogeValueNotifier _hogeValueNotifier = _HogeValueNotifier(0);
  @override
  Widget build(BuildContext context) {
    // ここからintを下層に渡す
    return ValueListenableProvider<int>(
      builder: (_) => _hogeValueNotifier,
      child: Column(
        children: <Widget>[
          WidgetA(_hogeValueNotifier.increment),
          WidgetB(),
        ],
      ),
    );
  }
}

class _HogeValueNotifier extends ValueNotifier<int> {
  _HogeValueNotifier(value) : super(value);
  void increment() {
    value++;
    // この場合 notifyListeners(); は不要。valueの変更は自動で検知される。
  }
}
```

値の受け取り手であるWidgetCはこんな感じ

```dart:my_app_9.dart
class WidgetC extends StatelessWidget {
  WidgetC(this.child);
  final Widget child;
  @override
  Widget build(BuildContext context) {
    print('WidgetC is built.');
    return Column(
      children: <Widget>[
        // ここで、リビルド対象として登録される。
        Text(Provider.of<int>(context).toString()),
        child,
      ],
    );
  }
}
```

MyApp4でやったInheritedWidgetの時と同じように、WidgetCがまるごとリビルドされます。

なので、WidgetDはあらかじめ作って外から渡すことでリビルドを防いでいます。

つづいて更新ボタンのWidgetAについてですが、

状態管理クラスのインスタンスそのものを下層に渡しているわけではないので、状態を変更するメソッドに直接アクセスできません。

なので、MyApp3でやったように、更新用メソッドを直接WidgetAに渡しています。

```dart:my_app_9.dart
class WidgetA extends StatelessWidget {
  WidgetA(this.incrementer);
  final void Function() incrementer;
  // 略
      // 既に受け取っているincrementerを呼ぶだけ
      onPressed: () => incrementer(),
  // 略
}
```

##メリット

 - 値だけが渡るので、受け取り手の書き方がシンプル


##デメリット

 - 更新メソッドへのアクセス方法は別途考える必要がある
 - intやStringといったシンプルな値を流したい場合、同じ型の値が複数あると交錯して混ざる


リビルド範囲を最小限にするにはConsumerを使えばいいかもしれませんが、それをやると「受け取り手をシンプルに書ける」というメリットが死にますね。

今の所の僕の理解では、基本的にChangeNotifierで良い気がします。

「一度オンラインやストレージから値を取得したら、基本的に変更しない」という場合には、ValueListenableProviderが良さそう。

同じ型の値が交錯する問題は、以下が実装されれば解決しますね。

[typedef for simple type aliases](https://github.com/dart-lang/sdk/issues/2626)


#その他

##Redux ・ MobX

どちらも`React`界隈で生まれた手法ですかね？

私自身が`React`の経験がまったくないため、解説できません！興味ある方はお調べください！

#まとめ

ということで、2019年9月現在の本命としては、`ChangeNotifier`と`ChangeNotifierProvider`を使った方法が良いと思います。

すでに`Java`や`JavaScript`などで`Rx`や`Observable`に馴染みのある人は、BLoCパターンも使ってみると良いと思います。BLoCにデータをポンポン放り込む感じが楽しいです。

`Stream`に興味のある方は下のリンク先をお読みください。いまだにちょくちょく「いいね」が付く`Observable`の記事です。`JavasScript`の記事ですが、`Observable`を`Stream`と読み替えればそのまま`Dart`でも通用します。

[RxJSの基礎中の基礎](https://qiita.com/agajo/items/7942743a0130f7a0f30b)

また、一部の状態管理手法に「本命」なんて書いてしまいましたが、別にそれ以外の管理手法がまったく悪いわけではありません。

「非推奨」と書いたものですら、状況によっては問題なく使えます。使う場合が想定されてるから用意されているわけですしね。

それから、一つのアプリの中で一つの状態管理手法しか使っていけないということもないですね。状況に応じて使い分けると良いと思います。

でも一つのパターンに固定するのも管理コストが抑えられて良いですけどね。

状態管理手法に「唯一の正解」はないので、「これでいいのかな…？」と不安な人も、決めないと始まりませんので、決断して実装に取り掛かりましょう。