Provierを経由してWidgetに値を伝播する方法

# はじめに
`StateNotifierProvider`や`ChangeNotifierProvier`などの`Provider`から、自作の`Provier`を経由して、`Widget`に値を伝播する方法のサンプルです。
メリット等は解説せず、導入後に「実際にどう書けばよいか分からない。。。」と詰まった方向けの内容となっています。


https://riverpod.dev/ja/docs/concepts/reading

> プロバイダから`ref`を取得する

この章単体で読んでもイメージが掴みにくかったのですが、サンプルコードと一緒だとイメージしやすいと思いサンプルを作りました。
詳細は公式リファレンスをご確認ください。

# 環境
```
Flutter: 3.0.5

hooks_riverpod: 1.0.4
flutter_hooks: 0.18.5
```

# 結論
以下のコードで下記画面が作れます。
![sample.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2611496/e2d728ee-86c2-bf84-2b76-004c194a8d36.gif)


```dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

class SamplePage extends HookConsumerWidget {
  const SamplePage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      body: Center(
        child: Text(ref.watch(calculatorProvider).calculatedValue.toString()),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: ref.read(numberNotifierProvider.notifier).plusOne,
        child: const Icon(Icons.add),
      ),
    );
  }
}
```

```dart
final calculatorProvider = Provider((ref) => Calculator(ref));

class Calculator {
  Calculator(this.ref);

  final Ref ref;

  int get calculatedValue {
    return ref.watch(numberNotifierProvider) * 2;
  }
}
```
```dart
final numberNotifierProvider =
    StateNotifierProvider<NumberNotifier, int>((ref) => NumberNotifier());

class NumberNotifier extends StateNotifier<int> {
  NumberNotifier() : super(0);

  void plusOne() {
    state++;
  }
}
```



# おまけ①

上記書き方をベースに、`Calculator`の部分をViewModelにすることで、MVVMライクなViewModelのProviderを作ることもできます。
※FlutterでMVVMアーキテクチャを推奨している意味合いではございません。本記事ではアーキテクチャについては取り扱いません。
```dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

class SettingPage extends HookConsumerWidget {
  const SettingPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      body: Center(
        child: Text(
          ref.watch(settingPageViewModelProvider).darkMode
              ? 'ダークモード'
              : 'ライトモード',
        ),
      ),
    );
  }
}

final settingPageViewModelProvider =
    Provider((ref) => SettingPageViewModel(ref));

class SettingPageViewModel {
  SettingPageViewModel(this.ref);

  final Ref ref;

  bool get darkMode {
    return ref.watch(darkModeNotifierProvider);
  }
}

final darkModeNotifierProvider =
    StateNotifierProvider<DarkModeNotifier, bool>((ref) => DarkModeNotifier());

class DarkModeNotifier extends StateNotifier<bool> {
  DarkModeNotifier() : super(false);

  void change() {
    state = !state;
  }
}

```

# おまけ②
また、`Caluculator`を以下のように書いても上記はじめのコードと全く同じく`Provider`に値を伝播させることができます。

```dart
final calculatorProvider =
    Provider((ref) => Calculator(ref.watch(numberNotifierProvider)));

class Calculator {
  Calculator(this.value);

  final int value;

  int get calculatedValue {
    return value * 2;
  }
}
```

# おわりに
実装に悩んでいる際`StateNotifier`や`ChangeNotifier`を使用して、`Widget`に情報を伝える方法を解説している記事は多くヒットしたのですが、`Provider`を経由した場合の記事がほとんどヒットしなかったので、サンプルコードを記事にしてみました。

この記事で挫折する人を減らしてRiverpodの楽しさを共有できたら幸いです。

// おまけの方が行数多い。。。