第16回:Compose Previewを設計に活かす方法

社員ブログ

― Previewは「確認ツール」ではなく「設計レビュー装置」 ―

※ChatGPTを使用して記事を作成しています。

Jetpack Compose を使っていると、ほぼ全員が Preview を使います。

  • UIの見た目確認
  • レイアウトの微調整
  • ダークモードの確認

しかし多くの場合、Previewはここで止まります。

「とりあえず表示できればOK」

これは非常にもったいない使い方です。

実は Preview は、

UI設計の正しさをチェックできる強力なツール

です。

この記事では、

  • Previewが設計に効く理由
  • Previewで見抜ける設計ミス
  • 実務で使えるPreview設計パターン

を解説します。

■ Previewはなぜ設計に効くのか

Composeの本質はこれでした。

UI = State の結果

そして前回の記事で扱った通り、

  • UIは状態とイベントだけを受け取る
  • ViewModelはUIから切り離す

この設計ができていると、どうなるか。

👉 PreviewでそのままUIが再現できる

逆に言うと、

PreviewできないUIは、設計が崩れている可能性が高い

■ 失敗例:PreviewできないComposable

@Composable
fun UserProfile(viewModel: UserViewModel = viewModel()) {
    val user by viewModel.user.collectAsState()
    Text(user.name)
}

このコードはPreviewできません。

なぜなら:

  • ViewModelが必要
  • Flowが必要
  • ライフサイクルが必要

つまり、

UI単体で成立していない

■ PreviewできるUIの条件

PreviewできるUIは必ずこの形になります。

@Composable
fun UserProfile(
state: UserProfileUiState,
onClick: () -> Unit
)

つまり、

  • 状態は引数
  • イベントはコールバック

です。

この時点で、

  • 再利用可能
  • テスト可能
  • Preview可能

という理想状態になります。

■ 基本のPreview

@Preview
@Composable
fun PreviewUserProfile() {
UserProfile(
state = UserProfileUiState(
name = "Taro",
isFollowing = false
),
onClick = {}
)
}

これは単なる確認ではありません。

👉 設計が正しいかの検証

です。

■ Previewで“状態設計”を検証する

良いPreviewは、1つでは足りません。

複数の状態を用意します。

正常状態

@Preview(name = "Normal")
@Composable
fun PreviewNormal() {
UserProfile(
state = UserProfileUiState("Taro", false),
onClick = {}
)
}

フォロー済み

@Preview(name = "Following")
@Composable
fun PreviewFollowing() {
UserProfile(
state = UserProfileUiState("Taro", true),
onClick = {}
)
}

ローディング

@Preview(name = "Loading")
@Composable
fun PreviewLoading() {
UserProfile(
state = UserProfileUiState.loading(),
onClick = {}
)
}

ここで分かることがあります。

状態が増えるほど、UI設計の甘さが露呈する

  • 分岐漏れ
  • 表示崩れ
  • null依存

👉 Previewは「状態網羅チェック」に使える

■ Previewが教えてくれる設計の問題

① 引数が多すぎる

fun UserProfile(
name: String,
age: Int,
isFollowing: Boolean,
isLoading: Boolean,
errorMessage: String?
)

Previewを書くのが辛くなります。

👉 Stateにまとめるべきサイン

② 状態が曖昧

errorMessage: String?
isLoading: Boolean

Previewで「どの状態を作ればいいか分からない」

👉 正規化されていない

③ ViewModel依存

Previewが書けない

👉 責務分離が崩れている

■ Preview Driven Development

ここで一歩進めます。

Previewを先に書く

です。

手順

① Stateを定義
② Previewを書く
③ UIを実装する

data class UiState(
val title: String,
val isLoading: Boolean
)
@Preview
@Composable
fun PreviewSample() {
SampleScreen(
state = UiState("Hello", false)
)
}

👉 ここからUIを書く

この方法のメリット:

  • UI仕様が明確になる
  • 状態設計が先に固まる
  • ViewModelに引きずられない

■ Previewを“コンポーネント設計”に使う

Composable分割とも強く関係します。

@Composable
fun ProfileHeader(...)

これ単体でPreviewできるか?

👉 YESなら良い設計

👉 NOなら依存が強すぎる

■ 実務でのPreview活用パターン

パターン① 状態網羅Preview

@Preview(name = "Empty")
@Preview(name = "Error")
@Preview(name = "Content")

パターン② ダークモード

@Preview(uiMode = UI_MODE_NIGHT_YES)

パターン③ サイズ違い

@Preview(widthDp = 320)
@Preview(widthDp = 600)

👉 UI仕様の検証ができる

■ Previewを壊すアンチパターン

❌ ViewModel依存

❌ remember乱用

❌ 外部状態依存

❌ Singletonアクセス

これらはすべて、

👉 Preview不能 = 設計不良

のサインです。

■ まとめ

Previewは単なる便利機能ではありません。

UI設計を可視化するツール

です。

重要なポイント

  • PreviewできるUIは良い設計
  • PreviewできないUIは責務が崩れている
  • 状態を網羅すると設計の穴が見える
  • Previewを先に書くと設計が安定する

Composeにおいて、

  • 再利用できる
  • テストしやすい
  • バグりにくい

UIはすべて、

👉 PreviewしやすいUI

でもあります。

タイトルとURLをコピーしました