freezed、stateNotifier、Riverpodで状態管理してみた。

# 初めに

Providerの作者が去年riverpodという状態管理法を作成しました。
今回は、私自身が使っている状態管理法freezed ＋ stateNotifier ＋ Provider のProviderの部分をRiverpodに変更して状態管理していこうと思います。

# Riverpodとは
Riverpodとは簡単に言うと、Providerの上位互換です。
最近、Flutterの公式ドキュメントにも状態管理の手法としてRiverpodが追加されたみたいです。
Riverpodの公式ホームページは以下です。
[Riverpodホームページ](https://riverpod.dev/docs/concepts/reading/)

英語ですが、非常にわかりやすいので参考にしてみてください。

何がProviderの上位互換なのかというと、
*  MultiPleProviderのようにWidgetをProviderでラップする必要がなくなった。
*   グローバル変数でProviderを使用可能。
* BuildContext配下で使う必要がなくなった。
etc...
いろいろあります。

雄一の欠点といえば、
1. これから破壊的な変更がある可能性あり。
2. 公式ドキュメントがあるが、日本語の説明がほぼない
3. Hooks的な使い方が出来るのですが、公式がHooksをいまだ認めていない(Hooksなしでも使用可能。)。


## riverpodの設定
riverpodを使用する際、3つパターンがあるみたいです。

1. Flutterのみ使用場合 →　flutter_riverpodパッケージ
2. Dartのみ使用 → riverpodパッケージ
3. Flutter とflutter_hooksの場合 → flutter_hooks、hooks_riverpodパッケージ

Flutter使用者は、1 or 3を主に使用していきます。
riverpod開発者は3を主に使用しています。


今回は、1の場合、つまりhooksは使わない場合で行っていきます。
## Riverpodの使い方
### Providerの設定

providerでは上の方でwidgetをラップしていましたが、riverpodの場合はどうやってProviderを設定するのでしょうか、下のコードを見てみましょう。

```dart:lib/controller/nwes_controller.dart

//グローバル変数でProviderを設定。
final newsProvider = StateNotifierProvider((ref) => NewsController());

@freezed
abstract class NewsState with _$NewsState {
  factory NewsState({
    bool loading,
    List<Article> articles,
  }) = _NewsState;
}


class NewsController extends StateNotifier<NewsState> {

 
  NewsQueryService _newsQueryService;

  NewsController(this._newsQueryService) : super(NewsState(loading: false));


  Future<void> getNews() async {
    List<Article> articles = [];
    articles = await _newsQueryService.getNews();
    state = state.copyWith(articles: articles);
  }


}

```
以前は、ProviderをWidgetのなるべく上にラップして置いていましたが、riverpodではその必要がなくなり、
final newsProvider = StateNotifierProvider((ref) => NewsController());
と書くだけでProviderの設定になります。(今回はStateNotifierを使用するため、StateNotifierProvider使用。)

### Providerの読み方。
Providerの設定が出来たので、widget内でProviderを読んでみましょう。
以前は、
Provider.of<NwesController>(context, listen :false).getNews();
で、ロジックやstateを読み込んでいました。
Riverpodではどうなのか下のコードを見てみましょう。

```dart:lib/controller/nwes_controller.dart

class MainPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: (){
          context.read(newsProvider).getNews();
        },
      ),
      appBar: AppBar(
        centerTitle: true,
        title: Text("Demo"),
      ),
      body: HeadPage()
    );
  }
}

class HeadPage extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    //controllerのメソッドを読む場合は、context.read(Provider名).メソッド名と書いて呼び出す。
    final _fetch = context.watch(newsProvider).getNews();
    // TODO: implement build
    return FutureBuilder<void>(
      future: _fetch,
      builder: (context, snapshot) {
        if (snapshot.connectionState != ConnectionState.done) {
          return Center(child: CircularProgressIndicator());
        }
        if (snapshot.hasError) {
          print("エラーが出たよ");
        }
        return HeadNewsListPage();
      },
    );
  }

}

//viewの部分表示
class HeadNewsListPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(top: 10),
      child: Container(
        margin: EdgeInsets.symmetric(horizontal: 10),
        //Hooksを使わない場合はConsumerでProviderを使用する。
        child: Consumer(
          builder:(context, watch, child){
            //stateを呼び出す場合は、watchで監視し(変更を監視)、下のように書くと、NwesState内のstateを取得可能。
            final state = watch(newsProvider.state);
            return ListView.builder(
              itemCount: state.articles.length,
              itemBuilder: (context, index) {
                return ArticleTile(state.articles[index]);
              },
            );
          }
        ),
      ),
    );
  }
}


}
```
このように、providerを読みだし、widget内で使うことが出来ます。

また、hoksを使用した書き方は、useProviderを使ってよビアスことが出来ます。
ちなみにこのuseProviderはonPressed内では使えないので、上記のcontext.readを使用しましょう。

### 終わりに
riverpod使ってみた感想は、非常に使いやすいです。
こんご、自分の開発は積極的にriverpodを使用していこうと思うので、hooksを使用した場合などまた記事にできればと思います。

ちなみに、NwesControllerは引数で、NwesQueryServiceもってきているのですが、引数でのもってきかたが下のコードになります。

```dart:
final newsQueryService = Provider((ref) => NewsQueryService());
final newsProvider = StateNotifierProvider((ref) => NewsController(ref.watch(newsQueryService)));

```
参考になればと思います!!