Interview QuestionPractical QuestionFollow-up Questions

Handling Transient Messages With Unidirectional Data Flow

skydovesJaewoong Eum (skydoves)||8 min read

Handling Transient Messages With Unidirectional Data Flow

Transient UI messages like Snackbars often originate from ViewModel logic, such as a failed network request or a completed action. In a Unidirectional Data Flow (UDF) architecture, these messages should be represented as part of the UI state rather than fired as one-off events. The ViewModel sets the message in state, the UI observes and displays it, and then notifies the ViewModel to clear it. By the end of this lesson, you will be able to:

  • Explain why transient messages should be modeled as UI state rather than one-off events.
  • Implement a Snackbar display using a nullable userMessage property in the UI state.
  • Describe the consumption cycle: ViewModel sets state, UI displays, UI notifies, ViewModel clears.
  • Identify the risks of using Channel or SharedFlow for transient UI events.
  • Apply LaunchedEffect to display a Snackbar when the message state changes.

Why State, Not Events

In UDF, the UI is a function of its state. If a Snackbar needs to appear, the state should reflect that. Modeling the message as a nullable property in the UI state data class ensures that:

  • The message survives configuration changes because the state is preserved in the ViewModel.
  • The UI always renders what the state dictates, making behavior reproducible.
  • There is no risk of losing the message if the UI is not collecting at the moment the event fires.

One-off event mechanisms like Channel or SharedFlow introduce timing dependencies. If the UI is not actively collecting when the event is emitted, the event can be lost. If multiple collectors exist, the event might be consumed by the wrong one. Representing the message as state eliminates these problems.

The Consumption Cycle

The cycle has four steps:

  1. The ViewModel updates the userMessage property in its UI state with the message text.
  2. The UI collects the state. When userMessage is not null, it triggers Snackbar display.
  3. After the Snackbar is shown and dismissed, the UI calls userMessageShown() on the ViewModel.
  4. The ViewModel sets userMessage back to null, preventing the Snackbar from reappearing on recomposition.

Implementation

The UI state includes a nullable message field:

data class LatestNewsUiState(
    val news: List<NewsItemUiState> = emptyList(),
    val isLoading: Boolean = false,
    val userMessage: String? = null
)

This interview continues for subscribers

Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.

Become a Sponsor