Riverpodのプロバイダの利用方法について説明してみた

## そもそもプロバイダってなに？
RiverPodの公式ドキュメントによると、

「プロバイダは Riverpod において中心的な役割を担っています。 プロバイダはあるステート（状態）をラップするためのオブジェクトであり、その監視を可能にしてくれます。」

とあります。要するに、プロバイダはアプリの色々な場所から、ステートの状態にアクセスできるようにしてくれるんです。

# プロバイダの利用方法

### プロバイダの作成

 プロバイダの作成は基本的にグローバル変数として、宣言をしていきます。

 ```
 final myProvider = Provider((ref) {
  return MyValue();
});
```

- グローバルで宣言します。（プロバイダはイミュータブルなので、心配はないです。）
- `final`で宣言します。
- 右辺の`Provider`で使用するプロバイダの種類を指定しています。他にも、`FutureProvider`や`StreamProvider`などの様々な種類のプロバイダがあるので、調べてみてください。

### プロバイダ修飾子

　必要であれば、プロバイダ修飾子をつけてプロバイダを作成してください。
 
##### プロバイダ修飾子の種類

- `.autoDispose`：　プロバイダの監視が終わったタイミングで自動的にステートを破棄します。
- `.family`     ：　プロバイダ外部の値を用いて、プロバイダを作成できます。

以下、使用例です。
```
final myAutoDisposeProvider = StateProvider.autoDispose<int>((ref) => 0);
```

# プロバイダの利用

## `ref`オブジェクトの取得

### プロバイダからの`ref`取得

プロバイダは全て`ref`オブジェクトを引数とします。

```
final provider = Provider((ref) {
  // `ref` を通じて他のプロバイダを利用する
  final repository = ref.watch(repositoryProvider);

  return SomeValue(repository);
})
```
この`ref`を渡す例は以下の通りです。

```
final countProvider = StateNotifierProvider<Count, int>((ref) {
  return Count(ref);
});

class Counter extends StateNotifier<int> {
  Counter(this.ref) : super(0);

  final Ref ref;

  void increment() {
    // Count は `ref` を使って他のプロバイダーを利用することができる
    final repository = ref.read(repositoryProvider);
  }
}
```

このようにすることでStateNotifierの`Count`は`ref`を通して、別のプロバイダを利用可能になります。

### ウィジェットからの`ref`取得

通常のウィジェットは`ref`を取得できないので、Riverpodでは特別なウィジェットを用意しています。

- StatelessWidget → ConsumerWidget
- StatefulWidget (State) → ConsumerStatefulWidget (ConsumerState)
- HookWidget →　HookConsumerWidget    (flutter_hooksを使用している場合）

### `ref.watch`

プロバイダか、ウィジェットの中で、`ref.watch`を用いて、プロバイダを監視することができます。実際のところ、`ref.watch`では値の変化に応じて、ウィジェットやプロバイダを変化させます。

以下はウィジェット内で、`ref.watch`を使って、プロバイダの値が変わるたびに画面を更新させる処理の例です。

```
final counterProvider = StateProvider((ref) => 0);

class HomeView extends ConsumerWidget {
  const HomeView({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // `ref` を使ってプロバイダを監視する
    final counter = ref.watch(counterProvider);

    return Text('$counter');
  }
}
```
今回の場合、カウンターの数を値として管理するプロバイダを監視しており、プロバイダの値が変化すると、ウィジェットが更新されて、変化後の値で画面が作られます。

### `ref.listen`

`ref.listen`はエラー発生時のモーダル表示などの、変化に応じた処理をするときに使えます。
`ref.listen`メソッドは2つの引数を持っています。第一引数はプロバイダ、第二引数はステートが変化したときに実行されるコールバック関数を呼び出した際に、プロバイダの変更前後のステートの値が渡され、それぞれを使用できます。

以下のプロバイダ内で使用した例です。
```
final counterProvider = StateNotifierProvider<Counter, int>(Counter.new);

final anotherProvider = Provider((ref) {
  ref.listen<int>(counterProvider, (int? previousCount, int newCount) {
    print('The counter changed $newCount');
  });
  // ...
});
```

また、`ref.listen`はウィジェットの`build`メソッド内で使用できます。


```

final counterProvider = StateNotifierProvider<Counter, int>(Counter.new);

class HomeView extends ConsumerWidget {
  const HomeView({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    ref.listen<int>(counterProvider, (int? previousCount, int newCount) {
      print('The counter changed $newCount');
    });
    
    return Container();
  }
}
```

### `ref.read`
`ref.read`メソッドを使うことでプロバイダの現在のステートを取得できます。`ref.read`はユーザの操作によって呼び出される関数内で使用することができる。

```

final counterProvider = StateNotifierProvider<Counter, int>(Counter.new);

class HomeView extends ConsumerWidget {
  const HomeView({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // `Counter` クラスの `increment()` メソッドを呼び出す
          ref.read(counterProvider.notifier).increment();
        },
      ),
    );
  }
}
```

このようにプロバイダを用いて、ステータスにアクセスしましょう。
弊社のFlutterアプリでもRiverpodアーキテクチャによく使われるこれらプロバイダを用いて、状態管理をしています。