OneTimeEvent

class OneTimeEvent<T>(content: T)

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

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

T

The type of the event content. Can be nullable.

See also

UiState
StatefulComposable

Constructors

Link copied to clipboard
constructor(content: T)

Functions

Link copied to clipboard

Returns the content if it hasn't been handled yet, then marks it as handled.

Link copied to clipboard
fun peekContent(): T

Returns the content without marking it as handled.