mcpField
A
android-viewmodel

android-viewmodel

github.com/new-silvermoon開発992552 回インストール13 週間前に更新
リポジトリ

android-viewmodel について

Android Kotlin 開発者向けの ViewModel と状態管理のガイドです。StateFlow、SharedFlow、ライフサイクルに応じた収集方法を扱い、不変な UI 状態と明示的なバッキングフィールドを重視します。

主な機能

  1. 01StateFlow による永続 UI 状態
  2. 02SharedFlow による単発イベント
  3. 03ライフサイクル対応の Flow 収集
  4. 04viewModelScope での処理起動
  5. 05明示的なバッキングフィールドの優先

活用シーン

  1. 01画面の不変状態の設計
  2. 02ナビゲーションや Snackbar の処理
  3. 03Compose での状態とイベントの収集
  4. 04View でのライフサイクル別 Flow 収集

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/new-silvermoon/awesome-android-agent-skills --skill android-viewmodel

インストール後、Claude Code で/android-viewmodelを入力して読み込まれたか確認します。

詳細

Android ViewModel と状態管理

手順

ViewModel を使用して UI の状態とビジネスロジックを管理します。構成変更後も保持され、イミュータブルな API を公開する必要があります。

Kotlin 2.3+

従来の _state/state バッキングプロパティのパターンではなく、明示的なバッキングフィールドを使用してください。

  • Kotlin 2.3:試験的機能(-Xexplicit-backing-fields)
  • Kotlin 2.4+:安定版(推奨)

古い Kotlin バージョンを対象とする場合を除き、重複するバッキングプロパティの作成は避けてください。


1. UI の状態(StateFlow)

目的

永続的な UI の状態を表します。

例:

  • 読み込み中
  • 成功(データ)
  • エラー(メッセージ)
  • 空
宣言

明示的なバッキングフィールドを使用します。

val uiState: StateFlow<UiState>
    field = MutableStateFlow(UiState.Loading)

バッキングフィールドはミュータブル(MutableStateFlow)で、利用側には StateFlow のみを公開します。

状態の更新

アトミックな更新には update { } を使用してください。

_uiState.update { old ->
    old.copy(isLoading = false)
}

明示的なバッキングフィールドを使用する場合:

uiState.update {
    it.copy(isLoading = false)
}

適切な場合は、直接代入することもできます。

uiState.value = UiState.Success(data)

2. 単発イベント(SharedFlow)

目的

再コンポーズや構成変更後に保持されない一時的なイベントに使用します。

例:

  • 画面遷移
  • スナックバーの表示
  • トーストの表示
  • ダイアログを開く
  • 権限をリクエストする
宣言
val uiEvent: SharedFlow<UiEvent>
    field = MutableSharedFlow(
        replay = 0,
        extraBufferCapacity = 1
    )

replay = 0 にすると、再作成後にイベントが再生されません。

tryEmit() で送信する UI イベントには、extraBufferCapacity = 1 を推奨します。

イベントの送信

サスペンド関数:

uiEvent.emit(UiEvent.NavigateBack)

サスペンドしない場合:

uiEvent.tryEmit(UiEvent.ShowSnackbar("Saved"))

3. Compose での収集

StateFlow

必ずライフサイクルを考慮して収集します。

val state by viewModel.uiState.collectAsStateWithLifecycle()
SharedFlow

単一の LaunchedEffect 内で収集します。

LaunchedEffect(Unit) {
    viewModel.uiEvent.collect { event ->
        when (event) {
            is UiEvent.NavigateBack -> { /* ... */ }
            is UiEvent.ShowSnackbar -> { /* ... */ }
        }
    }
}

collectAsState() で SharedFlow を収集しないでください。


4. XML/View システムでの収集

ライフサイクルを考慮して収集します。

lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.uiState.collect {
            render(it)
        }
    }
}

イベントについても同様です。

lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.uiEvent.collect(::handleEvent)
    }
}

5. コルーチンスコープ

ViewModel の処理はすべて viewModelScope 内で起動します。

viewModelScope.launch {
    repository.refresh()
}

ビジネスロジックは、UseCase または Repository に委譲することを推奨します。


6. 状態の設計

画面全体を、単一のイミュータブルな状態オブジェクトで表します。

data class UiState(
    val isLoading: Boolean = false,
    val items: List<Item> = emptyList(),
    val error: String? = null
)

ライフサイクルが本当に異なる場合を除き、1 つの画面に対して相互に関連のない StateFlow を複数公開するのは避けてください。


7. ベストプラクティス

イミュータブルな API(StateFlow、SharedFlow)を公開する

明示的なバッキングフィールドを優先する(Kotlin 2.4+)

イミュータブルな UI 状態を使用する

状態の変更には update {} を使用する

単発イベントは SharedFlow で管理する

ビジネスロジックを Composable の中に置かない

collectAsStateWithLifecycle() を使用する

View では repeatOnLifecycle() を使用する

処理は viewModelScope 内で起動する

MutableStateFlow を公開しない

画面遷移イベントに StateFlow を使用しない

永続的な画面の状態に SharedFlow を使用しない

明示的なバッキングフィールドを使用できる場合、重複する _state プロパティを保持しない

コメント