🤹

【Flutter】色んな状態管理手法でカウンターアプリ作ってみる

2022/02/11に公開

Flutterの状態管理手法って沢山ありますよね

ネットで「flutter 状態管理」で検索するとやれProviderだのやれBlocだのRiverpodだのと様々なライブラリを使ったパターンが出てきます

しかも人によって同じ状態管理手法と言ってる割には書き方がだいぶ違ったり、1つの状態管理手法を違う名前で言っていたりとなんやねーん!と混乱してしまう記事が多いなと感じました

仕事でFlutterを使い始めて1年半、Providerを使ってきましたが、最近Riverpodの名前もよく聞く様になったし、ちょっと触ってみようかなと思った矢先、先の検索結果の迷宮に迷い込みました

折角なのでこの機会にRiverpodだけでなく主要な手法を一通り触って、その特徴や違いを明確にし、状態管理手法の全体像を掴んでみようと思いました

アプローチ

このプロジェクトではシンプルなカウンターアプリを、「Flutter界隈でよく出てくる状態管理手法」として独断と偏見で選んだ以下のパッケージ・パターンを用いて書き直してみました

詳細についてはそれぞれの記事に整理させてもらいました

  1. Provider
  2. Bloc
  3. Redux
  4. RxDart
  5. Scoped Model
  6. MobX
  7. GetX
  8. State Notifier

もっとマイナーな手法を探せばまだまだありますが、これらを網羅しておけば大体の全体像は掴めるかなと思います

あれ?

あれ?さっきあんなに言ってたRiverpod入ってないじゃん!と思った方、そうです、それこそ自分がこの試みを通して学んだ事の1つでした。

そんな訳で現在は主流ではない手法も含めて複数の状態管理手法を触ることで見えてきたものがいくつかありました

このプロジェクトを通して見えた事

1. ProviderとRiverpodは状態管理手法ではないのでは?

実はProviderパッケージとRiverpodパッケージは直接的には状態管理用のライブラリではありません。厳密には依存関係を注入する為のライブラリです。

Providerはwidgetツリーに沿って依存関係を下位のクラスに流し込み、Riverpodはwidgetツリーに縛られる事なくグローバル変数としてツリーのどこにあるwidgetにも依存関係を注入する事が出来るパッケージです

状態管理の核となる状態管理クラス自体は他のパッケージのモノを使い生成する必要があります。

散々「flutter 状態管理」で調べるとProviderRiverpodの名前出てくるのに!と自分も思いました

またこれが「Provider」「Riverpod」と銘打った記事でも書き方に違いがある原因でもありました

例えばRiverpodを解説した記事ではStateNotifierを一緒に使う例が殆どでStateNotifierでないといけないような印象を受けますが、実はそんな事はなくRiverpodChangeNotifierに対応した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

参考

4. 状態管理手法の変遷

その他、リリース順に並べてみることでどういった方向に状態管理手法のトレンドが動いているのかなんとなく掴む事も出来ました

ライブラリを初回リリース時期の順に並べると以下の通りとなります。

  1. Rxdart 2015/10
  2. Redux 2017/08
  3. Scoped Model 2017/08
  4. Bloc 2018/10
  5. Provider 2018/10
  6. Mobx 2019/01
  7. GetX 2019/11
  8. 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などデータクラスについてのサンプルもアップデートしていこうと思っています。

flutter_state_management101

参考

Discussion