[Flutter] package:provider の各プロバイダの詳細

# はじめに

[Google I/O 2019](https://www.youtube.com/watch?v=d_m5csmrf7I) にて `provider` というパッケージについて言及があり、公に Google 推奨になりました。

* provider | Flutter Package
    * https://pub.dev/packages/provider

~~使用例を探してみたのですが、[scoped_model](https://pub.dev/packages/scoped_model) の代わりに `ChangeNotifierProvider` を使う例ばかりで、他の多数のプロバイダの情報はほとんど見当たりません。~~
~~各プロバイダの使用例について [要望](https://github.com/rrousselGit/provider/issues/65) が上がっているので、そのうち公式に用意される可能性がありますが~~（結局なし）、まず自分で探り探りサンプルを作りながら調べてみました。
文字どおり「探り探り」ですので、誤りがあった場合にはご容赦ください。

**2019/12/25 追記**
投稿から一年が経った今では非常に有用なパッケージとしてメジャーになっています。

**2020/7/31 追記**
Flutter の [状態管理方法一覧ページ](https://flutter.dev/docs/development/data-and-backend/state-mgmt/options#provider) が半月ほど前に更新され、provider について「A recommended approach.」と記されました。


## バージョン

この記事は 2021/3/6 更新時点で provider v5.0.0 に対応しています。

最近の大きな変化：

* [v3.2.0](https://pub.dev/packages/provider/changelog#320)
    * `ProxyProvider` の `initialBuilder`/`builder` がそれぞれ `create`/`update`、他の各 Provider の `builder` が `create` に変わり、元の引数名は deprecated となりました。
* [v4.0.0](https://pub.dev/packages/provider/changelog#400)
    * v3.2.0 で deprecated となった引数名が完全廃止されました。
    * ~~`Provider.of` の `listen` を指定しなくても使い分けてくれるようになりました。~~
        * デグレードが起こったため、すぐに v4.0.0-hotfix.1 で無くなりました。
    * `StreamProvider.controller` が廃止されました。
    * `Selector` でコレクションの深い比較が行われるようになりました。
    * `Selector` に `shouldRebuild` が追加され、リビルドの条件を指定できるようになりました。
    * 各 Provider によるインスタンス生成と listen 開始が lazy になりました。
        * 必要なタイミングに遅延実行されます。
        * アプリの挙動が過去バージョンと違ってくる場合があるのでご注意ください。
* [v4.1.0](https://pub.dev/packages/provider/changelog#410)
    * `BuildContext` の三つの extension メソッドが追加されました。
        * `context.read()`
        * `context.watch()`
        * `context.select()`
    * 各 Provider に `builder` の引数が追加されました。
    * `Locator` という型定義が追加されました。
* [v4.2.0](https://pub.dev/packages/provider/changelog#420)
    * `MultiProvider` にも `builder` の引数が追加されました。
* [v4.3.0](https://pub.dev/packages/provider/changelog#430)
    * `ReassembleHandler` が追加されました。
* [v4.3.2+4](https://pub.dev/packages/provider/changelog#4324)
    * `ValueListenableProvider` のデフォルトコンストラクタが deprecated になりました。
* [v4.3.3](https://pub.dev/packages/provider/changelog#433)
    * `context.read()` と `context.watch()` の使用箇所の assertion がなくなりました。
* [v5.0.0](https://pub.dev/packages/provider/changelog#500)
    * Null safety に対応しました。
    * `ValueListenableProvider` のデフォルトコンストラクタが廃止されました。
    * `FutureProvider`、`StreamProvider` とその各々の `.value` コンストラクタで `initialData` が必須となりました。

活発に開発されていて、破壊的な変更もたびたび加えられています。
今後も大きく変わる場合がありますが、この記事を読んでおけば変更を理解しやすくなるはずです。


## provide と provider

`provider` の他に [provide](https://pub.dev/packages/provide) という類似パッケージがあります。
名前までよく似ていて一字違いなのでちょっと紛らわしいですね。

`provide` はもともと Google 公式で「Scoped Model バージョン2」と言える位置付けだったようですが、数ヶ月前から議論 [^1] があり、結局 `provider` のほうが優れているという判断に至ったそうです。[^2]
今後は `provider` を使いましょう。

[^1]: https://pub.dev/packages/provide "NOTE 2019-02-21: There's a discussion in the Flutter community over the difference between this package, `package:provider`, and `package:scoped_model`. There is a possibility that (some) of these efforts will merge. Learn more in issue [#3](https://github.com/google/flutter-provide/issues/3)."

[^2]: https://www.youtube.com/watch?time_continue=1162&v=d_m5csmrf7I 19:23～19:33辺り "Fun fact: we actually had our own package, kind of like Scoped Model version 2. And then we released it earlier this year, open-sourced it everything, and then we realized `provider` is actually much better. So we're using `provider` instead. So don't use `provide`. Use `provider`."


# 何のためのパッケージ？

Google I/O のプレゼンでは Scoped Model の新しい書き方という印象でした。
しかし、作者自身による様々な説明を見ると、それに限定した用途ではないことがわかります。

* [InheritedWidget](https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html) を使いやすくしてミスを防ぐためのシンタックスシュガー [^3] [^4]
* DI の仕組みを提供 [^5] [^6]
* インスタンスの生成と破棄を助ける（`StatelessWidget` でも `dispose()` が可能になる等） [^7]
* 他の様々な用途（Scoped Model、BLoC 等による状態管理、[ValueNotifier](https://api.flutter.dev/flutter/foundation/ValueNotifier-class.html) 等による Widget 更新など） [^8]

## 本記事について

provider を使った状態管理方法に重点を置いて解説するものではありません。
その用途でよく使われる Provider や ChangeNotifier をはじめとして使用頻度の低いプロバイダまで各種類を把握しておきたい方や、個々のプロバイダを使うときに辞書/参考書のように見直したい方に向いています。

[^3]: https://pub.dev/packages/provider "`provider` is mostly syntax sugar for `InheritedWidget`, to make common use-cases straightforward."

[^4]: https://github.com/rrousselGit/provider/issues/45#issuecomment-488241885 "This library is not opinionated on state management. It aims mostly at fixing the errors that the community make when working with inherited widgets."

[^5]: https://pub.dev/packages/provider "A dependency injection system built with widgets for widgets."

[^6]: https://www.reddit.com/r/FlutterDev/comments/bmrvey/so_is_provider_recommended_over_bloc_just_watched/emze9z6/ "What `provider` is all about is: having a really robust dependency injection system. It "forces" you to have a architecturally better app. No global, no messy object graph"

[^7]: https://www.reddit.com/r/FlutterDev/comments/bmrvey/so_is_provider_recommended_over_bloc_just_watched/emze9z6/ "explicit creation/ dispose"

[^8]: https://www.reddit.com/r/FlutterDev/comments/bmrvey/so_is_provider_recommended_over_bloc_just_watched/emze9z6/ "you can fit it pretty much anything. For example BLoC may use `provider` this way: ..."

# 下位 Widget で値を得る方法

`InheritedWidget` を使うと、ツリーの下位にある Widget に情報を効率良く渡すことができます。

provider は先述のとおり `InheritedWidget` のシンタックスシュガーなので使い方が似ていて、プロバイダで設定した値を下位 Widget で得ることができます。

その方法をまず見ておきましょう。複数あります。


## [Provider.of()](https://pub.dev/documentation/provider/latest/provider/Provider/of.html)

```dart
class FooWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider<String>.value(
      value: 'この値を下位Widgetで使いたい',
      child: _BarWidget(),
    );
  }
}

class _BarWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final value = Provider.of<String>(context);  // value == 'この値を下位Widgetで使いたい'
    return Text(value);
  }
}
```
</div>

このメソッドには `listen` という引数もあります。

* **true**
こちらがデフォルトです。
[inheritFromWidgetOfExactType()](https://api.flutter.dev/flutter/widgets/BuildContext/inheritFromWidgetOfExactType.html) が使われ、変更が notify されるとリビルドが起こります。

* **false**
[ancestorInheritedElementForWidgetOfExactType()](https://api.flutter.dev/flutter/widgets/BuildContext/ancestorInheritedElementForWidgetOfExactType.html) が使われ、リビルドを防げます。
値の変更に応じた処理（表示更新など）が起こらなくなりますが、そういった処理が不要な箇所（受け取ったモデルのメソッドを使うだけ等）では `false` にすることで無駄を避けられます。

~~provider 4.0.0 では自動的に判断して使い分けてくれるようになりました。
次の場合を除き、基本的に明示的な指定は不要です。~~

* ~~State.initState() 内で使うとき~~
* ~~Provider の create で使うとき~~

listen の自動使い分けにはバグが見つかり、4.0.0-hotfix.1 で廃止されました。

v4.1.0 では `Provider.of()` の代わりに使える extension メソッドが追加されましたので後述します。


## [Consumer](https://pub.dev/documentation/provider/latest/provider/Consumer/Consumer.html)

`Provider.of()` ではプロバイダの子孫にあたる Widget の `BuildContext` が必要ですが、`Consumer` はそれが得られない場合にも使えて便利です。

```dart
class FooWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider<String>.value(
      value: 'この値を下位Widgetで使いたい',
      child: Consumer<String>(
        builder: (context, value, child) {  // value == 'この値を下位Widgetで使いたい'
          return Text(value);
        },
      ),
    );
  }
}
```

また [FAQ](https://pub.dev/packages/provider#my-widget-rebuilds-too-often-what-can-i-do) にあるとおり、次のように使うと Foo や Baz はリビルドせずに Bar だけをリビルドさせることができます。

```dart
Foo(
  child: Consumer<A>(
    builder: (_, a, child) {
      return Bar(a: a, child: child);
    },
    child: Baz(),
  ),
)
```

Baz はあらかじめビルドされてから `builder` の `child` に渡されます。
これは [AnimatedBuilder](https://api.flutter.dev/flutter/widgets/TweenAnimationBuilder-class.html) 等の child と同じ仕組みです。

例えば下記コードでは、四角形の高さが変わっていっても `Text('ほげ')` はリビルドされません。
そうやってパフォーマンスを改善するのに使えるものだと理解しておきましょう。

```dart
Consumer<SampleModel>(
  builder: (context, model, child) {
    return Container(
      width: 250.0,
      height: 250.0 + model.value,
      color: Colors.red,
      child: child,
    );
  },
  child: Text('ほげ'),
)
```

Consumer の他に [Consumer2](https://pub.dev/documentation/provider/latest/provider/Consumer2/Consumer2.html) から [Consumer6](https://pub.dev/documentation/provider/latest/provider/Consumer6/Consumer6.html) まであり、得たい値の種類数によって使い分けることができます。
個人的にそういう仕様はイマイチに思えましたが、Dart の型システムの制限によるものだそうです。[^9]

[^9]: https://github.com/rrousselGit/provider/issues/127#issuecomment-508954636

provider v3.1.0 で `MultiProvider` と組み合わせることもできるようになりました。


## [Selector](https://pub.dev/documentation/provider/latest/provider/Selector/Selector.html)

`Consumer` をより便利にしたものです。
provider v3.1.0 で追加され、v4.0.0 で更に機能が増しました。

```dart
class DualCounter with ChangeNotifier {
  int count1 = 0;
  int count2 = 0;

  DualCounter() {
    Timer.periodic(Duration(seconds: 1), (t) {
      t.tick.isOdd ? count1++ : count2++;
      notifyListeners();
    });
  }

  ...
}
```

count1 と count2 の値を1秒ごとに交互にインクリメントして変更を通知するクラスです。
その値を `Consumer` で受け取ると、count1 の変更時に count2 用の Text までリビルドされてしまいます。

そこで役立つのが `Selector` です。
`selector` で指定した値が変更されたときだけ `builder` が呼ばれるように制限できます。
下記では、count1 の値が変わると a の `builder`、count2 の値が変わると b の `builder` が呼ばれます。

```dart
ChangeNotifierProvider<DualCounter>(
  create: (context) => DualCounter(),
  child: Column(
    children: <Widget>[
      Selector<DualCounter, int>(
        selector: (context, model) => model.count1,
        builder: (context, count, child) => Text(count.toString()),  // a
      ),
      Selector<DualCounter, int>(
        selector: (context, model) => model.count2,
        builder: (context, count, child) => Text(count.toString()),  // b
      ),
    ],
  ),
)
```

実際に試してみると、Text が交互にリビルドされました。
このように変更に応じる対象を絞れば、余計なリビルドを防いで無駄をなくせます。
![flutter_provider_selector.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132102/a6bb0935-5e54-d1db-26e3-0c506c925aa3.gif)

provider v4.0.0 では `selector` で指定するものがコレクションの場合に深い比較が行われるようになりました。
更に、リビルドの条件を `shouldRebuild` という引数で上書きできるようになりました。

その例として、GitHub に置いているサンプルに [偶数のみ表示するカウンターの例](https://github.com/kaboc/flutter_provider_examples/blob/6097fe13b9873b4599c7f2ec81502ccb751b9b0f/lib/pages/selector.dart#L49-L53) を追加しました。

```dart
Selector<CnCounter, int>(
  selector: (context, counter) => counter.number,
  shouldRebuild: (prev, next) => next.isEven,  // 偶数になるときのみ
  builder: (context, number, child) => Text(number.toString()),
),
```

`Consumer` と同様に他に5つあります（`Selector2` ～ `Selector6`）。
基底クラスである `Selector0` も存在しますが、基本的には自分で使う必要はないと思います。

v4.1.0 では `Selector` の代わりに使える extension メソッドが追加されましたので後述します。


## [ProxyProvider](https://pub.dev/documentation/provider/latest/provider/ProxyProvider/ProxyProvider.html)

v3.0.0 で追加されました。[^10]
`Consumer` に似ていて、他のプロバイダの値を得て利用することができます。

[^10]: `ProxyProvider` 導入が検討されていたときの「[[RFC] Simplifying providers that depends on each others #46](https://github.com/rrousselGit/provider/issues/46)」という Issue を見るとよりわかりやすいです。

```dart
MultiProvider(
  providers: [
    Provider<Foo>(
      create: (context) => Foo(),
    ),
    ProxyProvider<Foo, Bar>(
      create: (context) => Bar(),
      update: (context, foo, prevBar) => prevBar..foo = foo,
    ),
  ],
  child: ...,
)
```

同類として `ChangeNotifierProxyProvider` と `ListenableProxyProvider` も v3.0.0 で追加されています。
これらも全て `Consumer` と同様に6つずつあります。

上の例では、別の Provider で渡された Foo が変化したときに Bar をアップデートするものですが、`State` 等で管理されている値の変化に伴わせるには `ProxyProvider0` が使えます。

```dart
class _FooState extends State<Foo> {
  int _value = 0;

  void updateValue(int value) {
    setState(() => _value = value);
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProxyProvider0<Bar>(
      create: (context) => Bar(_value),
      update: (context, bar) => bar..value = _value,
      child: ...,
    );
  }
}
```


## context.[read()](https://pub.dev/documentation/provider/latest/provider/ReadContext/read.html) / [watch()](https://pub.dev/documentation/provider/latest/provider/WatchContext/watch.html)

v4.1.0 で追加されました。
`BuildContext` の extension であり、`Provider.of()` に相当します。

ただし、下表のような使い分けが必要で、`Provider.of()` より少しややこしいです。
**以前は誤って用いるとエラーが出ていましたが、v4.3.3 で出なくなりました**。
自分で気を付けましょう。

| | 相当するもの | 制約 |
|---|---|---|
| context.read<Foo>() | Provider.of<Foo>(context, listen: false) | ビルド中は不可<br>各 Provider の create の関数内では使用可 |
| context.watch<Foo>() | Provider.of<Foo>(context, listen: true) | ビルド中のみ可<br>各 Provider の update の関数内でも使用可 |

「ビルド中」というのは StatelessWidget や State の build() メソッドが始まってから終わるまでの間のことです。
例外として、`read()` は各 Provider の `create` の関数内では使用できます。
また、`watch()` は `update` の関数内で使用することもできます。

```dart
Widget build() {
  final foo1 = context.read<Foo>();   // ダメ
  final foo2 = context.watch<Foo>();  // OK
  final fooValue = context.select((Foo foo) => foo.value);  // OK（後述します）

  return Provider<Foo>(
    create: (context) => Foo(),
    child: ProxyProvider0<Bar>(
      create: (context) {
        return Bar(foo: context.read<Foo>()),     // OK
        //return Bar(foo: context.watch<Foo>()),  // ダメ
      },
      update: (context, bar) {
        //return bar..foo = context.read<Foo>();  // ダメ
        return bar..foo = context.watch<Foo>();   // OK
      },
      child: ...,
    ),
  );
}
```

さらに、ボタンの `onPressed` 等のハンドラで使うときにも注意が必要です。

```dart:read()の場合
Column(
  children: <Widget>[
    RaisedButton(
      child: ...,
      onPressed: () => context.read<CnCounter>().increment(),  // OK
    ),
    RaisedButton(
      child: ...,
      onPressed: context.read<CnCounter>().increment,  // ダメ
    ),
  ],
)
```

```dart:watch()の場合
Column(
  children: <Widget>[
    RaisedButton(
      child: ...,
      onPressed: () => context.watch<CnCounter>().increment(),  // ダメ
    ),
    RaisedButton(
      child: ...,
      onPressed: context.watch<CnCounter>().increment,  // OK
    ),
  ],
)
```

`read()` をビルド中に使えないのは、（確信はないですが）値の変化に応じたリビルドが必要な箇所で誤って `read()` を使ってしまうミスを防ぐためだと思われます。
~~ビルド中に使いたければ、これまで通り `Provider.of(context, listen: false)` を使いましょう。~~[^value]

[^value]: `read()` の制約が厳しい理由を理解した上であれば、あえて制約のない `context.value()` （名前は適当）のような extension を自作して使うのは良いかもしれません。

[こちらの Issue](https://github.com/rrousselGit/provider/issues/456) に書かれている作者の説明によると、`read()` だけでなく `Provider.of(context, listen: false)` もエッジケース以外では避けたほうが良いそうです。

* 状態が変わったら表示に反映するのが適当なので `watch()` か `select()` が良い
* それ以外の用途は主にタップ等によるメソッドのコールであって、そこ（`onPressed` 等）では `read()` が使える

こう考えると、ビルド中に使えないのはあまり困ることでもないですね。

ただし、ケースによっては無駄なリビルドが生じることもあります。
パフォーマンスを確認しながら判断するのが良いでしょう。[^rebuild]

[^rebuild]: リビルドは再描画ではないので、多少の無駄なリビルドが体感やバッテリ消費に大きく影響するものでもありません。


## context.[select()](https://pub.dev/documentation/provider/latest/provider/SelectContext/select.html)

v4.1.0 で追加されました。
`BuildContext` の extension であり、`Selector` に近い機能を簡潔な書き方で使えるものです。
使いどころは [Selector の説明](#selector) を参照ください。

```dart
Selector<Foo, int>(
  selector: (context, foo) => foo.value,
  builder: (context, value, child) => Text(value.toString()),
)
```

これを `select()` を使って書くと次のようにシンプルに書けます。

```dart
final value = context.select((Foo foo) => foo.value);
Text(value.toString());
```

実装は `R select<T, R>(R selector(T value))` となっているので、`context.select<Foo, int>((foo) => foo.value)` のように書くこともできます。


## builder

v4.1.0 で各 Provider に追加された引数です（`MultiProvider` には v4.2.0 で追加）。
ややこしいですが、v4.0.0 より前に存在していた builder（現 create）とは異なるものです。

では例を見ましょう。

```dart
ChangeNotifierProvider<Foo>(
  create: (context) => Foo(),
  child: Selector<Foo, int>(
    selector: (context, foo) => foo.value,
    builder: (context, value, baz) {
      return Bar(value, child: baz);
    }
    child: Baz(),
  ),
)
```

これまで、Provider の子ですぐにその Provider で渡そうとした値を使うことはできず、`Builder`・`Consumer`・`Selector` といった Widget を挟む必要がありました。
新しい `builder` の引数は、そうしなくても済むようにしてくれるものです。[^builder]

[^builder]: [更新履歴](https://pub.dev/packages/provider/changelog#410) には `Builder` と同じ振る舞いだと書かれていますが、少しだけ異なると思います。`Builder`・`Selector` 等では Baz はその孫なので Foo の値が更新されるたびにリビルドされますが、`builder` を使う場合は Baz は ChangeNotifierProvider の子であり、あらかじめビルドしてから使用されるのでリビルドされないはずです。

```dart
ChangeNotifierProvider<Foo>(
  create: (context) => Foo(),
  builder: (context, baz) {
    final value = context.select((Foo foo) => foo.value));
    return Bar(value, child: baz);
  },
  child: Baz(),
)
```


# サンプルと解説

よくあるカウンターのサンプルです。
右下のボタンをタップすると画面中央の値がインクリメントされます。

![flutter_provider_examples.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132102/11c5feda-5de0-50e1-d929-9fc0908b66c1.gif)

大半のプロバイダでは同じ動作のカウンターにしました。
一部（v3.0.0 で追加されたプロバイダ等）は同じ動作にするのが適していないため、異なるものにしています。

* ソースコード
    * https://github.com/kaboc/flutter_provider_examples


## [Provider](https://pub.dev/documentation/provider/latest/provider/Provider/Provider.html)

何らかの型の値をツリーの下方に渡したいときに使える一番シンプルなプロバイダです。
BLoC パターンのためのプロバイダとしても使えます。
[ドキュメント](https://pub.dev/documentation/provider/latest/provider/Provider-class.html) によれば、BLoC 等のために `StatefulWidget` を使う手間を省くのに使えます。[^11]

[^11]: "It is usually used to avoid making a StatefulWidget for something trivial, such as instantiating a BLoC."

そこで、このサンプルでも BLoC パターンを使ってみました。
BLoC パターンの学習までまだ進んでいない方にはわかりにくいかもしれませんが、

* (a) BLoC をインスタンス化するタイミングに迷う
* (b) BLoC が不要になったときに `StatelessWidget` だと自動で破棄されない
* (c) BLoC パターンを実現しやすくするプロバイダのパッケージが乱立していた

というあたりの問題をそれぞれ次のように解決してくれています。

* (a) `Provider` の `create` にインスタンス生成処理を渡す
    * Provider の `State` 生成時（Widget ツリーに追加されるとき）に一度だけ実行してくれる
        * 指定例： `create: (context) => CounterBloc()`
* (b) `Provider` の `dispose` に破棄用メソッドを指定する
    * Provider が Widget ツリーから外されるタイミングで実行してくれる
        * 指定例： `dispose: (context, bloc) => bloc.dispose()`
* (c) provider パッケージが無難な選択肢になった [^12]

[^12]: BLoC に特化した他の軽量なパッケージをあえて用いるのも良い選択だと思います。

BLoC パターンでなくても、インスタンスの生成と破棄を自動的にやってもらいつつ下位の Widget で値を利用したいケースでは役立つはずです。

```dart:blocs/counter_bloc.dart
class CounterBloc {
  final _valueController = StreamController<int>();
  Stream<int> get value => _valueController.stream;

  int _number = 0;

  void increment() {
    _number++;
    _valueController.sink.add(_number);
  }

  void dispose() {
    _valueController.close();
  }
}
```

```dart:pages/provider.dart
class ProviderPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider<CounterBloc>(
      create: (_) => CounterBloc(),
      dispose: (_, bloc) => bloc.dispose(),
      child: Scaffold(
        body: _CounterText(),
        floatingActionButton: _floatingButton(),
      ),
    );
  }

  Widget _floatingButton() {
    return Consumer<CounterBloc>(
      builder: (_, bloc, child) {
        return FloatingActionButton(
          onPressed: bloc.increment,
          child: child,
        );
      },
      child: const Icon(Icons.add),
    );
  }
}

class _CounterText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final bloc = Provider.of<CounterBloc>(context, listen: false);

    return StreamBuilder<int>(
      stream: bloc.value,
      initialData: 0,
      builder: (_, snapshot) {
        return Center(
          child: Text(snapshot.data.toString()),
        );
      },
    );
  }
}
```

BLoC パターンについては、以前に関連記事を書いています。
一読されると理解しやすくなるかもしれません。

* [長めだけどたぶんわかりやすいBLoCパターンの解説](https://qiita.com/kabochapo/items/8738223894fb74f952d3)
* [BLoCパターンの問題点とScoped Modelとの比較](https://qiita.com/kabochapo/items/2b992cc00e9f464c1ea9)

`lazy` という引数については各プロバイダの後に解説していますので [そちら](#lazy) をご覧ください。


### 注意

* `create` の処理は一度しか呼ばれません。
    * 値が変化するたびにプロバイダにセットし直したいケースでは `.value` という名前の付いたコンストラクタのほうを使いましょう。
* 既に存在するインスタンスをデフォルトコンストラクタで扱うのは安全ではありません。
    * まだ使用が終わっていないうちに dispose される危険性があります。
    * 代わりに `Provider.value` のほうを使いましょう。
* `lazy` はデフォルトで `true` です。
    * 思った挙動と違ったらそれが原因の場合かもしれません。


## [Provider.value](https://pub.dev/documentation/provider/latest/provider/Provider/Provider.value.html)

`Provider` の名前付きコンストラクタ版です。
先ほどと同じ CounterBloc を使ったサンプルにしてみました。

名前付きのほうは BLoC の `dispose()` が自動的には呼ばれません。
そこで代わりに `State.dispose()` を使って呼べるように `StatefulWidget` にしています。

このように `Provider` を使った場合より少し手間が多くなります。
`Provider.value` は先ほど [下位 Widget で値を得る方法](#下位-widget-で値を得る方法) でご紹介したような単純な値の伝播に向いているようです。

```dart:pages/provider_value.dart
class ProviderValuePage extends StatefulWidget {
  @override
  _ProviderValueState createState() => _ProviderValueState();
}

class _ProviderValueState extends State<ProviderValuePage> {
  final _bloc = CounterBloc();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Provider<CounterBloc>.value(
        value: _bloc,
        child: _CounterText(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: bloc.increment,
        child: const Icon(Icons.add),
      ),
    );
  }

  @override
  void dispose() {
    _bloc.dispose();
    super.dispose();
  }
}

class _CounterText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final bloc = Provider.of<CounterBloc>(context, listen: false);

    return StreamBuilder<int>(
      stream: bloc.value,
      initialData: 0,
      builder: (_, snapshot) {
        return Center(
          child: Text(snapshot.data.toString()),
        );
      },
    );
  }
}
```

このサンプルでは使いませんでしたが、次の引数もあります。

* **updateShouldNotify**
    * 値に変更がなくても不要なリビルドが起こってしまうのを防ぐものです。
必要に応じて指定しましょう。


## StreamProvider.controller

provider v4.0.0 で廃止されました。
折り畳んで残しておきます。

<details><summary>クリックで開閉</summary><div>
`StreamProvider` からご紹介すべきかもしれませんが、v2 系で `StreamProvider` だったものが v3.0.0 で `StreamProvider.controller` に変わったため、先にこちらを説明します。

---

`Provider` の `Stream` 版のようなものです。
これもインスタンスの生成と破棄をうまく扱ってくれます。

* `create` には `StreamController` を渡す
* Widget ツリーから外されるときに `StreamController.close()` が自動的に呼ばれる

注意が必要なのは次の点です。

* `StreamController<int>` を `create` に渡すからと言って `StreamProvider<StreamController<int>>.controller` とするわけではない（`StreamProvider<int>.controller` にする）
* ツリーの下位にある Widget で値を得るときにも `Provider.of<int>(context)` とする

つまり、下位 Widget で得られるのは単なる int 型の値だということです。

---

しかし、使い方はわかってもあまりメリットが見えません。
次の二つくらいでしょうか…。:thinking: [^streamprovider]

* `StreamBuilder` を使わないで済む
* BLoC パターン等以外で `Sink` / `Stream` によって変更を通知するのに使える

[^streamprovider]: `.controller` ではなく `.value` のほうについては、Firebase のユーザ認証に使う例が [こちらの記事](https://www.didierboelens.com/2019/07/provider---points-of-interest---points-to-care-about/#therefore-will-you-ask-me-is-there-any-advantage-of-using-a-streamprovider) に書かれています。プラグイン等によって用意される `Stream` をそのまま用いるようなケースに向いていそうです。

また、`StreamController` のインスタンスを `create` で生成せずに、先に生成して変数に入れなければならなかったのも少し腑に落ちません。
`create` で生成したインスタンスを取得する方法がないと `.sink.add()` できないので、やむを得ずそうしました。

```dart:pages/stream_provider_controller.dart
class StreamProviderCtrlPage extends StatefulWidget {
  @override
  _StreamProviderCtrlState createState() => _StreamProviderCtrlState();
}

class _StreamProviderCtrlState extends State<StreamProviderCtrlPage> {
  int _number = 0;

  @override
  Widget build(BuildContext context) {
    final streamController = StreamController<int>();

    return Scaffold(
      body: StreamProvider<int>.controller(
        create: (_) => streamController,
        initialData: 0,
        child: _CounterText(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => streamController.sink.add(++_number),
        child: const Icon(Icons.add),
      ),
    );
  }
}

class _CounterText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final number = Provider.of<int>(context);

    return Center(
      child: Text(number.toString()),
    );
  }
}
```

* **initialData**
    * `Stream` にまだ値が来ていない間の初期値（省略時は `null`）。

このサンプルでは使いませんでしたが、次の引数もあります。

* **catchError**
    * エラーが来たときのフォールバック用の値をここで返します。  
下位 Widget ではエラー時にそのフォールバック値が得られます。  
エラーデータが扱われる `Stream` であれば必須です。
* **updateShouldNotify**
    * `Provider.value` の `updateShouldNotify` と同様です。
</div></details>


## [StreamProvider](https://pub.dev/documentation/provider/latest/provider/StreamProvider/StreamProvider.html)

`create` で生成する対象が、v3.0.0 で `StreamController` から `Stream` に変わりました。
~~代わりに先ほどの `StreamProvider.controller` が `StreamController` 版として追加されています。~~
StreamProvider.controller は provider v4.0.0 で廃止されました。

他のプロバイダではデフォルトコンストラクタに破棄の機能が備わっていますが、`StreamProvider` の [ドキュメント](https://pub.dev/documentation/provider/latest/provider/StreamProvider/StreamProvider.html) にはそのことが書かれていません。
自分で `StreamController.close()` する必要があると思われます。[^13]

[^13]: ソースコードをみると、`create` の処理が `BuilderStateDelegate` というものに移譲され、そこの `dispose()` で `Stream` の破棄が行われているようです。このときに `StreamController` が破棄されるわけではないはずです。

```dart:pages/stream_provider.dart
class StreamProviderPage extends StatefulWidget {
  @override
  _StreamProviderState createState() => _StreamProviderState();
}

class _StreamProviderState extends State<StreamProviderPage> {
  final _streamController = StreamController<int>();
  int _number = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamProvider<int>(
        create: (_) => _streamController.stream,
        initialData: 0,
        child: _CounterText(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _streamController.sink.add(++_number),
        child: const Icon(Icons.add),
      ),
    );
  }

  @override
  void dispose() {
    _streamController.close();
    super.dispose();
  }
}

class _CounterText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final number = Provider.of<int>(context);

    return Center(
      child: Text(number.toString()),
    );
  }
}
```

* **initialData**
    * `Stream` にまだ値が来ていない間の初期値。
    * 以前は任意でしたが、v5.0.0 で必須となりました。

このサンプルでは使いませんでしたが、次の引数もあります。

* **catchError**
    * エラーが来たときのフォールバック用の値をここで返します。  
下位 Widget ではエラー時にそのフォールバック値が得られます。  
エラーデータが扱われる `Stream` であれば必須です。
* **updateShouldNotify**
    * `Provider.value` の `updateShouldNotify` と同様です。


## [StreamProvider.value](https://pub.dev/documentation/provider/latest/provider/StreamProvider/StreamProvider.value.html)

`StreamProvider` とほとんど同じで、異なるのは次の点のみです。

* `create` はなく `value` に `Stream` を指定する

`StreamController.close()` は自分で行う必要があります。


```dart:pages/stream_provider_value.dart
class StreamProviderValuePage extends StatefulWidget {
  @override
  _StreamProviderValueState createState() => _StreamProviderValueState();
}

class _StreamProviderValueState extends State<StreamProviderValuePage> {
  final _streamController = StreamController<int>();
  int _number = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamProvider<int>.value(
        value: _streamController.stream,
        initialData: 0,
        child: _CounterText(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _streamController.sink.add(++_number),
        child: const Icon(Icons.add),
      ),
    );
  }

  @override
  void dispose() {
    _streamController.close();
    super.dispose();
  }
}

class _CounterText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final number = Provider.of<int>(context);

    return Center(
      child: Text(number.toString()),
    );
  }
}
```

引数は、v2 系で `stream` だったのが v3.0.0 で `value` に変わりました。ご注意ください。
`value` 以外の引数は `StreamProvider` と同じです。


## [ChangeNotifierProvider](https://pub.dev/documentation/provider/latest/provider/ChangeNotifierProvider/ChangeNotifierProvider.html)

`scoped_model` パッケージに似ていて、ほとんど同じ感覚で使えそうです。
モデルの状態を変更したときに `notifyListeners()` で通知するのも同じです。
次のようにして置き換えることができます。

* モデルは `Model` ではなく [ChangeNotifier](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html) を継承/Mixin適用する
* [ScopedModel<T>](https://pub.dev/documentation/scoped_model/latest/scoped_model/ScopedModel-class.html) を `ChangeNotifierProvider<T>` にする
* [ScopedModelDescendant<T>](https://pub.dev/documentation/scoped_model/latest/scoped_model/ScopedModelDescendant-class.html) を `Consumer<T>` にする [^14]

[^14]: `ScopedModelDescendant` に近い書き方になるのは `Consumer` ですが、もちろん `Provider.of()` も使えます。

モデルのインスタンスの生成と破棄はよしなに取り計らってくれます。
破棄のほうは `Provider` と違って `dispose` の引数がありません。
`ChangeNotifierProvider` が Widget ツリーから外されたときに自動的に破棄されます。

```dart:models/change_notifier_counter.dart
class CnCounter with ChangeNotifier {
  int _number = 0;
  int get number => _number;

  void increment() {
    _number++;
    notifyListeners();
  }
}
```

```dart:pages/change_notifier_provider.dart
class CnProviderPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<CnCounter>(
      create: (_) => CnCounter(),
      child: Scaffold(
        body: _CounterText(),
        floatingActionButton: _FloatingButton(),
      ),
    );
  }
}

class _FloatingButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<CnCounter>(context, listen: false);

    return FloatingActionButton(
      onPressed: counter.increment,
      child: const Icon(Icons.add),
    );
  }
}

class _CounterText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<CnCounter>(context);

    return Center(
      child: Text(counter.number.toString()),
    );
  }
}
```

このサンプルでは `ChangeNotifier` を使いましたが、その派生クラス（`ValueNotifier`、`ScrollController` など）にも使えます。


## [ChangeNotifierProvider.value](https://pub.dev/documentation/provider/latest/provider/ChangeNotifierProvider/ChangeNotifierProvider.value.html)

`ChangeNotifierProvider` と異なるのは次の一点のみです。

* モデルのインスタンスの生成・破棄は自己責任

```dart:change_notifier_provider_value.dart
class CnProviderValuePage extends StatefulWidget {
  @override
  _CnProviderValueState createState() => _CnProviderValueState();
}

class _CnProviderValueState extends State<CnProviderValuePage> {
  final _counter = CnCounter();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ChangeNotifierProvider<CnCounter>.value(
        value: _counter,
        child: _CounterText(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _counter.increment,
        child: const Icon(Icons.add),
      ),
    );
  }

  @override
  void dispose() {
    _counter.dispose();
    super.dispose();
  }
}

class _CounterText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<CnCounter>(context);

    return Center(
      child: Text(counter.number.toString()),
    );
  }
}
```

引数は、v2 系で `notifier` だったのが v3.0.0 で `value` に変わりました。ご注意ください。


## [ValueListenableProvider](https://pub.dev/documentation/provider/latest/provider/ValueListenableProvider/ValueListenableProvider.html)

v5.0.0 でこのデフォルトコンストラクタのほうだけ廃止されました。
廃止されていない `.value` のほうの理解に必要であれば下記を開いてご覧ください。

<details><summary>クリックで開閉</summary><div>

`ValueListenable` というと、そのインタフェースを実装した `ValueNotifier` を `ValueListenableBuilder` と組み合わせて値の変更を Widget に随時反映するのがよくある使い方です。
次の動画を観るほうがイメージしやすいと思います（2分弱の短い動画です）。

* [ValueListenableBuilder (Flutter Widget of the Week) - YouTube](https://www.youtube.com/watch?v=s-ZG-jS5QHQ)

`ValueListenableProvider` を使うと、`ValueListenableBuilder` を使わずに同じことが簡単にできます。
動画では `InheritedWidget` も組み合わせることが提案されていますが、provider パッケージは `InheritedWidget` のシンタックスシュガーなのでそれも不要です。

なお、`Provider.of()` で得られるのは `create` で生成されたインスタンスではない点に注意が必要です。
`ValueNotifier.value`（本サンプルでは `VnCounter.value`）の値です。
その値を変更しても Notify されません（そもそも変更もできません）。

`create` で生成したものは `ChangeNotifierProvider` と同様に自動的に破棄してくれます。

```dart:models/value_notifier_counter.dart
class VnCounter extends ValueNotifier<int> {
  VnCounter() : super(0);

  void increment() => value++;
}
```

```dart:value_listenable_provider.dart
class VlProviderPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = VnCounter();

    return Scaffold(
      body: ValueListenableProvider<int>(
        create: (_) => counter,
        child: _CounterText(),
      ),

      floatingActionButton: FloatingActionButton(
        onPressed: counter.increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

class _CounterText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final number = Provider.of<int>(context);

    return Center(
      child: Text(number.toString()),
    );
  }
}
```

このサンプルでは使いませんでしたが、次の引数もあります。

* **updateShouldNotify**
    * `Provider.value` 等の `updateShouldNotify` と同様です。

</div></details>

### おさらい（ChangeNotifierProvider vs ValueListenableProvider）

* **ChangeNotifierProvider**
    * `ChangeNotifier` にも `ValueNotifier` にも使える
    * 下位の Widget に伝わるのはインスタンス
* **ValueListenableProvider**
    * `ChangeNotifier` には使えない
    * 下位の Widget に伝わるのは `ValueNotifier.value` の中身


## [ValueListenableProvider.value](https://pub.dev/documentation/provider/latest/provider/ValueListenableProvider/ValueListenableProvider.value.html)

`ValueListenableProvider` / `ValueListenableProvider.value` の違いは、`Provider` / `Provider.value` の違いと同じです。

```dart:value_listenable_provider_value.dart
class VlProviderValuePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = VnCounter();

    return Scaffold(
      body: ValueListenableProvider<int>.value(
        value: counter,
        child: _CounterText(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: counter.increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

class _CounterText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final number = Provider.of<int>(context);

    return Center(
      child: Text(number.toString()),
    );
  }
}
```

引数は、v2 系で `valueListenable` だったのが v3.0.0 で `value` に変わりました。ご注意ください。


## [ListenableProvider](https://pub.dev/documentation/provider/latest/provider/ListenableProvider/ListenableProvider.html)

`ChangeNotifierProvider` はこれを継承したものであり、似ています。

`create` では `Listenable` を生成します。
`Listenable` を実装しているものとして、[ドキュメント](https://api.flutter.dev/flutter/foundation/Listenable-class.html) には次のように4つが挙げられています。

> Implementers
> Animation, ChangeNotifier, CustomPainter, ValueListenable

このうちの `ChangeNotifier` と `ValueListenable` は既に見た `ChangeNotifierProvider` や `ValueListenableProvider` で扱えますね。
その二つにはこの `ListenableProvider` も使えます [^15] が、特別な理由がなければあえて使う必要はないと思います。

[^15]: ややこしいですが、`ChangeNotifier` と `ValueListenable` は `Listenable` の実装であり、`ValueNotifier` は `ChangeNotifier` を継承 ＆ `ValueListenable` を実装したものです。また、`ChangeNotifierProvider` は `ListenableProvider` を継承しています。

使う場合、破棄の処理を任せたいなら `dispose` を指定する必要があります。
この点は `ChangeNotifierProvider` 等よりわずかに手間です。

---

2019/7/6

サンプルを載せていましたが、このプロバイダを使う明確な理由がなかったため削除しました。
説明文も更新しました。
（[GitHub に置いているサンプル](https://github.com/kaboc/flutter_provider_examples/blob/master/lib/pages/listenable_provider.dart) では参考としてアニメーションに使っています。）


## [MultiProvider](https://pub.dev/documentation/provider/latest/provider/MultiProvider/MultiProvider.html)

複数のプロバイダを使うときに

```dart
Provider<Foo>.value(
  value: foo,
  child: Provider<Bar>.value(
    value: bar,
    child: Provider<Baz>.value(
      value: baz,
      child: someWidget,
    )
  )
)
```

という深いネストにする代わりに

```dart
MultiProvider(
  providers: [
    Provider<Foo>.value(value: foo),
    Provider<Bar>.value(value: bar),
    Provider<Baz>.value(value: baz),
  ],
  child: someWidget,
)
```

のように簡潔に書けるようにするプロバイダです。
コードの見た目は異なりますが、Widget ツリー上は同じものになります。

※このコードは [クラスのドキュメント](https://pub.dev/documentation/provider/latest/provider/MultiProvider-class.html) から借用しました。


### 注意

上記の例では `Provider<T>.value` の `T` の部分に `Foo` `Bar` `Baz` という異なる型が指定されています。
もし同じ型にしてしまうと、次のように最後のプロバイダで指定した値になってしまいます。

```dart
MultiProvider(
  providers: [
    Provider<int>.value(value: 1),
    Provider<int>.value(value: 2),
    Provider<int>.value(value: 3),
  ],
  child: someWidget(context),
)
```

```dart
Widget someWidget(BuildContext context) {
   final val1 = Provider.of<int>(context);
   final val2 = Provider.of<int>(context);
   final val3 = Provider.of<int>(context);

   print(val1);  // 3
   print(val2);  // 3
   print(val3);  // 3
}
```

これは [Provider.of<T>() のドキュメント](https://pub.dev/documentation/provider/latest/provider/Provider/of.html) に書かれているとおり

> Widget ツリーをさかのぼって最寄りの `Provider<T>` を取得し、その値を返す [^17]

という仕様によるものです。[^18]
`MultiProvider` に限ったことではありません。
ご注意ください。

[^17]: "Obtains the nearest `Provider<T>` up its widget tree and returns its value."

[^18]: 延いては、`Provider.of()` で使われている `BuildContext.inheritFromWidgetOfExactType()` や `BuildContext.ancestorInheritedElementForWidgetOfExactType()` の仕様です。


## [FutureProvider](https://pub.dev/documentation/provider/latest/provider/FutureProvider/FutureProvider.html)

ここからは v3.0.0 で追加された種類になります。
`.value` という名前付きのコンストラクタに関する説明はこの先省略します。

---

`InheritedWidget` と [FutureBuilder](https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html) を組み合わせたような便利なものです。
ここまでのサンプルと同じ動作にしたかったのですが、無理でしたので異なるものにしました。

![flutter_future_provider.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132102/760c0645-7bc2-5fa0-def9-dfbd16da672a.gif)

1. ページを表示すると「Wait for 3 seconds...」と表示される
1. `Future` の処理が3秒後に完了する
1. 自動的に表示が「3 seconds elasped.」に変わる

```dart:pages/future_provider.dart
class FutureProviderPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureProvider<String>(
        create: (_) =>
            Future.delayed(Duration(seconds: 3), () => '3 seconds elapsed.'),
        initialData: 'Wait for 3 seconds...',
        child: _FutureText(),
      ),
    );
  }
}

class _FutureText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final description = Provider.of<String>(context);

    return Center(
      child: Text(description),
    );
  }
}
```

* **initialData**
    * `Future` の処理が完了していない間の初期値。
    * 以前は任意でしたが、v5.0.0 で必須となりました。

このサンプルでは使いませんでしたが、次の引数もあります。

* **catchError**
    * 他のプロバイダの同名の引数と同様です。
* **updateShouldNotify**
    * 他のプロバイダの同名の引数と同様です。

[FutureProvider.value](https://pub.dev/documentation/provider/latest/provider/FutureProvider/FutureProvider.value.html) もありますが、省略します。


## [ProxyProvider](https://pub.dev/documentation/provider/latest/provider/ProxyProvider/ProxyProvider.html)

[下位 Widget で値を得る方法](#下位-widget-で値を得る方法) のところで既に触れたものです。

これも同じ動作にするのが難しかったため、異なるサンプルにしました。
カウンターという点は同じですが、10進数と16進数の二つの表示になっています。

![flutter_proxy_provider.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132102/be402e07-a694-7083-e6cb-fa81bea26663.gif)

1. 10進数と16進数のカウンターがある
    * 10進数は `ValueListenableProvider` のサンプルで使ったものを流用。
    * 16進数のほうは10進数の値を受け取って保持する immutable なクラスであり、ゲッターで返すときに16進数に変換する。
1. `ChangeNotifierProvider` で10進数カウンターを生成
1. `ProxyProvider` で10進数の値を渡して16進数カウンターを生成
1. 10進数カウンターの値が変わるたびに `ProxyProvider` の `update` が呼ばれる
    * そのたびに16進数カウンターのインスタンスが生成される。
1. 両カウンターのインスタンスが Widget ツリーの下位に伝播される
    * 10進数のカウントアップに連動して16進数の値も更新される。

```dart:models/hex_counter.dart
class HexCounter {
  int _decimal;

  HexCounter();

  set newValue(int newValue) => _decimal = newValue;

  String get hex => _decimal.toRadixString(16);
}
```

```dart:pages/proxy_provider.dart
class ProxyProviderPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<VnCounter>(
          create: (_) => VnCounter(),
        ),
        ProxyProvider<VnCounter, HexCounter>(
          create: (_) => HexCounter(),
          update: (_, decCounter, prevHexCounter) =>
              prevHexCounter..newValue = decCounter.value,
        ),
      ],
      child: Scaffold(
        body: _CounterResults(),
        floatingActionButton: _FloatingButton(),
      ),
    );
  }
}

class _FloatingButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<VnCounter>(context, listen: false);

    return FloatingActionButton(
      onPressed: counter.increment,
      child: const Icon(Icons.add),
    );
  }
}

class _CounterResults extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final decCounter = Provider.of<VnCounter>(context);
    final hexCounter = Provider.of<HexCounter>(context);

    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          const Text('Decimal'),
          Text(decCounter.value.toString()),
          const Text('Hexadecimal'),
          Text(hexCounter.hex),
        ],
      ),
    );
  }
}
```

`create` は一度しか呼ばれず、`update` は何度も呼ばれます。
`create` で生成されたインスタンスを `update` のコールバック関数の第3引数で受け取って再利用することができ、再利用して更新した値を下位に伝えたいときに役立ちます。

なお、`ChangeNotifierProxyProvider` と `ListenableProxyProvider` では、provider v3.1.0 において `create` が必須となりました。

※v3.1.0 時点では `create` ではなく `initialBuilder`、`update` ではなく `builder` という引数名でした。
builder の呼ばれるタイミングが ProxyProvider だけ異なるというややこしさがありましたが、その後 v3.2.0 のリネームにより解消しました。

```dart
ChangeNotifierProxyProvider<Foo, MyChangeNotifier>(
  create: (_) => MyChangeNotifier(),
  update: (_, foo, myNotifier) => myNotifier
    ..foo = foo,
  child: ...
);
```

このコードは `ChangeNotifierProxyProvider` の [ドキュメント](https://pub.dev/documentation/provider/latest/provider/ChangeNotifierProxyProvider-class.html) からの引用したものです。
MyChangeNotifier のインスタンスを `create` で一度だけ生成しています。
`update` が呼ばれるたびに生成済みのインスタンスを第3引数で受け取って使い回すことになります。


## [InheritedProvider](https://pub.dev/documentation/provider/latest/provider/InheritedProvider/InheritedProvider.html)

[クラスのドキュメント](https://pub.dev/documentation/provider/latest/provider/InheritedProvider-class.html) に次のように書かれています。

> Do not use this class directly unless you are creating a custom "Provider".
> Instead use Provider class, which wraps InheritedProvider.

訳： カスタムなプロバイダを作る以外では `InheritedProvider` クラスを直接使わず、
　　 それをラップした `Provider` クラスを代わりに使うこと。

基本的に直接触る必要がないものです。


## [ReassembleHandler](https://pub.dev/documentation/provider/latest/provider/ReassembleHandler-class.html)

Provider ではなくインタフェースです。
このインタフェースを実装（`reassemble()` というメソッドを用意）してそこに何らかの処理を書いておくと、Hot reload 等でリビルドされたときにその処理が実行されます。

実際の用途としては Hot reload 時にそのクラスのオブジェクトの値だけリセットするといったところかと思うのですが、ここでのサンプルとしては良いものが浮かばず、ボタン押下時だけでなく Hot reload 時にもカウンターの値が増えるというサンプルにしてみました。

```dart
class ReassemblingCounterNotifier extends ValueNotifier<int> with ReassembleHandler {
  ReassemblingCounterNotifier() : super(0);

  @override
  void reassemble() {
    increment();
  }

  void increment() {
    value++;
  }
}
```

コード全体は [GitHub にあるプロジェクト](https://github.com/kaboc/flutter_provider_examples) の該当箇所をご覧ください。
（実際のコードではリビルド時に SnackBar の表示もしています。）


# lazy

v4.0.0 で各プロバイダのコンストラクタ（MultiProvider や名前付きコンストラクタを除く）に導入された引数です。
省略するか `true` を指定すると、`create` に渡したコールバック関数で生成されるオブジェクトが最初に使用されるときまでその生成が遅延されます。

```dart
class Foo {
  Foo() {
    print('Foo was created.');
  }
}

class Bar {
  Bar(this.foo) {
    print('Bar was created.');
  }

  final Foo foo;

  void method() {
    print("Bar's method was called.");
  }
}
```

```dart
MultiProvider(
  providers: [
    Provider<Foo>(
      create: (context) {                 // 3
        print('Creating Foo...');
        return Foo();                     // 4
      },
    ),
    Provider<Bar>(
      create: (context) {                 // 2
        print('Creating Bar...');
        final foo = context.read<Foo>();  // 5
        return Bar(foo);                  // 6
      },
    ),
  ],
  builder: (context, child) {
    return RaisedButton(
      child: child,
      onPressed: () {                     // 1
        print('Button was pressed.');
        context.read<Bar>();              // 7
      },
    );
  },
  child: const Text('Push'),
)
```

この例では `lazy` を省略していて `true` になるため、`create` に渡したコールバック関数はすぐに呼ばれません。
ボタンを押すと初めて Foo や Bar が生成されます。
そのときの出力は次のようになります。

```
I/flutter ( 4276): Button was pressed.
I/flutter ( 4276): Creating Bar...
I/flutter ( 4276): Creating Foo...
I/flutter ( 4276): Foo was created.
I/flutter ( 4276): Bar was created.
I/flutter ( 4276): Bar's method was called.
```

試しに `Provider<Bar>(` の次の行に `lazy: false,` を加えてみてください。
そうするとボタンを押さなくてもコールバックが実行されて次の出力になります。

```
I/flutter ( 4276): Creating Bar...
I/flutter ( 4276): Creating Foo...
I/flutter ( 4276): Foo was created.
I/flutter ( 4276): Bar was created.
I/flutter ( 4276): Button was pressed.
I/flutter ( 4276): Bar's method was called.
```


## [Locator](https://pub.dev/documentation/provider/latest/provider/Locator.html)

v4.1.0 で追加されました。
DI に使うことができます。

早速ですが用例を見てみましょう。

```dart:read()のドキュメントに書かれているLocatorの使用例
class Model {
  Model(this.locator);

  final Locator locator;

  void method() {
    print(locator<Whatever>());
  }
}

// ...

Provider(
  create: (context) => Model(context.read),
  child: ...,
)
```

これまでは Whatever のオブジェクトを Model 内で使うには、そのオブジェクト自体を Model に渡すか、`BuildContext` を渡して Model の中で `Provider.of()` を使って取り出す必要がありました（後者は Provider や Flutter に依存してしまうアンチパターン）。

一方 `Locator` を使うと、上記のように `context.read` を Model で受け取っておけば `locator<Whatever>()` で値にアクセスすることができます。

この `Locator` は次のように定義されていて、Flutter への依存がありません。
依存させたくない Model の中でも安心して使えることになります。

```dart
typedef Locator = T Function<T>();
```

ただし、これが便利かどうかは使用者の考え次第でしょう。[^locator]

[^locator]: 個人的には、Model がどのようなオブジェクトを使うのかが渡す側から見てわかりにくいと感じました。また、単体テスト等で `T Function<T>()` 型にして渡す手間も生じます。一方で、依存が増えても渡す側を変更する手間をなくせます。そのようなメリットとデメリットのトレードオフになります。


# トラブルシューティング

## 遷移先のページで値を得られない

解決方法は主に三つあります。

### MaterialApp より上で渡す

`MaterialApp` より下で Provider によって渡そうとしても次のページからは値にアクセスできません。
これは、遷移前後のページ間に親子関係がないからです。
遷移先から見た親は遷移前のページではなく、最寄りの `Navigator`（自分で新たな `Navigator` を追加しない限りは `MaterialApp`）です。
そこより上で渡していなければ受け取れません。
逆に言うと、`MaterialApp` より上で渡していれば問題に遭遇せずに済むということです。

これで解決できるわけですが、上の位置になるほどツリーから外される機会が減って寿命が長くなりますので、そのことが好ましくない場合には残りの方法を検討しましょう。

### Navigator を追加する

今述べた構造から想像できたかもしれませんが、遷移前に `Navigator` を追加すれば遷移先ページの親がその `Navigator` になり、それより上（`MaterialApp` より下であっても）で渡している値にアクセスできるようになります。

ただし、`Navigator` が多重になることで何らかの悪影響が出る可能性があります。
この方法を使う場合にはご注意ください。

### ページ遷移時に渡し直す

```dart
// A
Navigator.of(context).push(
  MaterialPageRoute<void>(builder: (context2) {
    // B
    return NextPage();
  }),
)
```

A 以上と B 以下の `BuildContext`（context と context2）は大きく異なるものです。
名前に 2 をつけて区別していますが、名前だけでなく中身も違っていて、ツリーの同じ枝上にすらありません。
これも先ほど説明した構造によるもので、ツリーは `MaterialApp` で分岐しているので、context2 を用いて遡っても他の枝にある元ページのほうで渡された値にアクセスできないのです。

どうしても無理ですから、受け取るには A の `BuildContext` を使うしかありません。
そうやって受け取った値を B 側で渡し直せば解決します。

```dart
Navigator.of(context).push(
  MaterialPageRoute<void>(
    builder: (context2) => Provider<Foo>.value(
      value: Provider.of<Foo>(context, listen: false),
      child: NextPage(),
    ),
  ),
)
```

ちょっと冗長な書き方になりますね。
ツリーの深いところまで渡す必要がなければ、プロバイダを使わずにコンストラクタの引数で渡すほうがシンプルで良い場合もあります。

```dart
Navigator.of(context).push(
  MaterialPageRoute<void>(builder: (context) => NextPage(foo)),
)
```

※[コンストラクタ経由以外の渡し方](https://flutter.dev/docs/cookbook/navigation/passing-data) もあります。


## initState() で値を得られない

```dart
class _FooPageState extends State<FooPage> {
  Bar _bar;

  @override
  void initState() {
    super.initState();
    _bar = Provider.of<Bar>(context);
  }

  ...
}
```

このように `initState()` にてプロバイダ経由で値を得ようとすると例外が発生します。

```
I/flutter ( 4953): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 4953): The following assertion was thrown building InheritedProvider<Bar>:
I/flutter ( 4953): inheritFromWidgetOfExactType(InheritedProvider<Bar>) or inheritFromElement() was called before
I/flutter ( 4953): _FooPageState.initState() completed.
I/flutter ( 4953): When an inherited widget changes, for example if the value of Theme.of() changes, its dependent
I/flutter ( 4953): widgets are rebuilt. If the dependent widget's reference to the inherited widget is in a constructor
I/flutter ( 4953): or an initState() method, then the rebuilt dependent widget will not reflect the changes in the
I/flutter ( 4953): inherited widget.
I/flutter ( 4953): Typically references to inherited widgets should occur in widget build() methods. Alternatively,
I/flutter ( 4953): initialization based on inherited widgets can be placed in the didChangeDependencies method, which
I/flutter ( 4953): is called after initState and whenever the dependencies change thereafter.
```

このエラーメッセージには答えがちゃんと書かれています。
特に最後の3行です。
`build()` 内か、`initState()` の後に呼ばれる `didChangeDependencies()` の中で行いましょう。

なお、didChangeDependencies() は initState() と違って何度も呼ばれるのでご注意ください。
State のライフサイクル（各メソッドが呼ばれるタイミング）については次のページがわかりやすいです。

* ~~[Stateful Widget Lifecycle | Flutter By Example](https://flutterbyexample.com/stateful-widget-lifecycle/)~~　ページがなくなったようです…
* https://stackoverflow.com/a/53379776/12086560

provider 4.0.0 以降では `listen: false` を指定すれば initState() 内で使用できます。
ただし、リビルドが起こるようなメソッドをそこで使うことはできません。

```dart
@override
void initState() {
  super.initState();
  _bar = Provider.of<Bar>(context, listen: false);
  //_bar.someMethodToCauseRebuild(0);
}
```


## build()内で値を変えるとエラー

Widget の `build()` 内で `ChangeNotifier` や `ValueNotifier` を使って値を更新すると起こる現象です。
例をご覧ください。

```dart
class FooModel extends ValueNotifier<String> {
  FooModel() : super('');
}
```

```dart
ChangeNotifierProvider<FooModel>(
  create: (_) => FooModel(),
  child: MyWidget(),
)
```

```dart
class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  @override
  Widget build(BuildContext context) {
    final model = Provider.of<FooModel>(context);

    // build()内ですぐに値を変える
    model.value = 'Hello!';

    return Center(
      child: Text(model.value),
    );
  }
}
```

1. `FooModel` の `value` を `ChangeNotifierProvider` が `Listen` している
2. 値が変わると、関連箇所をリビルドさせるために内部的に `dirty` という印が付けられる [^19]
3. そのタイミングは Widget のビルド中であってはならず、違反すると次の例外が発生する

[^19]: Flutter in Focus の [How Stateful Widgets Are Used Best - Flutter Widgets 101 Ep. 2](https://www.youtube.com/watch?v=AqCMFXEmf3w&list=PLjxrf2q8roU2HdJQDjJzOeO6J3FoFLWr2) という動画が非常にわかりやすいです。

```
I/flutter (19781): ══╡ EXCEPTION CAUGHT BY FOUNDATION LIBRARY ╞════════════════════════════════════════════════════════
I/flutter (19781): The following assertion was thrown while dispatching notifications for FooModel:
I/flutter (19781): setState() or markNeedsBuild() called during build.
I/flutter (19781): This ChangeNotifierProvider<FooModel> widget cannot be marked as needing to build because the
I/flutter (19781): framework is already in the process of building widgets.  A widget can be marked as needing to be
I/flutter (19781): built during the build phase only if one of its ancestors is currently building. This exception is
I/flutter (19781): allowed because the framework builds parent widgets before children, which means a dirty descendant
I/flutter (19781): will always be built. Otherwise, the framework might not visit this widget during this build phase.
I/flutter (19781): The widget on which setState() or markNeedsBuild() was called was:
I/flutter (19781):   ChangeNotifierProvider<FooModel>
I/flutter (19781): The widget which was currently being built when the offending call was made was:
I/flutter (19781):   MyWidget
```

これを避ける方法は、Widget のビルド中に `dirty` を付けないことです。
そう考えると、代わりのタイミングは次の二つになります。

* **ビルドより前（祖先がビルドされている間）**
`FooModel` の `value` の変更によって `dirty` が付く Widget がビルドされる前という意味です。
つまり `ChangeNotifierProvider` より前である必要があります。
`ChangeNotifierProvider` の `create` でも可能ですが、そこが OK なのは、その時点でまだビルドが始まっていないからだと思います。

* **ビルドが終わった後**
`ChangeNotifierProvider` の子孫のビルド中に値を変えても例外が発生します。
全子孫のビルドが終わったタイミングに処理をするには、下記のようなハックが必要です。[^20]
下記6つのうちどれを使っても、実行をビルド完了後に遅延させることができました。

[^20]: StackOverflow の [ここ](https://stackoverflow.com/a/49458289) や [ここ](https://stackoverflow.com/a/44271936) が助けになりました。書かれている質問は別件だったりするのですが、回答のほうはビルド中の `dirty` 化の問題にも当てはまります。

```dart
Future(() { /* 処理 */ });
```

```dart
Future.delayed(Duration.zero, () { /* 処理 */ });
```

```dart
WidgetsBinding.instance.addPostFrameCallback((_) { /* 処理 */ });
```

```dart
// package:flutter/scheduler.dartのインポートが必要
// 機能はWidgetsBindingのほうと全く同じ
SchedulerBinding.instance.addPostFrameCallback((_) { /* 処理 */ });
```

```dart
// dart:asyncのインポートが必要
scheduleMicrotask(() { /* 処理 */ });
```

```dart
Future.microtask(() { /* 処理 */ });
```

`initState()`、`didChangeDependencies()`、`build()` のどこに書いてもビルド後になります。

[^21]: 仕組みは難しくて私も理解が不十分ですが、[公式の非同期処理のドキュメント](https://dart.dev/tutorials/language/futures#async-await) や [こちらの動画](https://www.youtube.com/watch?v=pHpvfaanrbw) を見ると理解が進むと思います。この二つのリンク先は StackOverflow の [ここ](https://stackoverflow.com/a/51273797) で知ったものです。

**注意**

* 使う前によく検証しましょう。

* `initState()` 以外はリビルドのたびに呼ばれ、値更新→リビルド→値更新… と繰り返してしまいます。
フラグなどで制御する必要があると思います。

* [こちらの SO の情報](https://stackoverflow.com/q/49466556) では `addPostFrameCallback()` でできた人とできなかった人がいます。個人的には問題なく使えています。


## Selector で複数の値を対象にしたい

[公式ドキュメント](https://pub.dev/documentation/provider/latest/provider/Selector-class.html) では [tuple](https://pub.dev/packages/tuple) を使うのが最も簡単な方法だと紹介されています。
次の例では `foo.bar` か `foo.baz` の値が変わったときのみ `builder` が呼ばれます。

```dart
Selector<Foo, Tuple2<Bar, Baz>>(
  selector: (_, foo) => Tuple2(foo.bar, foo.baz),
  builder: (_, data, __) {
    return Text('${data.item1}  ${data.item2}');
  }
)
```

しかし、[FAQ](https://pub.dev/packages/provider#can-i-obtain-two-different-providers-using-the-same-type) に書かれているように複数の値にそれぞれの型を持たせるのも良いでしょう。
FAQ の例では国名と市名を String ではなく Country と City という個別の型にすることで解決しています。


```dart
Provider<Country>(
  create: (_) => Country('England'),
  child: Provider<City>(
    create: (_) => City('London'),
    child: ...,
  ),
),
```


## ダイアログでエラーが出る

ダイアログに限りませんが、Provider と異なるツリーで `Provider.of()` を使おうとすると例外が発生し、次のようなエラーメッセージが出力されます。

```
Tried to listen to a value exposed with provider, from outside of the widget tree.
```

もしそうなったら、Provider.of() で `listen: false` を指定してみましょう。

これで解決しない場合、似て非なる原因かもしれません。
メッセージの続きに対処法が書かれていないか確認してみてください。


## Provider で生成したオブジェクトを直後の Provider で使いたい

`create` の関数に渡される `BuildContext` を使うと直前の Provider で生成されたオブジェクトにアクセスできます。

```dart:Providerをネストした場合
Provider<Foo>(
  create: (_) => Foo(),
  child: Provider<Bar>(
    create: (context) {  // このBuildContextを使う
      final foo = Provider.of<Foo>(context, listen: false),
      // または final foo = context.read<Foo>();
      return Bar(foo);
    },
    child: ...,
  ),
)
```

```dart:MultiProviderの場合
MultiProvider(
  Provider<Foo>(
    create: (_) => Foo(),
  ),
  Provider<Bar>(
    create: (context) {
      final foo = Provider.of<Foo>(context, listen: false);
      return Bar(foo);
    },
  ),
  child: ...,
)
```