mcpField
R
riverpod-from-provider

riverpod-from-provider

github.com/serverpod開発1065 回インストール34 週間前に更新
リポジトリ

riverpod-from-provider について

Flutter の状態管理を Provider から Riverpod へ移行する開発者向けのガイドです。段階的な移行方法や API の変更点、Provider の一般的なパターンの置き換え方を説明します。

主な機能

  1. 01段階的な移行ガイド
  2. 02Provider と Riverpod の併用
  3. 03ChangeNotifier の移行手順
  4. 04ref.watch による Provider の合成
  5. 05パラメーター化と自動破棄状態
  6. 06Provider のオーバーライドによるテスト

活用シーン

  1. 01Provider アプリの段階的な移行
  2. 02ProxyProvider の依存関係の置き換え
  3. 03パラメーターごとの状態管理
  4. 04テストでの Provider のモック化

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/serverpod/skills-registry --skill riverpod-from-provider

インストール後、Claude Code で/riverpod-from-providerを入力して読み込まれたか確認します。

詳細

Riverpod — Provider からの移行

動機

Riverpod は Provider の後継として作られ、InheritedWidget の制限に対処しています。

  • 同じ型: Provider ではツリー内に Provider<Item> を 2 つ配置できません(最も近いものだけが見つかります)。Riverpod にはそのような制限はなく、プロバイダーは型ではなく変数によって識別されます。
  • プロバイダーの結合: ProxyProvider は手間がかかり、エラーが起きやすいです。Riverpod では、プロバイダー内で ref.watch を使って他のプロバイダーに依存します。構成はシンプルです。
  • AsyncValue: Riverpod では読み込み中も以前のデータを公開できます(例:古いリストと読み込みインジケーターを表示)。Provider では、これを簡潔に実現できません。
  • 安全性: Provider では実行時に ProviderNotFoundException がスローされることがあります。Riverpod では設計上、この問題が起きません。
  • 破棄: Provider では、利用側がリッスンを停止したときに対応できません。また、スコープの管理も困難です。Riverpod には、ライフサイクルとキャッシュを明確に管理できる autoDispose と ref.keepAlive があります。
  • パラメーター: Riverpod の .family では、型安全なパラメーター付きプロバイダーと、パラメーターごとの状態を利用できます。autoDispose を使えば、使用されなくなった状態は破棄されます。Provider で同等のことを実現するのは現実的ではありません。
  • テスト: Provider ではテストごとにプロバイダーを再定義する必要があります。Riverpod では overrides を使ってモックに置き換えられます。
  • 副作用: Riverpod には、変更への反応(例:画面遷移、スナックバーの表示)に使える ref.listen があります。Provider には組み込みの同等機能がありません。

主な API の変更点は、StatelessWidget の代わりに ConsumerWidget(および WidgetRef)を使い、context.watch/context.read の代わりに ref.watch/ref.read を使うことです。


クイックスタート

  • Riverpod のスタートガイド(riverpod-getting-started)を読み、小さなサンプルを試してください。
  • 段階的に移行してください。Provider と Riverpod は並行して実行できます(必要に応じてインポートのエイリアスを使ってください)。
ChangeNotifierProvider から始める

既存の ChangeNotifier クラスはそのままにして、Riverpod の ChangeNotifierProvider でラップします。

final myNotifierProvider = ChangeNotifierProvider<MyNotifier>((ref) => MyNotifier());

ルートに ProviderScope を配置します。このプロバイダーを使用している箇所(例:ConsumerWidget 内)で、context.watch を ref.watch に置き換えてください。すべての ChangeNotifier をただちに Notifier に変換する必要はありません。

リーフから始める

まず依存関係のないプロバイダー(「リーフ」)を移行し、次にそれらに依存するプロバイダーを移行します。依存先の移行が済むまでは、ProxyProvider の移行を避けてください。

一度に 1 つのプロバイダーを移行する

一度に 1 つのプロバイダーを移行してテストします。ChangeNotifier の移行を完了するには、(1)Notifier + NotifierProvider に変換し、(2)そのプロバイダーに対するすべての context.watch を ref.watch に置き換えます。

ProxyProvider → ref.watch

Riverpod では、別のプロバイダー内で ref.watch を使ってプロバイダーを結合します。

final labelProvider = Provider<String>((ref) {
  final userIdNotifier = ref.watch(userIdNotifierProvider);
  return 'The user ID is ${userIdNotifier.userId}';
});

ChangeNotifierProxyProvider のような状態を持つ結合オブジェクトには、プロバイダー内で ref.listen を使い、別のプロバイダーに反応して自分の notifier を更新します。

遅延初期化をしない場合

Riverpod のプロバイダーは遅延初期化されます。起動時にデータを読み込んでおくには、ルートでプロバイダーを監視します(例:アプリを子として返す、ProviderScope 配下の Consumer 内)。riverpod-eager-initialization を参照してください。

コード生成

コード生成では ChangeNotifierProvider は生成されません。移行中に ChangeNotifier を @riverpod で公開するには、小さな拡張(listenAndDisposeChangeNotifier)を使えます。Notifier に切り替えたら、この拡張を削除してください。コードスニペットについては公式クイックスタートを参照してください。


Provider と Riverpod の比較

プロバイダーの定義
  • Provider: プロバイダーはウィジェットです(例:MultiProvider 内)。
  • Riverpod: プロバイダーはトップレベルの final 変数です。定義にウィジェットツリーは必要ありません。アプリのルートに ProviderScope を追加します。
読み取り
  • Provider: context.watch<T>()、context.read<T>()、context.select<T,R>(...)。
  • Riverpod: ConsumerWidget(または Consumer)を使って WidgetRef ref を取得し、ref.watch(provider)、ref.read(provider)、ref.watch(provider.select(...)) を使います。
  • build 内では watch を、イベントハンドラー内では read を使います。Provider と同じ考え方です。
コンシューマー

Riverpod には (context, ref, child) を受け取る Consumer があります。Consumer2、Consumer3 などは必要ありません。1 つのビルダー内で ref.watch を複数回呼び出すだけです。

スコープと family + autoDispose の比較

Provider では、状態を破棄したり、ページごとの状態を持たせたりするためにスコープを使用します。Riverpod では次のようになります。

  • autoDispose はリスナーがいなくなると状態を破棄します(ref.onCancel / ref.onDispose)。
  • .family はパラメーター化されたプロバイダーを作成します(パラメーターごとに 1 つの状態)。キャッシュが無制限に増えるのを防ぐため、autoDispose と併用してください。

つまり、スコープの代わりに、「X ごとの状態」には .family を、「未使用時に破棄」するには .autoDispose を使用してください。

詳しくは、riverpod-getting-started、riverpod-providers、riverpod-family、riverpod-auto-dispose をご覧ください。

コメント