Skip to content

Instantly share code, notes, and snippets.

@benigumocom
Last active December 9, 2025 02:35
Show Gist options
  • Select an option

  • Save benigumocom/1fc2ba6d5fd21b31b5ff84e0ad0ae3c8 to your computer and use it in GitHub Desktop.

Select an option

Save benigumocom/1fc2ba6d5fd21b31b5ff84e0ad0ae3c8 to your computer and use it in GitHub Desktop.
How to handle UI interactions with event processing
// No ViewModel
@Composable
fun UiOnlySample() {
val context = LocalContext.current
Button(onClick = {
Toast.makeText(context, "Clicked!", Toast.LENGTH_SHORT).show()
}) {
Text("Click")
}
}
// State Management
class StateFlowViewModel : ViewModel() {
private val _state = MutableStateFlow("initial")
val state = _state.asStateFlow()
fun update() {
_state.value = "updated at " + System.currentTimeMillis()
}
}
@Composable
fun StateFlowSample(vm: StateFlowViewModel = viewModel()) {
val value by vm.state.collectAsState()
Column {
Text(value)
Button(onClick = { vm.update() }) {
Text("Update")
}
}
}
// One-Shot Events
class SharedFlowViewModel : ViewModel() {
private val _event = MutableSharedFlow<String>(
replay = 0,
extraBufferCapacity = 1 // One-shot
)
val event = _event.asSharedFlow()
fun sendEvent() {
_event.tryEmit("Hello from SharedFlow!")
}
}
@Composable
fun SharedFlowSample(vm: SharedFlowViewModel = viewModel()) {
val context = LocalContext.current
LaunchedEffect(Unit) {
vm.event.collect { msg ->
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
}
}
Button(onClick = { vm.sendEvent() }) {
Text("Send Event")
}
}
// Strict One-Time + Ordered Delivery
class ChannelViewModel : ViewModel() {
private val _channel = Channel<String>(capacity = Channel.BUFFERED)
val eventFlow = _channel.receiveAsFlow()
fun sendEvent() {
viewModelScope.launch {
_channel.send("Hello from Channel!")
}
}
}
@Composable
fun ChannelSample(vm: ChannelViewModel = viewModel()) {
val context = LocalContext.current
LaunchedEffect(Unit) {
vm.eventFlow.collect { msg ->
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
}
}
Button(onClick = { vm.sendEvent() }) {
Text("Send Event")
}
}
@benigumocom
Copy link
Author

benigumocom commented Dec 9, 2025

  • UI Only

    • Good for very small, UI-contained logic.
    • No state preservation; everything resets on recomposition.
    • Simple one-time actions still work fine.
    • Weak testability and scalability.
    • Poor separation of concerns without ViewModel.
  • StateFlow

    • Designed specifically for holding and exposing UI state.
    • Always provides the latest value after recomposition.
    • Not suitable for one-time events because old values persist.
    • Ideal for continuously changing UI states.
    • Assumes at least one active collector.
    • Ensures consistent UI across configuration changes.
  • SharedFlow

    • Optimal for one-time UI events (Toast, Snackbar, Navigation).
    • replay = 0 + extraBufferCapacity = 1 helps prevent event loss.
    • Supports multiple collectors safely.
    • Not intended for state storage.
    • Standard modern pattern for UI event handling.
    • Robust against lifecycle situations where collectors appear/disappear.
  • Channel

    • Best suited for FIFO, strictly ordered event delivery.
    • Should only have one collector (shared consumption is unsafe).
    • Ensures one-by-one processing with guaranteed order.
    • Less compatible with UI lifecycle due to collector timing issues.
    • Typically replaced by SharedFlow in UI-layer logic.
    • More appropriate for internal sequential pipelines rather than UI events.

【Android】How to handle UI interactions with event processing
https://android.benigumo.com/20251209/how-to-handle-ui-interactions/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment