ChangeNotifierというのがFlutterにはあり、ViewModelで継承して使っている。 StateNotifierとよくパフォーマンス面で比較されるValueNotifierというのもあり、そっちは1個のValueしか扱えないがChangeNotifierは複数扱える。なのでValueNotifierやStateNotifierのように新たに管理する値を増やすときにクラスを定義する必要がなく楽ではある。
結論
ChangeNotifierにあまりポジティブな印象がない。 新たに設計し直すなら後述の方法か違う方法をとるとおもう。
なぜ
値を変更したあとにnotifyを忘れる
以下の例だとisLoading を更新したあとにnotifyしないといけないのだけどうっかり忘れることがある。 また途中でreturnとかしているとうっかり忘れることがある。 するとbuildされず???となる。
class HogeViewModel extends ChangeNotifier { Future<void> load() async { isLoading = false; // 本当は変更したあとにnotifyする必要がある // 時間のかかる処理 isLoading = true; notifyListeners(); } }
Linterなどでどうにか出来るとよいけど、どうにか出来るかは知らない。
notifyするとすべての値が更新されたことになり影響範囲が大きくなる
ViewModel なので Viewに関するStateを持っておりそれが大きくなることがある。 またViewModel以外にもアプリ全体で値を管理するStoreクラスみたいなのを作っている。
そのStoreもChangeNotifierを継承している。例えば注文に関するStoreがあって、注文に関するStateをいれていくと多くの値を持つStoreが出来上がる。
Aという値とBという値があったとして、Aを更新したあとにnotifyした場合変更していないBも更新されたことになる。 なので、Aを更新したいだけなのにBが更新されても大丈夫か気にする必要がある。
これをどうにかするには、値をwatchする側で更新されたか確認してあげる必要があるのだが、ちゃんと更新されたか確認しているかどうかはwatchする側に依存するのでちゃんと更新されたか確認しているか確認する必要があるが、全て見るのは大変で大体見ない。
そもそもクラスをちゃんと分割できればいいのだがそううまくいかないのが現実だなと最近思っている。難しい。
今ならどうするか
RiverpodのStateProviderを使うのがよさそう
- 値を定義するのにクラスを作らなくてもよい
- 値を更新したあとのnotifyが不要
- static member で定義するので注文に関するProviders みたいにまとめられる
- StateProviderは値が1つしか持てない。複数持つにはfreezedなどでデータクラスを作るけどそれやるならselectはしなくていいと思う
まとめ
今日notifyしていないバグを見つけたので書いた。 ChangeNotifierにメリットはあるが大変なことのほうが多いなという感じです。
ちなみにChangeNotifierを採用したときはめっちゃ便利じゃんとか言ってた
— ひさいち (@hisaichi5518) 2021年5月25日