Android アーキテクチャは Event-Driven UI から State-Driven UI へ移行している

Google の公式 Android ガイダンスは 2026年5月18日に更新され、ViewModel のイベントを UI State に変換することを明示的に推奨するようになった

これは、もはや単なる昔からのアーキテクチャ論争ではありません。

Google の Android ドキュメントは 2026年5月18日 に更新され、現在では明確に次のように述べています。

ViewModel events should always result in a UI state update.

ViewModel のイベントは、常に UI State の更新につながるべきです。

これは、ViewModel と UI の間の通信をどのように考えるべきかという点で、大きな変化です。

これまで Android では、次のようなパターンが一般的でした。


ViewModel
   │
   ├── UiState
   │
   └── UiEvent
          ↓
         UI

UiEvent は、Snackbar の表示、画面遷移、ダイアログの表示など、一度だけ実行したい処理によく使われていました。

しかし現在のガイダンスは、次の方向へ移りつつあります


ViewModel
   │
   │ UI State
   ↓
  UI

ここでは、簡単な Snackbar の例を使って、この違いを見てみましょう。

 

🤔 Event-Driven アプローチ

従来の実装は、例えば次のようになります。


sealed interface UiEvent {
    data class ShowSnackbar(
        val message: String
    ) : UiEvent
}

private val _events = Channel<UiEvent>()
val events = _events.receiveAsFlow()

fun save() {
    viewModelScope.launch {
        repository.save()

        _events.send(
            UiEvent.ShowSnackbar("Saved successfully")
        )
    }
}

UI 側では、このイベントを消費します。


LaunchedEffect(Unit) {
    viewModel.events.collect { event ->
        when (event) {
            is UiEvent.ShowSnackbar ->
                snackbarHostState.showSnackbar(event.message)
        }
    }
}

この場合、ViewModel は実質的に UI に対して次のように指示しています。


"Snackbar を表示してください。"

これが Event-Driven なアプローチです。

 

🤔 State-Driven アプローチ

現在の Android ガイダンスは、上記とは異なる方向を示しています。

ShowSnackbar を送るのではなく、ViewModel が UI State を更新します。


data class UiState(
    val userMessage: String? = null
)

fun save() {
    viewModelScope.launch {
        repository.save()

        _uiState.update {
            it.copy(
                userMessage = "Saved successfully"
            )
        }
    }
}

UI は State を監視します。


LaunchedEffect(uiState.userMessage) {
    uiState.userMessage?.let { message ->
        snackbarHostState.showSnackbar(message)
        viewModel.userMessageShown()
    }
}

この場合、ViewModel は次のように言っているわけではありません。


"Snackbar を表示してください。"

代わりに、次のように伝えています。


"現在、表示すべきメッセージがあります。"

どのように表示するのかは UI が決定します。

 

🤔 重要な違い

この違いは、単純に


Channel → StateFlow

という API の置き換えではありません。

本質的には次の違いです。

Event-Driven:


ViewModel
    ↓
"これを実行して"
    ↓
UI

State-Driven:


ViewModel
    ↓
"これが現在の状態です"
    ↓
UI

Google は、この違いを次のようにも表現しています。

「State は存在する。Events は発生する。」

もちろん、イベントそのものがなくなったわけではありません。

ボタンのクリックは、今でもイベントです。


User
 ↓
Button.onClick
 ↓
ViewModel
 ↓
State
 ↓
UI

重要な変化は、ViewModel → UI の通信が、一度きりのイベントではなく State を中心とする方向へ進んでいることです。

 

🤔 では、Event を使うのをやめるべきなのか?

公式ガイダンスは、ChannelSharedFlow、あるいはイベントそのものを禁止しているわけではありません。

むしろ、ViewModel のイベントを UI State として表現できないかを改めて考えることを求めています。

ViewModel を設計するときには、次の問いが役立ちます。

- ViewModel は UI に「何をすべきか」を伝える必要があるのか?

- 「現在何が起きているのか」を State として表現するだけでよいのか?

この小さな考え方の変化が、Android の UI アーキテクチャの設計を大きく変える可能性があります。

 

🤔 参考


Related Categories :  DevelopmemtGoogleJetpackComposeKMPKotlinKotlin Multiplatform MobileRecommendedTrending