【Flutter】色んな状態管理手法でカウンターアプリ作ってみる
Flutterの状態管理手法って沢山ありますよね
ネットで「flutter 状態管理」で検索するとやれProviderだのやれBlocだのRiverpodだのと様々なライブラリを使ったパターンが出てきます
しかも人によって同じ状態管理手法と言ってる割には書き方がだいぶ違ったり、1つの状態管理手法を違う名前で言っていたりとなんやねーん!と混乱してしまう記事が多いなと感じました
仕事でFlutterを使い始めて1年半、Provider
を使ってきましたが、最近Riverpod
の名前もよく聞く様になったし、ちょっと触ってみようかなと思った矢先、先の検索結果の迷宮に迷い込みました
折角なのでこの機会にRiverpod
だけでなく主要な手法を一通り触って、その特徴や違いを明確にし、状態管理手法の全体像を掴んでみようと思いました
アプローチ
このプロジェクトではシンプルなカウンターアプリを、「Flutter界隈でよく出てくる状態管理手法」として独断と偏見で選んだ以下のパッケージ・パターンを用いて書き直してみました
詳細についてはそれぞれの記事に整理させてもらいました
もっとマイナーな手法を探せばまだまだありますが、これらを網羅しておけば大体の全体像は掴めるかなと思います
あれ?
あれ?さっきあんなに言ってたRiverpod
入ってないじゃん!と思った方、そうです、それこそ自分がこの試みを通して学んだ事の1つでした。
そんな訳で現在は主流ではない手法も含めて複数の状態管理手法を触ることで見えてきたものがいくつかありました
このプロジェクトを通して見えた事
1. ProviderとRiverpodは状態管理手法ではないのでは?
実はProvider
パッケージとRiverpod
パッケージは直接的には状態管理用のライブラリではありません。厳密には依存関係を注入する為のライブラリです。
Provider
はwidgetツリーに沿って依存関係を下位のクラスに流し込み、Riverpod
はwidgetツリーに縛られる事なくグローバル変数としてツリーのどこにあるwidgetにも依存関係を注入する事が出来るパッケージです
状態管理の核となる状態管理クラス自体は他のパッケージのモノを使い生成する必要があります。
散々「flutter 状態管理」で調べるとProvider
とRiverpod
の名前出てくるのに!と自分も思いました
またこれが「Provider」「Riverpod」と銘打った記事でも書き方に違いがある原因でもありました
例えばRiverpod
を解説した記事ではStateNotifier
を一緒に使う例が殆どでStateNotifier
でないといけないような印象を受けますが、実はそんな事はなくRiverpod
はChangeNotifier
に対応したChangeNotifierProvider
も実装しています
ではなぜProvider
を状態管理手法として挙げているのかというと、これはchangeNotifier
x Provider
= Provider
パターンとして定着している為です
ただGoogleが紹介している状態管理手法の中にRiverpodが含まれている事もあり、ここの切り分けについては色々と議論がありそうです。
とはいえ、管理手法としてのProvider
とパッケージとしてのProvider
が混同されている事により混乱する人は私だけじゃないのではと思い、切り分けさせてもらいました
2. Providerは多くの状態管理手法で取り入れられている
またFlutterの状態管理を調べていく時にこんがらがるのが、Provider
が様々な状態管理手法で取り入れられている為でした
今回紹介する状態管理手法の中でも、BlocにはBlocProvider
,ReduxにはStoreProvider
、StateNotifierにはStateNotifierProvider
、MobXとRxDartもProvider
を用いて依存関係の注入を行なっています
Provider
があくまでも依存関係の注入を行うライブラリである事を理解していれば、不思議なことはありませんが、Provider
って状態管理手法の1つでしょ?Providerとそれ以外の手法の線引きってどうなってるの?と最初混乱しました
3. MutableとImmutable、2つの重要なパラダイム
状態管理手法を選定する際にトピックとして上がるのが、Mutableな状態オブジェクトを扱うかImmutableな状態オブジェクトを扱うかです
状態管理をする際に現在の状態を表す変数(状態変数)を管理する事になりますが、このオブジェクトが更新可能なオブジェクトにするのか(Mutable)、それとも更新の度に新しいインスタンスを生成し差し替えるのか(Immutable)の違いになります
Mutableな状態オブジェクトの管理では変更したいフィールドのみに変更を加える為、直感的で分かりやすいですが、アプリケーションの様々な場所から参照され、また変更をしている場合、変更が意図しないタイミングで行われてしまったり、意図しない変更が加わってしまったり、副作用があります
状態オブジェクトをImmutableにする事で、参照しているオブジェクトの状態を一意にする事でそれらの副作用の発生を防ぐ事ができます。ただその代わり、状態変更の度にインスタンスを生成する必要がある事や、dartの==オペレーターでは値比較が出来ない為、ライブラリを使う必要がある等、準備が大変な一面もあります
今回試した手法では以下のように分かれます
Mutable
- StatefulWidget
- ChangeNotifier x Provider
- Scoped Model
- GetX
Immutable
- BLoC(Cubit)
- StateNotifier x Provider
- Redux
- RxDart
- MobX x Provider
参考
- https://medium.com/flutter-jp/immutable-d23bae5c29f8
- https://qiita.com/k3ntar0/items/f14b0b03fbc13d0ed925
- https://dart.academy/immutable-data-patterns-in-dart-and-flutter/
4. 状態管理手法の変遷
その他、リリース順に並べてみることでどういった方向に状態管理手法のトレンドが動いているのかなんとなく掴む事も出来ました
ライブラリを初回リリース時期の順に並べると以下の通りとなります。
-
Rxdart
2015/10 -
Redux
2017/08 -
Scoped Model
2017/08 -
Bloc
2018/10 -
Provider
2018/10 -
Mobx
2019/01 -
GetX
2019/11 -
StateNotifier
2020/03
Flutterは元々Reactを参考に作られているだけあって、初期はReactで使われていたReduxやReactiveプログラミングのライブラリであるRxdart、Blocなどの開発から始まり、その後、Flutterのwidgetツリー構造を利用したProviderへ。そしてWidgetツリー構造を利用したProviderが成熟して来たところで、今度はツリー構造から解放されたGetXやStateNotifier(x Riverpod)が注目を集めてきたという流れになりそうです。
BlocはGoogle自ら開発した状態管理手法でありながら、2019年に正式にGoogleがProviderを推奨している事から、FlutterにおいてはStreamをベースとしたReactiveな状態管理から離れていってる様な印象を受けます。が、手法の良し悪しというよりblocの方がえてしてProviderよりも記述量が多くなりがちだったり、学習コストが高いという理由からProviderが推奨される様になった様です。
最後に
こちらの記事に書かれている内容や理解は私見であり、多分に間違いや理解が足りていない部分もあるかと思います。もしそういった点見つけられた方は生温かく見守って頂きつつ、赤子を諭すようにコメント頂ければありがたいですm(_ _)m
また全てのコードは以下のGithubレポジトリにまとめているのでご興味のある方は覗いてみてください。RiverpodなどDI関連のパッケージやfreezedなどデータクラスについてのサンプルもアップデートしていこうと思っています。
Discussion