OneTimeEvent
A thread-safe wrapper for one-time events that should be consumed only once, even when observed multiple times.
This pattern solves a common problem in Android development: preventing events (like showing a snackbar, navigating to another screen, or displaying an error) from being triggered multiple times when the UI is recreated (e.g., due to configuration changes).
The Problem It Solves
When using StateFlow or LiveData, UI recompositions or lifecycle events can cause the same state to be observed multiple times. For events like "show error snackbar", this would result in the same error appearing repeatedly, which is undesirable.
How It Works
On first access via getContentIfNotHandled, returns the content and marks it as handled
Subsequent calls to getContentIfNotHandled return null
Uses AtomicBoolean for thread-safe handling without synchronization
peekContent allows inspecting the content without consuming it (useful for debugging/logging)
Usage in UiState
This class is primarily used for error events in UiState:
data class UiState<T : Any>(
val data: T,
val loading: Boolean = false,
val error: OneTimeEvent<Throwable?> = OneTimeEvent(null)
)Usage in StatefulComposable
The StatefulComposable automatically handles one-time events:
state.error.getContentIfNotHandled()?.let { error ->
LaunchedEffect(onShowSnackbar) {
onShowSnackbar(error.message.toString(), SnackbarAction.REPORT, error)
}
}When to Use
Use OneTimeEvent for:
Error messages/snackbars
Navigation events
Showing dialogs
One-time animations
Any event that should happen once per trigger, not once per observation
When NOT to Use
Don't use OneTimeEvent for:
Regular state data (use plain properties)
Loading states (use Boolean)
Data that should persist across recompositions
Thread Safety
This implementation uses AtomicBoolean.compareAndSet to ensure thread-safe handling without requiring explicit synchronization, making it safe to use from multiple threads.
Related Reading
Based on the SingleLiveEvent pattern: https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150
Parameters
The type of the event content. Can be nullable.