📘

【Riverpod学習】Providerの種類〜Providerについて〜

2024/10/11に公開

Providerの種類について以下の公式サイトをもとに学習したのでその備忘録。

https://riverpod.dev/ja/docs/from_provider/quickstart

Provider

Providerは、Riverpodで最も基本的なプロバイダで、同期的な処理の結果を管理するために使われます。主に、計算結果をキャッシュ(一時的に保存)したり、他のプロバイダやクラス(例えば、RepositoryやHttpClient)のインスタンスを共有するために使われます。

Providerの基本的な使い方

Providerは、同期的にデータを生成し、それをキャッシュしてくれます。例えば、計算処理の結果をキャッシュして、無駄な再計算を防ぐことができます。これにより、パフォーマンスが向上します。

例: Providerで計算結果をキャッシュする
例えば、完了したタスクだけをフィルタリングする機能があるとします。このフィルタリング処理は、パフォーマンス的に重くなりがちです。もし毎回画面が再描画される度に(たとえばボタンを押したり、リストをスクロールしたり、新しいデータが到着したりする場合)フィルタが適用されていたら、whereメソッドの重いフィルタリング処理が毎回行われて、画面がカクついたり、レスポンスが遅くなったりパフォーマンスに影響を与えてしまいます。 Providerを使ってフィルタリングの結果をキャッシュし、再描画ごとに再計算されないようにします。

フィルタリングとキャッシュの例

まず、Todoというクラスと、それを管理するTodosNotifierというStateNotifierがあります。StateNotifierは、アプリの状態を管理し、タスクを追加したり、リストを更新するために使います。

TodosNotifierの定義:


class Todo {
  Todo(this.description, this.isCompleted);
  final bool isCompleted;
  final String description;
}

// TodosNotifierは、複数のTodoを管理するクラス(状態を管理するクラス)
// StateNotifierを使って、状態を管理する
class TodosNotifier extends StateNotifier<List<Todo>> {
  TodosNotifier() : super([]);

// 引数で渡されたTodoを現在のリストに追加している
  void addTodo(Todo todo) {
    state = [...state, todo];
  }
}

// todosProviderは、TodosNotifierが管理しているTodoリストを他の場所で使えるようにするためのプロバイダ
// StateNotifierProviderを使って、TodosNotifierクラスのインスタンスを提供する
final todosProvider = StateNotifierProvider<TodosNotifier, List<Todo>>((ref) {
  return TodosNotifier();
});

StateNotifier = 状態を管理するクラス(状態のロジックが入っている)
StateNotifierProvider = StateNotifierを他の場所で使えるようにするプロバイダ
このTodosNotifierでは、Todoリストを管理し、タスクを追加することができます。

Providerでフィルタリングの結果を管理する

次に、Providerを使って、完了したタスクだけをリストにフィルタリングして公開します。このフィルタリング結果は一度キャッシュされ、todosProviderの内容が変更されるまでは再評価されません。

completedTodosProviderの定義:

// completedTodosProviderは、完了したTodoだけをフィルタリングするプロバイダ
// Providerを使って、完了済みのTodoリストだけを提供する
final completedTodosProvider = Provider<List<Todo>>((ref) {

// todosProviderから現在のTodoリストを取得する
  final todos = ref.watch(todosProvider);

// Todoリストの中で、完了している(isCompletedがtrueの)Todoだけを取り出す
  return todos.where((todo) => todo.isCompleted).toList();
});

このcompletedTodosProviderでは、todosProviderから取得したTodoリストの中で、isCompletedがtrueのタスクだけをフィルタリングし、その結果をキャッシュしています。

UIでの利用

View側で、completedTodosProviderを使ってフィルタリングされたタスクを表示します。ref.watchを使うことで、Providerの値を監視し、その変更に応じてUIを再描画します。

UIでの例:

Consumer(builder: (context, ref, child) {
  final completedTodos = ref.watch(completedTodosProvider);
  return ListView.builder(
    itemCount: completedTodos.length,
    itemBuilder: (context, index) {
      return ListTile(
        title: Text(completedTodos[index].description),
      );
    },
  );
});

このコードでは、completedTodosProviderからフィルタリングされた完了タスクを取得し、それをリストビューで表示しています。
Consumerウィジェットを使用する理由はデータを関ししてUIを自動的に再描画するためです。

Providerのメリット

  • キャッシュ機能: Providerを使うことで、一度計算された結果はキャッシュされ、不要な再計算を防ぎます。
  • 自動更新: ref.watchのおかげで、元のProvider(この場合はtodosProvider)の値が変わると、キャッシュも自動で更新されます。手動でキャッシュを無効化する必要がないので、開発が簡単です。
  • 柔軟性: Providerは、他のProviderやクラスのインスタンスを渡すためにも利用でき、アプリ全体でデータの管理を柔軟に行えます。

まとめ

Providerは、同期的な処理結果をキャッシュするための基本的なツールです。
パフォーマンスを改善しつつ、計算結果の再利用を自動で管理してくれます。
状態が変わった際には自動でキャッシュを無効化し、再計算を行います。
ref.watchを使うことで、必要なときにだけ再評価され、効率よくアプリを動作させることができます。
この仕組みを利用することで、アプリのパフォーマンスや可読性を向上させることができます。

Discussion