One-Time Events

One-Time Events with SharedFlow or Channels

Some outputs are not state: a snackbar message, a navigation command, a "copied" toast. If you store "show the message Added Salt and Saffron" in a StateFlow, it shows again after every rotation, because a StateFlow replays its value to each new collector. The two usual tools behave differently:

One-time events: SharedFlow versus Channel
Tool Delivery When nobody is collecting
MutableSharedFlow() (no replay) to every current collector the event is dropped
Channel(BUFFERED) + receiveAsFlow() to exactly one collector buffered until collected

For a snackbar, a lost event is a bug and a doubled one is too, so BookNest uses a Channel:

ui/ViewModels.kt and BookNestNav.kt: sending and showing messages (two excerpts)Kotlin
  private val _messages = Channel<String>(Channel.BUFFERED)
  val messages: Flow<String> = _messages.receiveAsFlow()   // each message delivered once
...
  val snackbar = remember { SnackbarHostState() }
  LaunchedEffect(cartVm) {
    cartVm.messages.collect { snackbar.showSnackbar(it) }   // each message shown once
  }

onEvent sends with trySend, which never suspends. A rotation cancels the LaunchedEffect and the new composition starts another; a message sent in between waits in the channel. Unidirectional Data Flow's figure shows the result.

Google's architecture guidance even models such messages as state (a userMessage field the UI clears with an event once shown). Both approaches are common; pick one per project.