A declarative API for Snackbars in Compose Multiplatform (Android, iOS, Desktop) that makes it easy to display and manage snackbar messages in your application.
- Declarative API: Unlike the official Scaffold-based approach, this library provides a fully declarative API that fits better with Compose's paradigm.
- Global and local snackbars: Easily create both application-wide and screen-specific snackbars without overlapping critical UI elements.
- Type-safe messages: Support for custom message types beyond just strings.
- Flexible positioning: Position snackbars at the top or bottom in your layout.
- Custom UI: No dependency on the Material snackbar, you can provide any custom UI you want.
- Custom animations: Customize the enter and exit animations for your snackbars.
| Global and local | Custom content |
|---|---|
![]() |
![]() |
Add the dependency to your app's build.gradle.kts file:
dependencies {
implementation("io.github.ajiekcx:declarative-snackbar-compose:0.2.0")
implementation("io.github.ajiekcx:declarative-snackbar-core:0.2.0") // Optional, for modules without compose dependency
}- Create a global snackbar component:
object GlobalSnackbarComponent: SnackbarComponent<String> by SnackbarComponent()- Wrap your app content with SnackbarBox:
SnackbarBox(
component = GlobalSnackbarComponent,
snackbarContent = { message ->
Snackbar { Text(message) }
},
) {
// Your app content here
}- Show a snackbar message from anywhere in your app (in the presentation layer — ViewModel, etc.):
GlobalSnackbarComponent.show(
SnackbarMessage(
content = "This is a global message",
duration = SnackbarDuration.Short
)
)- To prevent overlapping important UI elements, you can apply built-in modifiers:
SnackbarBox() {
Scaffold(
topBar = {
TopAppBar(
title = { Text("Title") },
modifier = Modifier.noOverlapTopContentBySnackbar()
)
},
bottomBar = {
NavigationBar(
modifier = Modifier.noOverlapBottomContentBySnackbar()
)
}
)
}
Use the noOverlapTopContentBySnackbar modifier to prevent the snackbar (with top alignment) from
overlapping top UI content, and noOverlapBottomContentBySnackbar for bottom-aligned snackbars.
- Optionally create a custom message types for type-safe snackbars:
sealed interface MySnackbarMessage {
val message: String
class Text(override val message: String) : MySnackbarMessage
class TextWithAction(
override val message: String,
val actionTitle: String,
val onActionClick: () -> Unit
) : MySnackbarMessage
}- Create a local snackbar component in your ViewModel:
class MyViewModel : ViewModel() {
val snackbarComponent: SnackbarComponent<MySnackbarMessage> = SnackbarComponent()
fun showMessage() {
snackbarComponent.show(
SnackbarMessage(
content = MySnackbarMessage.Text("This is a local message"),
duration = SnackbarDuration.Short
)
)
}
override fun onCleared() {
super.onCleared()
snackbarComponent.onDestroy()
}
}- Use SnackbarBox in your screen:
SnackbarBox(
component = viewModel.snackbarComponent,
alignment = Alignment.TopCenter,
snackbarContent = { message ->
Snackbar(
modifier = Modifier.padding(horizontal = 16.dp),
action = {
if (message is MySnackbarMessage.TextWithAction) {
TextButton(onClick = { message.onActionClick() }) {
Text(message.actionTitle)
}
}
}
) {
Text(message.message)
}
},
) {
// Your screen content here
}See the sample app for more examples.

