MVVM stands for Model View ViewModel. It is a common Android architecture pattern that separates UI from business logic.
The goal is simple: composables display state and send events, while the ViewModel prepares state and coordinates work.
Responsibilities
| Layer | Responsibility |
|---|---|
| View | Render UI and send user events |
| ViewModel | Hold UI state and handle screen logic |
| Model | Data classes, repositories, APIs, database |
Basic UI State
data class LoginUiState(
val email: String = "",
val password: String = "",
val isLoading: Boolean = false,
val error: String? = null
)
The UI reads this state.
@Composable
fun LoginScreen(
state: LoginUiState,
onEmailChange: (String) -> Unit,
onSubmit: () -> Unit
) {
TextField(value = state.email, onValueChange = onEmailChange)
Button(onClick = onSubmit, enabled = !state.isLoading) {
Text("Login")
}
}
ViewModel
The recommended approach is to expose state as a StateFlow. It survives configuration changes and works well with lifecycle-aware collection.
class LoginViewModel : ViewModel() {
private val _state = MutableStateFlow(LoginUiState())
val state: StateFlow<LoginUiState> = _state.asStateFlow()
fun onEmailChange(email: String) {
_state.update { it.copy(email = email) }
}
fun submit() {
_state.update { it.copy(isLoading = true, error = null) }
viewModelScope.launch {
try {
// call repository
} catch (e: Exception) {
_state.update { it.copy(isLoading = false, error = e.message) }
}
}
}
}
In the composable, collect the state with collectAsStateWithLifecycle. This stops collection when the screen is not visible, which saves battery.
@Composable
fun LoginRoute(viewModel: LoginViewModel = viewModel()) {
val state by viewModel.state.collectAsStateWithLifecycle()
LoginScreen(
state = state,
onEmailChange = viewModel::onEmailChange,
onSubmit = viewModel::submit
)
}
The ViewModel exposes state and functions. The UI does not know where data comes from.
Why MVVM Helps
MVVM makes code easier to test because business decisions live outside UI. It also avoids huge Activity or composable files.
Practice
Build a todo input screen. Create TodoUiState, a TodoViewModel, and a stateless composable that receives state and callbacks.
Summary
MVVM keeps UI simple and moves screen logic into ViewModels. Use StateFlow with collectAsStateWithLifecycle for production-grade state observation. For junior Android developers, this is the first major step toward professional app architecture.