Jetpack Compose State Management for Production Apps
A practical model for state holders, UI state, events, and testable Compose screens in large Android codebases.
The Production Compose Rule
Compose works best when screens receive stable UI state and send clear user events. The composable should describe what the user sees, while the ViewModel coordinates domain work and state transitions.
data class CheckoutUiState(
val items: List<CartItem> = emptyList(),
val isLoading: Boolean = false,
val error: String? = null
)State Holders
Use a state holder when local UI behavior becomes meaningful enough to test or reuse. Keep it small, predictable, and close to the screen that owns it.
When To Hoist
Hoist state when another component needs to read or mutate it, when configuration changes matter, or when business rules affect it.
Events Over Callbacks
Typed events make screens easier to evolve.
sealed interface CheckoutEvent {
data object Retry : CheckoutEvent
data class QuantityChanged(val id: String, val quantity: Int) : CheckoutEvent
}Testing Strategy
Test the ViewModel state machine first. Then add targeted Compose tests for accessibility labels, empty states, loading states, and critical actions.