← Android Interview Prep: Kotlin, Coroutines & Flow

Flow and modelling UI state

Lesson 4 of 51 min read

Cold vs hot, StateFlow for state, SharedFlow for events, and the operators worth knowing.

If coroutines are "one result later", Flow is "many results over time". For a screen, that is exactly what state is.

Cold vs hot, in one example

A cold Flow does nothing until collected, and each collector gets its own run — a Room query, an API poll. A hot flow is running regardless, and collectors join whatever is happening now.

  • StateFlow — hot, always holds exactly one current value. This is your screen state.
  • SharedFlow — hot, no stored value, can replay. This is your one-shot events: navigate, show a snackbar.

The shape that reviewers expect

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

class FeedViewModel(repo: FeedRepository) : ViewModel() {
    private val _state = MutableStateFlow(FeedUiState())
    val state: StateFlow<FeedUiState> = _state.asStateFlow()
}

Two details carry weight: the mutable flow is private and exposed with asStateFlow(), and the state is one object, so impossible combinations like loading-with-an-error are easy to spot.

stateIn, and why the timeout

val state = repo.observeFeed()
    .map { FeedUiState(items = it) }
    .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), FeedUiState())

WhileSubscribed(5_000) keeps the upstream alive for five seconds after the last collector leaves, so a rotation does not tear down and restart the database query. Knowing why that number is there is a senior signal.

The four operators that come up

  • map — transform each value.
  • combine — join two sources into one state (data plus filter).
  • debounce — wait for the user to stop typing.
  • flatMapLatest — cancel the previous inner work when a new value arrives. This is the search-box operator.

Collecting in the UI

Use collectAsStateWithLifecycle() in Compose. Plain collectAsState() keeps collecting while the app is in the background, which is a battery and crash report waiting to happen.