Provider という言葉をほどく
Provider という一語は、パッケージ名・クラス名・総称として別々のものを指す。同名の Provider<T> を見分け、Riverpod 3 の provider 一覧と legacy API の読み方を整理する。
ここまでの 15 回で、Flutter アプリを読み、画面を作りきるための基礎を一周しました。ここからは、 順番に積み上げる基礎ではなく、実際のコードで設計判断が必要になるテーマを掘り下げます。
最初のテーマは Riverpod と依存性注入です。4 回を使って、provider の種類、状態を更新する側、 依存性注入(DI)そのもの、そして Riverpod が依存をどう組み立てるかを順に見ていきます。
ところが、その入口にある Provider という語が、すでに一つではありません。パッケージ名にも クラス名にも、複数の種類をまとめる総称にも使われます。検索して見つけたコードが何を指しているか 分からなければ、その先の仕組みも読めない。今回はまず、この一語をほどきます。
§ 01WHY一語で五つのものを指している
Flutter と Riverpod の周辺で「Provider」と言ったとき、少なくとも次の五つが候補になります。
- Flutter の状態管理ライブラリである
providerパッケージ providerパッケージが持つProvider<T>Widget- Riverpod の値の入口をまとめた総称としての provider
- Riverpod が持つ具体的なクラス
Provider<T> - .NET にある別の設計パターン provider model
二つのパッケージに同名のクラスがあり、その片方ではクラス名と総称まで重なっています。大文字か 小文字かだけで判別できる場面もありますが、会話や日本語の文章ではその手がかりも消えます。
まず重要なのは、「Provider は何ですか」に答えは一つしかない、と思わないことです。どの パッケージの、クラスなのか総称なのかを確かめる。それだけで検索結果の読み違いをかなり防げます。
§ 02SAME-NAME同じ Provider<T> が二つある
混乱の中心は、provider パッケージと Riverpod の両方に Provider<T> というクラスがある
ことです。名前は同じでも、コードの中で占める場所が違います。
provider パッケージの Provider<T> は Widget です。create で値を作り、Widget ツリーの
中へ置きます。
Provider<Foo>(create: (context) => Foo(),child: const App(),)
Riverpod の Provider<T> は Widget ではありません。値の作り方を表すトップレベルの宣言です。
final fooProvider = Provider<Foo>((ref) => Foo());
見分け方は構文に現れます。
| 見る場所 | provider パッケージ | Riverpod |
|---|---|---|
| 置かれる場所 | Widget ツリーの中 | トップレベル |
| 受け取るもの | BuildContext を使う create | Ref を受け取る関数 |
| 自身の正体 | Widget | provider の宣言 |
Provider<Foo>(create: ..., child: ...) なら前者、final fooProvider = Provider<Foo>((ref) ...)
なら後者です。クラス名だけを見ず、Widget として置いているか、宣言として置いているかを見ると
迷いません。
§ 03ORIGINprovider から Riverpod へ
二つが似た名前なのは偶然ではありません。provider と Riverpod の作者はどちらも
Remi Rousselet で、Riverpod という名前自体が Provider のアナグラムです1provider と Riverpod は Remi Rousselet が手がけた別々のパッケージで、Riverpod は Provider のアナグラム。provider は現在も保守されており、Riverpod への移行によって廃止されたわけではない(pub.dev の provider)。。
provider は、Flutter の InheritedWidget を使いやすく、再利用しやすく包んだパッケージです。
したがって仕組みは Widget ツリーの上にあり、値を探す入口には BuildContext を使います。
Riverpod は、そこから InheritedWidget と BuildContext への依存を外すために書き直されました。
状態を Widget ツリーの外でも扱え、依存の不足を実行時まで待たずに検出しやすくなっています。
ただし、これは「provider は古く、Riverpod に捨てられた」という話ではありません。provider
は現在も維持されています。両者は同じ問題意識から生まれていますが、Widget ツリーに載せる
仕組みと、ツリーから独立した宣言を読む仕組みという別の選択肢です。
§ 04PATTERN別の生態系にも同名のものがある
「Provider」は GoF の 23 パターンにも、PoEAA のカタログにも載っていません。だからといって、 「プロバイダーパターンはどこにも存在しない、役割だけの名前」と言い切るのも正しくありません。
.NET には Microsoft が名付けた provider model があります。Strategy と Abstract Factory を 組み合わせ、設定によって実装を差し替える仕組みとして整理されたものです。「Strategy の別名に すぎない」という批判もありますが、名前のあるモデルとして .NET Framework 2.0 で正式に採用 されました2Microsoft の provider model は 2002 年に命名され、.NET Framework 2.0 で正式化された。Strategy と Abstract Factory を組み合わせ、設定によって実装を差し替えるモデルを指す(Wikipedia “Provider model”)。。
ここで指しているのは、状態を保持して UI に通知する Riverpod の provider ではありません。 実装を差し替えるという、むしろ DI に近い話です。同じ「provider」という語が、別の生態系で また別のものを指しています。
したがって、この連載では「プロバイダーパターン」という曖昧な呼び方は使いません。Riverpod の
provider なのか、provider パッケージなのか、.NET の provider model なのかを明示します。
§ 05MAPRiverpod 3 の provider を並べる
Riverpod の中だけに絞っても、provider は一種類ではありません。Riverpod 3 の主な API は六つです。
| provider | 用途 |
|---|---|
Provider | 読み取り専用の値・派生値 |
FutureProvider | 非同期に一回取得する値 |
StreamProvider | 継続的に流れてくる値 |
NotifierProvider | 変更できる同期状態 |
AsyncNotifierProvider | 変更できる非同期状態 |
StreamNotifierProvider | 変更できる Stream 状態 |
上の三つは、外から値を更新するメソッドを持たない provider です。下の三つは、状態を更新する側を 持ちます。どのように更新し、いつ作り直すかは、次回にまとめて扱います。
一方、検索すると次の三つにも頻繁に出会います。
StateProviderStateNotifierProviderChangeNotifierProvider
これらは削除されたわけではありませんが、Riverpod 3 では legacy API として
package:flutter_riverpod/legacy.dart へ移されました。新しいコードでは Notifier 系への
移行が推奨されています3Riverpod 3 では StateProvider / StateNotifierProvider / ChangeNotifierProvider が legacy.dart へ移された。既存コードのために残っているが、新しいコードでは Notifier 系への移行が推奨されている(Riverpod 3 移行ガイド)。。
古い記事で StateProvider を見たら「単純な変更可能状態」、StateNotifierProvider を見たら
「StateNotifier が更新する状態」、ChangeNotifierProvider を見たら「ChangeNotifier の
変更通知を Riverpod へつないだもの」と読めば、まず役割は追えます。ただし、新しい API への移行は
名前の置換だけで済むとは限りません。検索結果を読むための地図と、新しく書く形は分けて考えます。
§ 06WORDSこの連載での書き分け
ここまでの区別を、残り 3 回で使う言葉として固定します。
provider… Flutter のパッケージ名。コード表記にし、文脈で紛れるときは 「パッケージ」を添える- Riverpod の provider … 六つの主 API などをまとめた総称。パッケージ名や具体クラスと 見分けるため、コード表記にせず小文字で書く
Provider<T>… Riverpod の具体的なクラス。providerパッケージ側なら、その旨を明記する- provider model … .NET の別の設計モデル。Riverpod の話には混ぜない
そして「Provider を DI に使う」という表現の Provider は、Provider<T> 一種類だけではなく、
Riverpod の provider という総称を指します。ただし、この言い方だけでは何をどう注入しているかが
まだ曖昧です。この連載では、依存性注入そのものを整理したうえで、Riverpod のどの構造がそれを
担うのかを後半 2 回で分けて見ます。
§ 07SUMMARY名前から読み違えない
- 「Provider」は、パッケージ・クラス・総称・別生態系のモデルまで五つのものを指しうる
providerパッケージのProvider<T>は Widget、Riverpod のProvider<T>は トップレベルの宣言providerと Riverpod は同じ作者。前者はInheritedWidgetのラッパー、後者はBuildContextへの依存を外した書き直し- .NET の provider model は実装を差し替える別の仕組み。Riverpod の状態管理とは混ぜない
- Riverpod 3 の主 API は六つ。
StateProviderなど三つは legacy API - この連載では、総称を「Riverpod の provider」、具体クラスを
Provider<T>と書き分ける
語の地図ができたので、次は値を更新する側へ進みます。Notifier / AsyncNotifier が状態を
どう持ち、invalidate / refresh が provider をどう作り直すのかを整理します。
- [1]
providerと Riverpod は Remi Rousselet が手がけた別々のパッケージで、Riverpod は Provider のアナグラム。providerは現在も保守されており、Riverpod への移行によって廃止されたわけではない(pub.dev のprovider)。 ↩ - [2] Microsoft の provider model は 2002 年に命名され、.NET Framework 2.0 で正式化された。Strategy と Abstract Factory を組み合わせ、設定によって実装を差し替えるモデルを指す(Wikipedia “Provider model”)。 ↩
- [3] Riverpod 3 では
StateProvider/StateNotifierProvider/ChangeNotifierProviderがlegacy.dartへ移された。既存コードのために残っているが、新しいコードではNotifier系への移行が推奨されている(Riverpod 3 移行ガイド)。 ↩