tezvyn:

How would you architect state-driven UI with ViewModel and Compose?

Curated by the Tezvyn teamSource: developer.android.comadvanced
How would you architect state-driven UI with ViewModel and Compose?

Tests unidirectional data flow and state hoisting. Model screen state as an immutable data class, expose StateFlow from ViewModel, and collect it in Compose so events flow up and state down. Red flag: exposing mutable state or putting logic in Composables.

WHAT THIS TESTS: The interviewer wants to see if you treat the ViewModel as the single source of truth and keep Composables as pure functions of state. They care about unidirectional data flow, immutability, recomposition performance, and separation of concerns. Specifically, they are checking whether you know how to model UI state as a single coherent object rather than a scattered collection of variables, and whether you understand the lifecycle boundaries between Compose and ViewModel.

A GOOD ANSWER COVERS: First, model the screen state as an immutable data class that contains everything the UI needs to render, including loading flags, data lists, and error messages. Second, hold that state in a private MutableStateFlow inside the ViewModel and expose it as a public read-only StateFlow. Third, in the Composable, collect that flow with collectAsStateWithLifecycle to respect the lifecycle and avoid unnecessary recompositions. Fourth, send user actions back to the ViewModel via callback lambdas or a sealed class event sink so that all business logic lives above the UI layer. Fifth, keep Composables stateless and idempotent by deriving all presentation values from the state object rather than using remember for business data.

COMMON WRONG ANSWERS: Exposing mutable state such as var uiState directly from the ViewModel breaks the contract and allows Composables to mutate state. Using multiple LiveData or StateFlow objects for different parts of the screen creates synchronization bugs and makes loading or error handling inconsistent. Putting business logic like network calls or validation inside Composables makes them untestable and couples them to the Android framework. Using rememberSaveable for screen-level state that should survive process death is a mistake when the ViewModel with SavedStateHandle already owns that responsibility.

LIKELY FOLLOW-UPS: How do you handle partial state updates without triggering full recompositions? When would you use CompositionLocal instead of passing state down through parameters? How do you debounce rapid user inputs before they reach the ViewModel? What is the difference between collectAsState and collectAsStateWithLifecycle, and why does it matter for configuration changes?

ONE CONCRETE EXAMPLE: Imagine a news feed screen. The ViewModel holds a private val _uiState = MutableStateFlow(NewsFeedUiState()) and exposes val uiState: StateFlow<NewsFeedUiState> = _uiState.asStateFlow(). The Composable calls val state by viewModel.uiState.collectAsStateWithLifecycle() and renders a list when state.articles is non-empty, a loading spinner when state.isLoading is true, and an error banner when state.error is non-null. Tapping a bookmark button invokes viewModel.onBookmarkClicked(articleId), which updates the flow internally. The Composable never writes to the state.

Read the original → developer.android.com

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.

How would you architect state-driven UI with ViewModel and Compose? · Tezvyn