Add Action Buttons to Snackbars in Jetpack Compose Like a Pro

Snackbars are a fundamental UI element in modern Android apps, providing lightweight feedback to users about an operation, with the option to include actionable buttons. With Jetpack Compose’s declarative UI paradigm, implementing and customizing Snackbars has become more flexible and powerful.

In this blog, we’ll explore how to add action buttons to Snackbars in Jetpack Compose while adhering to best practices, ensuring accessibility, and using the latest APIs. Whether you're designing a simple undo operation or implementing more complex interactions, this guide is for you.

Understanding Snackbars in Jetpack Compose

In Jetpack Compose, Snackbars are handled using the SnackbarHost and SnackbarHostState classes. The SnackbarHost is a composable that displays Snackbars, while the SnackbarHostState manages the state of the Snackbar queue. This decoupled architecture allows greater control and flexibility when displaying messages.

Here’s a basic example of creating and displaying a Snackbar:

val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()

Scaffold(
    snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
) {
    Button(onClick = {
        scope.launch {
            snackbarHostState.showSnackbar("Hello, Snackbar!")
        }
    }) {
        Text("Show Snackbar")
    }
}

This snippet shows a simple Snackbar without any action button. Let’s enhance it by adding actionable buttons next.

Adding Action Buttons to Snackbars

Action buttons provide interactivity within Snackbars, allowing users to respond immediately to the message. To include an action button, the SnackbarData class offers the actionLabel parameter. Here’s how you can implement it:

Button(onClick = {
    scope.launch {
        snackbarHostState.showSnackbar(
            message = "Item deleted",
            actionLabel = "Undo"
        )
    }
}) {
    Text("Delete Item")
}

When an actionLabel is provided, the Snackbar renders a button alongside the message. Users can tap this button to perform the desired action. To handle user interactions with this button, use the return value of showSnackbar():

val result = scope.launch {
    val snackbarResult = snackbarHostState.showSnackbar(
        message = "Item deleted",
        actionLabel = "Undo"
    )
    if (snackbarResult == SnackbarResult.ActionPerformed) {
        // Handle the undo action
        println("Undo clicked")
    }
}

Customizing Snackbars in Jetpack Compose

While the default Snackbar appearance works for many use cases, Jetpack Compose allows full customization of its design and behavior. Let’s look at some advanced customization options.

1. Styling the Snackbar

To change the default look, wrap the Snackbar composable in a custom SnackbarHost:

SnackbarHost(hostState = snackbarHostState) { data ->
    Snackbar(
        snackbarData = data,
        backgroundColor = Color.Black,
        contentColor = Color.White,
        actionColor = Color.Cyan
    )
}

Here, you can customize colors and typography to match your app’s theme.

2. Custom Layout for the Snackbar

Compose’s flexibility allows you to create completely custom layouts for your Snackbars:

SnackbarHost(hostState = snackbarHostState) { data ->
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .background(Color.DarkGray)
            .padding(16.dp),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        Text(
            text = data.message,
            color = Color.White,
            style = MaterialTheme.typography.body1
        )
        Button(onClick = { /* Custom action */ }) {
            Text("Retry")
        }
    }
}

This approach gives you complete control over how the Snackbar appears and behaves.

Best Practices for Using Snackbars

When implementing Snackbars in your app, consider the following best practices:

  1. Use Snackbars Sparingly: Avoid overwhelming users with too many Snackbars. Use them for essential feedback only.

  2. Ensure Accessibility: Use descriptive messages and action labels. Avoid jargon or technical terms.

  3. Provide Timely Actions: The action should be relevant to the context, like undoing a delete operation.

  4. Limit Action Count: Stick to a single action to keep the UI simple and intuitive.

  5. Respect Timing: Snackbars automatically dismiss after a few seconds. If more time is needed, consider alternative feedback mechanisms.

Advanced Use Cases for Snackbars

Snackbars can be used in creative and complex ways to enhance user experience. Here are some advanced scenarios:

1. Snackbar Queuing

Compose’s SnackbarHostState manages a queue of Snackbars, displaying them one at a time. Here’s how to enqueue multiple messages:

scope.launch {
    snackbarHostState.showSnackbar("First Message")
    snackbarHostState.showSnackbar("Second Message")
}

Snackbars will appear sequentially, maintaining a clean and orderly experience.

2. Conditional Snackbar Display

Snackbars can be conditionally displayed based on state changes. For example, show a Snackbar when a network error occurs:

val isError by remember { mutableStateOf(false) }

if (isError) {
    scope.launch {
        snackbarHostState.showSnackbar("Network error occurred")
    }
}

3. Global Snackbar Management

For apps with multiple screens, managing Snackbars globally ensures consistency. Use a shared SnackbarHostState across your app’s Scaffold:

val snackbarHostState = remember { SnackbarHostState() }

NavHost(...) {
    Scaffold(snackbarHost = { SnackbarHost(snackbarHostState) }) { ... }
}

Additional Resources

To deepen your understanding of Snackbars in Jetpack Compose, explore these external resources:

  1. Snackbars in Jetpack Compose - Official Android documentation.

  2. Material Design Guidelines for Snackbars - Comprehensive guide on Material Design principles.

  3. Jetpack Compose Basics - Learn more about building UIs with Jetpack Compose.

Conclusion

Snackbars are a versatile and essential component of any Android app, providing quick feedback and actionable options for users. By leveraging Jetpack Compose’s declarative approach, you can create customized, accessible, and contextually relevant Snackbars to enhance the user experience.

With the knowledge and best practices shared in this guide, you’re now equipped to implement Snackbars like a pro. Experiment with different designs and use cases to make your app’s UI more interactive and engaging.

Happy coding!