RiverpodとFlutter Hooksを使う、はじめの一歩

## はじめに

この記事では、**RiverpodとFlutter Hooks**を使う、**はじめの一歩**として、
カウンターアプリを作ってみようと思います。

今回作成したコードはこちらのリポジトリに公開しています。
https://github.com/karamage/riverpod_flutterhooks_example

<img width="535" alt="スクリーンショット 2020-08-01 14.34.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93703/5e2dbe83-7fbe-9e6f-a571-a011e875e220.png">



### Flutterの状態管理パターン

みなさん、**Flutterの状態管理パターン**は、何を使っていますでしょうか？

Flutterの状態管理パターンはいっぱいあります。
**大すぎ**ですｗ

* StatefulWidget
* Redux
* BLoC(Stream/InheritedWidget) + Provider
* ChangeNotifier + Provider
* StateNotifier + freezed + Provider
* StateNotifier + Flutter Hooks + Riverpod  **【new】**

私は、これまでは**「StateNotifier + freezed + Provider」**を主に使っていました。

「StateNotifier + freezed + Provider」の使い方については、以下に記事を書きました。
https://qiita.com/karamage/items/4b1aff984b1af7541b73

### 新たなパターン登場

そんな今日この頃ですが、状態管理パターンに**キラボシのごとく輝く新星**が現れました。

**「RiverpodとFlutter Hooks」**です。

最近、Flutter界隈では、RiverpodとFlutter Hooksが話題です。

@mono0926 さんがツイッターで、Riverpodの発言をしているのをよく拝見します。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">最も取っ付きやすそうな例として、Riverpod(hooks非使用) + ChangeNotifierProviderのカウンターページ書いてみた(　´･‿･｀)<a href="https://t.co/PyZlVky4zo">https://t.co/PyZlVky4zo</a> <a href="https://t.co/PS8gYI8hrt">pic.twitter.com/PS8gYI8hrt</a></p>&mdash; mono (@_mono) <a href="https://twitter.com/_mono/status/1279379628910505984?ref_src=twsrc%5Etfw">July 4, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ProviderとRiverpodの扱いの感覚の違い、いつか記事書くかもしれないけど、とりあえずライトにツイート(　´･‿･｀)<br>(特に2枚目が大事) <a href="https://t.co/7ucPUdhdc6">pic.twitter.com/7ucPUdhdc6</a></p>&mdash; mono (@_mono) <a href="https://twitter.com/_mono/status/1278889029585403905?ref_src=twsrc%5Etfw">July 3, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

そこで、私も**いっちょやってみようかな**と思いました。

ざざっと、RiverpodとFlutterHooksを使ってカウンターアプリを作ってみましたので、当記事にまとめます。

## Riverpodとは

Providerの作者による、**Providerの進化版**です。

**Provider, but different**

Riverpod公式ページ
https://riverpod.dev
<img width="1157" alt="スクリーンショット 2020-08-01 14.36.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93703/36f6a392-020d-19d3-3bcf-bafbccf3a83b.png">

「Providerってなに？」って方は、以下のページが詳しいです。

FlutterのProviderパッケージを使いこなす
https://itome.team/blog/2019/12/flutter-advent-calendar-day7/

もともと使いやすかった「Provider」を**さらに強化したのが「Riverpod」**なのですね！

### Riverpodの良いところ

- Flutterに依存しなくなった(**Pure Dart**)
  - ProviderがInheritedWidgetをラップしたものだった。
  - Riverpodは**InheritedWidgetを使わずゼロから**構築した。
  - Providerから状態を読み込む際、**BuildContextが必要なくなった。**

- **コンパイル時にエラーを検知**できる
  - Providerでは実行時にしかエラーが検知できなかった。
  - よくあったのが、Providerから状態を読み出そうとして、「Providerが見つからない」エラー

- 同じ型のProviderを**複数**配信できる
- Providerを**Private**にできる
- ProviderをWidget Tree限定でなく、**Model/Domain Logic/Repositoryどこでも**利用できる
- **グローバル**変数としてProviderを宣言できるようになった
- 使われていないProviderの状態を**dispose**できる
- **Computed**で、Providerの状態を使って計算した値を状態として持てる。   
- 他にも、Family、AutoDisposeProviderなどの新機能多数

- **Hooks的な使い方**も可能(ゆえにHooks APIとの相性がよい)
  - Hooksなしでも使える

※ただし、Flutter公式はHooksを肯定的には捉えていない。今後の動向に注目。

### Riverpodの悪いところ

- まだまだ**開発段階**。
- **Hooks推し**がどうなるか**不明**。（Flutter公式は**否定的**）
- これから**破壊的な変更**が入りそう
- プロダクトに組み込むには**まだ早い**感じ
  - **stable**されるまで待とう。(将来的には、Flutter標準に取り込まれるのを願う)

## Flutter Hooks とは

https://github.com/rrousselGit/flutter_hooks

**「React Hooks」のFlutter版**みたいなイメージです。

参考：5分でわかるReact Hooks
https://qiita.com/Mitsuzara/items/98d1bc4a83265a764084

https://riverpod.dev
Riverpodの公式ページを見る限り、**RiverpodとFlutter Hooksはセットで使う**のが、作者の推しみたいです。
Riverpodは**Flutter Hooksを外しても利用できます。**

## カウンターアプリを作ってみよう

### Riverpod と Flutter Hooks と StateNotifierインストール

```yaml:pubspec.yaml
environment:
  sdk: ">=2.7.0 <3.0.0"
  flutter: ">= 1.17.0"

dependencies:
  flutter:
    sdk: flutter
  flutter_hooks: ^0.11.0
  hooks_riverpod: ^0.5.1
  state_notifier: ^0.5.0
```

pubspec.yamlに上記のように書いて、ターミナルで「flutter pub get」しましょう。

```
flutter pub get
```

これでインストールは完了です。

それでは、コードを書いていきましょう。

```dart:lib/main.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:state_notifier/state_notifier.dart';

final counterProvider = StateNotifierProvider((_) => Counter());

class Counter extends StateNotifier<int> {
  Counter() : super(0);
  void increment() => state++;
}

void main() {
  runApp(
    ProviderScope(
      child: CounterApp(),
    ),
  );
}

// Note: CounterApp is a HookWidget, from flutter_hooks.
class CounterApp extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final state = useProvider(counterProvider.state);
    final counter = useProvider(counterProvider);

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('CounterApp')),
        body: Center(
          child: Text(state.toString()),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed:() => counter.increment(),
          child: Icon(Icons.add),
        ), // This trailing comma makes auto-formatting nicer for build methods.
      ),
    );
  }
}
```

めちゃくちゃ**コードがすっきり**書けて感動。

実行して、「＋」を押すと数字がカウントアップすると思います。
<img width="535" alt="スクリーンショット 2020-08-01 14.34.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93703/be8a868c-6900-6ef4-1be9-9cac4a15460a.png">

## コードの説明

### グローバルにProviderを宣言することができる

```dart
final counterProvider = StateNotifierProvider((_) => Counter());
```
ProviderだとWidget Treeのrootのほうで、Providerを作成していたと思います。
Riverpodでは、グローバルに、**Widget Treeに縛られることなく**、Providerの作成が可能です。

### Providerにスコープを設定できる

```dart
    ProviderScope(
      child: CounterApp(),
    ),
```
ProviderScopeを使えば、**Providerにアクセスできる階層をコントロール**することができます。

### Hooksをつかうためには、HookWidgetを継承する

```dart
class CounterApp extends HookWidget {
```

将来的にはclassではなく、「React Hooks」と同じように**関数で書けるようになるといい**ですね。
そのためにもFlutte公式がHooksを認めてもらえるといいなと思います。

### 状態を読み込むには、useProviderを使う

```dart
final state = useProvider(counterProvider.state);
```
めっちゃ簡単でシンプルですね。
**BuildContextが必要なくなった**のが良い！


## まとめ

- **Riverpodめっちゃ良い**。Providerから正当進化している。使わない理由がない。
- 今後は、Providerではなく、Riverpodを使っていきたい。
- しかしながら、**Hooksがどうなるかわからない**ので、もうしばらくは**様子見**かなぁ。
- Hooksを外して使うのが良いかも。
- まずは個人アプリで採用してみようと思います。

最後までお読みいただき、ありがとうございました。

今回作成したコードはこちらのリポジトリに公開しています。
https://github.com/karamage/riverpod_flutterhooks_example

## 参考リンク

- Riverpod
https://riverpod.dev

- Flutter Hooks
https://github.com/rrousselGit/flutter_hooks

- Flutter Hooks, say goodbye to StatefulWidget and reduce boilerplate code.
https://medium.com/flutter-community/flutter-hooks-say-goodbye-to-statefulwidget-and-reduce-boilerplate-code-8573d4720f9a

- example counter
https://github.com/rrousselGit/river_pod/tree/master/examples/counter

- example todo
https://github.com/rrousselGit/river_pod/tree/master/examples/todos