― Boolean が増えた瞬間、UI は壊れ始める ―
※ChatGPTを使用して記事を作成しています。
はじめに
Jetpack Compose を使っていると、最初はこう思います。
「状態を持たせれば UI は書ける」
そして気づくと、ViewModel にこんな State が生まれます。
data class UiState(
val isLoading: Boolean = false,
val isError: Boolean = false,
val isEmpty: Boolean = false,
val isSuccess: Boolean = false
)
一見すると分かりやすそうです。
しかしこれは、UI State 破綻の第一歩です。
この記事では、
- UI State が破綻する理由
- 正規化されていない State の危険性
- Compose での正しい State 設計
を 失敗例 → 問題点 → 修正版 の順で解説します。
UI State が破綻する瞬間
上記の State には、致命的な問題があります。
問題①
組み合わせとして成立しない状態が存在する
- isLoading = true
- isError = true
→ ロード中でエラーとは?
- isEmpty = true
- isSuccess = true
→ 空なのに成功?
👉 コード上は可能だが、UIとして意味がない
問題②
「どれを表示すればいいか」が UI 側に漏れる
when {
state.isLoading -> Loading()
state.isError -> Error()
state.isEmpty -> Empty()
state.isSuccess -> Content()
}
- 優先順位は?
- 同時に true だったら?
- 誰が責任を持つ?
👉 UIが状態解釈を始めた時点で設計は崩壊
なぜ Boolean が増えがちなのか
理由は単純です。
- 最初は1画面1状態
- 要件追加で1フラグ追加
- 「念のため」false にしておく
- 修正漏れ
👉 人間は組み合わせを管理できない
これは Compose の問題ではなく、状態設計の問題です。
正規化とは何か?
正規化とは、
「同時に成立しない状態を、構造で表現すること」
つまり、
- 不正な状態を作れない
- 解釈の余地がない
- UI が迷わない
正解①:sealed class による正規化
✅ 正規化された UI State
sealed class UiState {
object Loading : UiState()
object Empty : UiState()
data class Success(val items: List<Item>) : UiState()
data class Error(val message: String) : UiState()
}
UI 側
when (state) {
UiState.Loading -> Loading()
UiState.Empty -> Empty()
is UiState.Success -> Content(state.items)
is UiState.Error -> Error(state.message)
}
✔ 同時成立不可
✔ UI の分岐が明確
✔ 意味のない状態が存在しない
正規化の副作用:State が減る
Boolean 地獄から抜けると、
- 状態数が減る
- UI 分岐が減る
- バグが減る
👉 コードは長くなるが、設計はシンプル
よくある誤解
「柔軟性が下がるのでは?」
答えは NO です。
例えば「リフレッシュ中」を追加する場合。
❌ Boolean 方式
val isRefreshing: Boolean
→ 全組み合わせを再検討
✅ 正規化方式
sealed class UiState {
object Loading
object Refreshing
...
}
→ 新しい状態を1つ追加するだけ
データと状態は分ける
UI State に 生データを詰め込みすぎる のも破綻の元です。
❌ 失敗例
data class UiState(
val items: List<Item>?,
val error: Throwable?,
val isLoading: Boolean
)
- null 判定地獄
- if の嵐
✅ 正しい分離
sealed class UiState {
object Loading
object Empty
data class Content(val items: List<Item>)
data class Error(val message: String)
}
👉 null を状態にしない
State 正規化の設計チェックリスト
次の質問に YES が多いなら危険です。
- Boolean が3つ以上ある
- null を意味として使っている
- UI 側に when/if が散らばっている
- ViewModel 以外で状態解釈している
正規化されていると Compose が活きる理由
Compose は、
- 入力が明確
- 出力が一意
- 差分判定しやすい
という前提で設計されています。
👉 正規化された State は Compose と相性が良い。
設計の本質
UI State 設計のゴールはこれです。
「今この画面は、どんな状態か?」を
1文で説明できること
それができない State は、ほぼ確実に破綻します。
まとめ
- Boolean が増えたら危険信号
- 同時成立しない状態は構造で表現
- 正規化は「制限」ではなく「安全装置」
- UI が迷わない State を作る
UI State を正規化できるようになると、
- Compose が楽になる
- バグが減る
- 設計の説明ができる

