状態管理する方法は色々ありますが、UiStateという状態を一つのclassでまとめて管理する方法を紹介します。
(UiStateの他にはUiModel, ViewStateなど色んな呼び方を見かけます)
UiStateでも大きく2つやり方があるかなと思っています。sealed class
を使ったやり方と data class
を使ったやり方です。
sealed class を使った管理
sealed class
を使って画面が取り得る状態を定義して、必要なデータは各継承クラスのプロパティに持たせる方法です。
以下のコメントに書いてる通り、各状態を sealed class
を継承した data class
や object
で表現しています。
ViewModelではこれを StateFlow
や LiveData
を使って状態ごとに更新していきます。
Activity/Fragment ではこの変更を受け取れるように実装していきます。
このやり方はAndroid Developerのドキュメントでもやっていたりします。
https://developer.android.com/kotlin/flow/stateflow-and-sharedflow
問題点
ぼく個人としてはこのやり方はいくつか問題点があり、あまりやりたくはない方法になります。
上の例で示したとおり、画面全体の状態を表しているので、ちょっと複雑な画面などすぐに破綻します。
例えば、画面にお気に入りボタンがあったりした場合などは、 sealed class
の継承するものがどんどん増えていきます。
画面の更新についても、画面全体の状態と表すのでUIを更新する際に冗長になり、またミスが増え要因になるように思えます。
以下に例を示しますが、画面全体の状態ごとにすべてのUIの状態を設定する必要があります。
また、DataBindingをシンプルに使用することができません。
data class を使った状態管理
次に単純な data class
を使って管理する方法です。
sealed class
のように画面全体の表現するのではなく、UIを表現するのに必要なものをプロパティとして定義するイメージです。
ViewModelは以下のように StateFlow
や LiveData
を使いつつ copy
メソッドで変更したいプロパティのみを更新していきます。
value
を参照して更新する箇所が微妙な感じですが、これは以下のような拡張関数を作っておくことで多少シンプルになります。
Activity/Fragment側は以下のようになります。
分岐もなくUiStateの状態を素直にUIに反映するだけです。この例ではViewBindingを使っていますが、DataBindingでも問題なく使用できます。
画面のUIが増えたとしても変更は容易かと思います。
状態の組み合わせでUIの状態を表したいときなどは、フィールド等を追加すればActivity/Fragmentで分岐したり、DataBinding内でいくつも条件を記述する必要もないです。
問題点
data class
を使った状態管理ではそこまで大きな問題はないと思ってますが、インスタンスを何度も生成するのでパフォーマンスが気になるかもしれません。
ぼくが触ってる感じでは大きくパフォーマンスが変化してるふうには見えないのでそこまで問題にはならないかなと思いますが、一応頭にいれておくと良いかもです。
Composeの場合
Composeの状態管理ですが、ぼく自身まだ明確な回答を持ってるわけではないです。上で紹介した sealed class
や data class
も collectAsState
などを使えばそのまま使えたりしますが、もっと良い方法があるかもしれません。
Composeについてはこれから色々試していきます。