Create a Custom Snackbar in Jetpack Compose (Success, Error, Warning)
Build a beautiful, reusable custom Snackbar component for Jetpack Compose that supports multiple message types with custom styling. This step-by-step tutorial shows you how to create Success, Error, Warning, and Default Snackbars with clean, production-ready code.
Learn how to build a beautiful, reusable custom Snackbar component in Jetpack Compose that supports different message types with custom styling. Perfect for displaying success messages, errors, warnings, and more!
📺 Are you a visual learner?
Check out the complete step-by-step video tutorial on YouTube where I walk you through building this custom Snackbar from scratch!
What We'll Build
By the end of this tutorial, you'll have a fully functional custom Snackbar system that:
- Supports multiple types (Success, Error, Warning, Default)
- Has custom colors and styling for each type
- Is easily reusable across your entire project
- Uses clean, production-ready code
Step 1: Define Snackbar Types
First, let's create an enum to define our different Snackbar types with their corresponding colors.
enum class SnackbarType(
val containerColor: Color,
val contentColor: Color = Color.White,
) {
SUCCESS(containerColor = Color(0xFF4CAF50)),
ERROR(containerColor = Color(0xFFF44336)),
WARNING(containerColor = Color(0xFFFF9800)),
DEFAULT(containerColor = Color.DarkGray);
}Why this approach?
- Each type has its own distinct color (green for success, red for error, orange for warning)
- Content color defaults to white for better contrast
- Easy to extend with more types if needed
Step 2: Create Custom Snackbar Visuals
Jetpack Compose's Snackbar system uses SnackbarVisuals to pass data. We'll create a custom implementation to include our type information.
data class CustomSnackbarVisuals(
override val message: String,
override val actionLabel: String? = null,
override val duration: SnackbarDuration = SnackbarDuration.Short,
override val withDismissAction: Boolean = false,
val type: SnackbarType = SnackbarType.DEFAULT,
) : SnackbarVisualsWhat's happening here?
- We implement the
SnackbarVisualsinterface to integrate with Compose's Snackbar system - All standard Snackbar properties are included (message, duration, action label, etc.)
- We add our custom
typeproperty to determine which style to use
Step 3: Create Extension Function for Easy Usage
To make our custom Snackbar easy to use throughout the app, we'll create an extension function on SnackbarHostState.
suspend fun SnackbarHostState.showCustomSnackbar(
message: String,
type: SnackbarType = SnackbarType.DEFAULT,
actionLabel: String? = null,
duration: SnackbarDuration = SnackbarDuration.Short,
withDismissAction: Boolean = false,
): SnackbarResult {
return showSnackbar(
CustomSnackbarVisuals(
message = message,
actionLabel = actionLabel,
duration = duration,
withDismissAction = withDismissAction,
type = type
)
)
}Benefits of this approach:
- Clean API that's easy to call from anywhere
- Returns
SnackbarResultso you can handle action button clicks
Step 4: Set Up the UI with Scaffold
Now let's implement the UI. We'll use a Scaffold with a custom SnackbarHost.
val snackbarHostState = remember {
SnackbarHostState()
}
val scope = rememberCoroutineScope()
Scaffold(
modifier = Modifier.fillMaxSize(),
snackbarHost = {
SnackbarHost(
hostState = snackbarHostState,
snackbar = { data ->
val visuals = data.visuals as? CustomSnackbarVisuals
val type = visuals?.type ?: SnackbarType.DEFAULT
Snackbar(
snackbarData = data,
shape = RoundedCornerShape(16.dp),
containerColor = type.containerColor,
contentColor = type.contentColor
)
}
)
}
) { innerPadding ->
// Your content here
}Key points:
SnackbarHostStatemanages the Snackbar queue and displayrememberCoroutineScope()allows us to launch coroutines for showing Snackbars- The custom
snackbarlambda extracts our custom type and applies the appropriate styling
Step 5: Display the Snackbar
To show a Snackbar, simply call the extension function we created:
Button(
onClick = {
scope.launch {
snackbarHostState.currentSnackbarData?.dismiss()
snackbarHostState.showCustomSnackbar(
message = "Operation completed successfully!",
type = SnackbarType.SUCCESS
)
}
}
) {
Text("Show Success Snackbar")
}Important details:
- Call
currentSnackbarData?.dismiss()to dismiss any existing Snackbar before showing a new one - Launch in a coroutine scope since
showCustomSnackbaris a suspend function - Simply change the
typeparameter to show different styled Snackbars
Complete Example with Multiple Buttons
Here's a complete example showing all Snackbar types:
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceAround
) {
SnackbarType.entries.forEach { type ->
Button(
onClick = {
scope.launch {
snackbarHostState.currentSnackbarData?.dismiss()
snackbarHostState.showCustomSnackbar(
"This is a ${type.name} snackbar",
type = type
)
}
},
colors = ButtonDefaults.buttonColors(
containerColor = type.containerColor
)
) {
Text(type.name, color = Color.White)
}
}
}Customization Ideas
Want to take this further? Here are some ideas:
1. Add Icons
enum class SnackbarType(
val containerColor: Color,
val contentColor: Color = Color.White,
val icon: ImageVector
) {
SUCCESS(
containerColor = Color(0xFF4CAF50),
icon = Icons.Default.CheckCircle
),
ERROR(
containerColor = Color(0xFFF44336),
icon = Icons.Default.Error
),
// ... more types
}2. Custom Durations per Type
kotlin
enum class SnackbarType(
val containerColor: Color,
val contentColor: Color = Color.White,
val defaultDuration: SnackbarDuration
) {
SUCCESS(
containerColor = Color(0xFF4CAF50),
defaultDuration = SnackbarDuration.Short
),
ERROR(
containerColor = Color(0xFFF44336),
defaultDuration = SnackbarDuration.Long
),
// ...
}3. Add Animations
Wrap your Snackbar in AnimatedVisibility for custom enter/exit animations.
Common Use Cases
Success Messages:
snackbarHostState.showCustomSnackbar(
"Profile updated successfully!",
type = SnackbarType.SUCCESS
)Error Messages:
snackbarHostState.showCustomSnackbar(
"Failed to load data. Please try again.",
type = SnackbarType.ERROR,
actionLabel = "Retry"
)Warning Messages:
snackbarHostState.showCustomSnackbar(
"Low battery. Some features may be limited.",
type = SnackbarType.WARNING,
duration = SnackbarDuration.Long
)Conclusion
You now have a fully functional, reusable custom Snackbar system for your Jetpack Compose app! This implementation is:
- Clean: Simple enum-based type system
- Flexible: Easy to extend with new types or features
- Reusable: Works across your entire app
- Modern: Uses Jetpack Compose best practices
The complete source code is available on GitHub. Feel free to customize the colors, add animations, or extend it with additional features!
Found this helpful? Subscribe to my YouTube channel for more Android and Jetpack Compose tutorials!
Questions or suggestions? Drop a comment below!