はじめに
この記事では、RiverpodとFlutter Hooksを使う、はじめの一歩として、
カウンターアプリを作ってみようと思います。
今回作成したコードはこちらのリポジトリに公開しています。
https://github.com/karamage/riverpod_flutterhooks_example
Flutterの状態管理パターン
みなさん、Flutterの状態管理パターンは、何を使っていますでしょうか?
Flutterの状態管理パターンはいっぱいあります。
大すぎですw
- 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の発言をしているのをよく拝見します。
最も取っ付きやすそうな例として、Riverpod(hooks非使用) + ChangeNotifierProviderのカウンターページ書いてみた( ´・‿・`)https://t.co/PyZlVky4zo pic.twitter.com/PS8gYI8hrt
— mono (@_mono) July 4, 2020
ProviderとRiverpodの扱いの感覚の違い、いつか記事書くかもしれないけど、とりあえずライトにツイート( ´・‿・`)
— mono (@_mono) July 3, 2020
(特に2枚目が大事) pic.twitter.com/7ucPUdhdc6
そこで、私もいっちょやってみようかなと思いました。
ざざっと、RiverpodとFlutterHooksを使ってカウンターアプリを作ってみましたので、当記事にまとめます。
Riverpodとは
Providerの作者による、Providerの進化版です。
Provider, but different
Riverpod公式ページ
https://riverpod.dev
「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 とは
「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インストール
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
これでインストールは完了です。
それでは、コードを書いていきましょう。
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.
),
);
}
}
めちゃくちゃコードがすっきり書けて感動。
実行して、「+」を押すと数字がカウントアップすると思います。
コードの説明
グローバルにProviderを宣言することができる
final counterProvider = StateNotifierProvider((_) => Counter());
ProviderだとWidget Treeのrootのほうで、Providerを作成していたと思います。
Riverpodでは、グローバルに、Widget Treeに縛られることなく、Providerの作成が可能です。
Providerにスコープを設定できる
ProviderScope(
child: CounterApp(),
),
ProviderScopeを使えば、Providerにアクセスできる階層をコントロールすることができます。
Hooksをつかうためには、HookWidgetを継承する
class CounterApp extends HookWidget {
将来的にはclassではなく、「React Hooks」と同じように関数で書けるようになるといいですね。
そのためにもFlutte公式がHooksを認めてもらえるといいなと思います。
状態を読み込むには、useProviderを使う
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