Sonner

An opinionated toast component for Jetpack Compose.

Configuration

To use a Sonner (snackbar) komoui, you need to use Scaffold as a root composable component. The Sonner will be registered as global component. This is to prevent a common issue where Snackbars disappear when navigating between screens in Compose because each screen can have its own Scaffold. This idea was inspired by Philipp Lackner

import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult
import com.komoui.themes.KomoTheme
import com.komoui.components.sooner.ObserveAsEvent
import com.komoui.components.sooner.SonnerHost
import com.komoui.components.sooner.SonnerProvider
import com.komoui.components.sooner.showSonner
 
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            KomoTheme {
                val scope = rememberCoroutineScope()
                val snackbarHostState = remember { SnackbarHostState() }
                ObserveAsEvent(SonnerProvider.events) { event ->
                    scope.launch {
                        snackbarHostState.currentSnackbarData?.dismiss()
                        val result = snackbarHostState.showSonner(event)
 
                        if (result == SnackbarResult.ActionPerformed) {
                            event.action?.execute()
                        }
                    }
                }
                Scaffold(
                    modifier = Modifier.fillMaxSize(),
                    snackbarHost = {
                        SonnerHost(hostState = snackbarHostState)
                    },
                ) { ip ->
                    // Your entire app navigation or root layout
                    AppNavigation(modifier = Modifier.padding(ip))
                }
            }
        }
    }
}

ObserveAsEvent

ObserveAsEvent is a lifecycle-aware composable utility that collects a Flow and treats each emission as a one-time event. It only collects when the composable is in the STARTED lifecycle state, preventing duplicate deliveries on recomposition or configuration changes.

import com.komoui.components.sooner.ObserveAsEvent
import com.komoui.components.sooner.SonnerProvider
 
ObserveAsEvent(
    flow = SonnerProvider.events,
    onEvent = { event ->
        // handle event
    }
)

Basic Usage

Destructive

With Action


API Reference

SonnerProvider

Singleton object for dispatching snackbar events. Call its suspend functions from a coroutine scope.

FunctionParametersDescription
showMessageSee belowShows a Default-variant snackbar
showErrorSee belowShows a Destructive-variant snackbar

Both showMessage and showError share the same parameter signature:

ParameterTypeDefaultDescription
messageStringPrimary message text
subMessageString?nullOptional secondary message displayed below the primary
actionSonnerAction?nullOptional action button configuration
withDismissActionBooleanfalseWhether to show a dismiss (close) button
durationSnackbarDurationShortHow long the snackbar stays visible

SonnerEvent

Data class representing a snackbar event dispatched through SonnerProvider.

ParameterTypeDefaultDescription
messageStringPrimary message text
subMessageString?nullOptional secondary message
actionSonnerAction?nullOptional action button
withDismissActionBooleanfalseWhether to show a dismiss button
variantSonnerVariantDefaultVisual variant of the snackbar
durationSnackbarDurationShortDisplay duration

SonnerAction

Data class defining an action button for the snackbar.

ParameterTypeDescription
actionTextStringLabel displayed on the action button
execute() -> UnitCallback invoked when the action button is clicked

SonnerVariant

Enum controlling the visual style of the snackbar.

ValueDescription
DefaultStandard snackbar using theme snackbar/foreground colors
DestructiveError/warning style using theme destructive colors

Sonner

The core snackbar composable. Rendered automatically by SonnerHost — you typically do not call this directly.

ParameterTypeDefaultDescription
modifierModifierModifierModifier applied to the snackbar
titleStringPrimary message text
subtitleString?nullOptional secondary message
actionLabelString?nullLabel for the optional action button
onActionClick(() -> Unit)?nullCallback for the action button
onDismiss(() -> Unit)?nullCallback for the dismiss button
variantSonnerVariantDefaultVisual variant

SonnerHost

A SnackbarHost wrapper that renders SonnerVisualsImpl visuals using Sonner and falls back to the default Material 3 Snackbar for any other visuals.

ParameterTypeDefaultDescription
hostStateSnackbarHostStateThe host state controlling snackbar display
modifierModifierModifierModifier applied to the host

ObserveAsEvent

Lifecycle-aware composable that collects a Flow and invokes onEvent for each emission only while the composable is in the STARTED state.

ParameterTypeDefaultDescription
flowFlow<T>The flow of events to observe
key1Any?nullOptional key — restarts the effect when its value changes
key2Any?nullOptional key — restarts the effect when its value changes
onEvent(T) -> UnitCallback invoked for each emitted event

SonnerVisualsImpl

SnackbarVisuals implementation that carries Sonner-specific data. Used internally by SonnerHost.

ParameterTypeDefaultDescription
messageStringPrimary message text
actionLabelString?nullOptional action button label
withDismissActionBooleanfalseWhether to show a dismiss button
durationSnackbarDurationDisplay duration
subMessageString?nullOptional secondary message
variantSonnerVariantDefaultVisual variant

SnackbarHostState.showSonner

Extension function that converts a SonnerEvent into SonnerVisualsImpl and shows it via the host state.

suspend fun SnackbarHostState.showSonner(event: SonnerEvent): SnackbarResult
ParameterTypeDescription
eventSonnerEventThe event containing message, variant, and action configuration

Returns SnackbarResultActionPerformed if the action button was tapped, Dismissed otherwise.

Edit this page on GitHub