Flutterで開発するならRiverpod使おうZE! -Riverpodの簡単な使い方-

Flutterで開発をしている皆さんこんにちは，今回はRiverpodの使い方を紹介してきます．
私はちょっと特殊な経歴で，React.jsを4ヶ月くらい勉強してから，Flutterに鞍替えした人間です．
そんな僕からすると「なんでRiverpod使わないの？」って思うくらい使いやすいです．

まず皆さんに言いたいのが，**FlutterはどんどんReactに近づいている**ということ．
特にDartパッケージのProviderが登場したあたりから，Reactに近づいている印象です．
state managementが非常にReactに似ていると思います．

さらにFlutter hooksというパッケージが出てきたので，さらにReactに近づくことになりました．
というよりFlutter hooksの最初の説明にReactのhooksの真似ですと書かれています．

今回はflutter_hooksとhooks_riverpodを使ったstate manegementのやり方を解説します．

# ステップ１：インストール
今回はflutter_hooksという別のパッケージを使って，hooks_riverpodという状態管理用のパッケージを動かすイメージです．
つまり2つのパッケージをインストールしなければいけません．

以下のようにpubspec.yamlを編集します．
これは，この記事を書いた当時のバージョンなので実際のバージョンを知るために [こちら](https://riverpod.dev/docs/getting_started)

```yaml:pabspec.yaml
dependencies:
  flutter:
    sdk: flutter
  flutter_hooks: ^0.17.0
  hooks_riverpod: ^0.14.0+4
```

# ステップ2：下準備

Riverpodを使うためには，main.dartを少し改良しなければいけません．
具体的には以下の通りです．

```dart:main.dart
void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}
```

上のようにrunApp()の中にProviderScopeを追加します．

# ステップ3：stateを定義する
やっとstateを定義するところまで来ました．
stateを定義すれば，それ以降stateを定義したファイルをimportすれば，どこでもstateを読み取り，書き取りすることができます．
実際に定義の仕方をお見せしましょう．

```dart:providers.dart
final helloWorldProvider = Provider((_) => 'Hello world');
```

今回は`Provider`の名前が`helloWorldProvider`になります．値を呼び出すときはこの名前を使用します．
また`Provider`の初期値，つまり初期のstateが`Hello world`となります．他の状態管理と同様にこの値は後から変更することができます．

ここで注目してほしいのが，providers.dartというファイルで定義されているということ．
私のおすすめはmain.dartなどで使う場合でも，このコードはstate定義用の専用ファイルを作成しておくのがおすすめです．
stateが何個あろうと，state定義用ファイルを一つだけimportするだけで，全てのstateの読み書きができるので便利です．

実はProvider以外にもstateを定義する方法があるのですが，今回は私がよく使うものを一つだけ教えます．
それは`StateProvder`です．

```dart:providers.dart
final helloWorldProvider = StateProvider((_) => 'Hello world');
```

このように`Provider`と同じように使えますが，`StateProvider`の方が値を読み取ることに優れています．
値の読み取り方については別の章で教えます．

# ステップ4：値を読み取る・変更する
まずは値の読み取り方を学びましょう．
実はRiverpodでは値を変更するのは，値を読み取ることの応用となっています．
早速，具体例から学んでいきましょう．

## 値を読み取る

```dart:main.dart
final counterProvider = StateProvider((ref) => 0);

class Count extends HookWidget {
  @override
  Widget build(BuildContext context) {
    int count = useProvider(counterProvider).state;

    return Text('$count');
  }
}
```

ここで重要なのは，通常classを作成する場合は`StatelessWidget`を使用しますが，今回はflutter_hooksというパッケージを導入しているため，`HookWidget`という特殊なWidgetを継承できます．
ここで`HookWidget`に関して説明すると，`HookWidget`は`StatelessWidget`を内包しているので，`StatelessWidget`でできることは全てできます．
`HookWidget`ではそれプラスhooksというものが使えます．

そのhooksの一つが，`useProvider`です．上のようにして`count`に`Provider`の中に入っている値を代入することができます．
この値は`Provider`の中の値が変更された時も，しっかりと反映されますのでご安心ください．

ここで注意しなければならないのが，`useProvider(counterProvider).state`というコードです．
`.state`という書き方ができるのは，`StateProvider`で定義した場合のみです．
普通の`Provider`で定義した場合は，`.state`と書いてもエラーが出るだけです．

### ちょっと一息
最近のコーディングでは，めんどくさいので`StatelessWidget`を一回も継承していません．
理由はhooksを使うかどうかは，classを作成している段階ではわからない時もあります．しかしhooksを使おうと使うまいと`HookWidget`を継承しておけば事足りるので，`StatelessWidget`を使う必要はないのです．
むしろclassを作成するたびに，hooksを使うかどうかを考えて，`StatelessWidget`と`HookWidget`のどちらを使うか考えていたら，あっという間に時間は過ぎていきます．

## 値を変更する
ここからは少し応用編になります．
Riverpodでは値の読み取り方は様々です．
先ほどはその中で最も簡単な方法をご紹介しました．

値を変更する場合はもう一つの値の読み取り方を応用します．
具体例を見ていきましょう．
カウンターの場合

```dart:main.dart
final counterProvider = StateProvider((ref) => 0);

@override
Widget build(BuildContext context) {
  return RaisedButton(
    onPressed: () => context.read(counterProvider).state++,
    child: Text('increment'),
  );
}
```

文字列の場合

```dart:main.dart
final textProvider = StateProvider((ref) => 0);

@override
Widget build(BuildContext context) {
  return ElevatedButton(
    onPressed: () {
      context.read(textProvider).state = 'Hello hello everybody^_^';
    },
    child: Text('increment'),
  );
}
```

このように書くことができます．
基本は以上です．また今度応用編も書くかもしれません．