Jetpack Compose: Dismiss Snackbar programmatically

Introduction

Jetpack Compose has revolutionized the way developers create user interfaces in Android applications by offering a modern, declarative approach to UI development. One of the powerful features in Jetpack Compose is the use of Snackbar, a transient message that appears at the bottom of the screen to inform users of some event or action. While displaying a Snackbar is straightforward, dismissing it programmatically can sometimes pose a challenge, especially if you're looking to handle it based on user actions or specific conditions in your code. In this article, we'll explore how to control the visibility of a Snackbar programmatically using Jetpack Compose in Kotlin.

The following walkthrough breaks down a practical example of how to implement and manage the dismissal of a Snackbar. This guide is aimed at Android developers who are looking to leverage the flexibility of Jetpack Compose to enhance their application's user experience. We'll delve into how to create a responsive and interactive UI, allowing users to both trigger and dismiss Snackbars with ease.

Understanding the Scaffold Layout

The core of this example revolves around the use of the Scaffold composable, which is a fundamental component in Jetpack Compose for building UI structures. The Scaffold provides a layout that typically includes slots for elements like the top app bar, bottom navigation, and floating action buttons, among others. Here, the Scaffold is initialized with a custom ScaffoldState that contains a SnackbarHostState. This state is essential for displaying and dismissing Snackbars dynamically.

Within the Scaffold, a TopAppBar is added at the top of the screen with a title indicating the purpose of the example. The background color is set to a light greenish tone, making the app visually appealing. The main content area of the Scaffold houses the primary components, where user interactions are handled.

Managing Snackbar State with Coroutines

In the MainContent composable function, a coroutine scope is utilized to manage the asynchronous nature of displaying and dismissing the Snackbar. The rememberCoroutineScope() function is crucial here, allowing us to launch coroutines in a safe, Compose-friendly way. Additionally, a mutable state variable, snackBarStatus, is used to track the status of the Snackbar, updating the UI dynamically when the Snackbar is dismissed.

When the user presses the "Show Snackbar" button, a coroutine is launched to display a Snackbar with an indefinite duration. This type of Snackbar remains visible until it is manually dismissed, either by user interaction or through programmatic control. The result of the showSnackbar function is checked, and if the Snackbar is dismissed, the snackBarStatus text is updated to inform the user.

Programmatic Dismissal of the Snackbar

One of the key features of this implementation is the ability to dismiss the Snackbar programmatically. The second button labeled "Dismiss Snackbar" is set up to achieve this functionality. When clicked, it directly accesses the current Snackbar instance using scaffoldState.snackbarHostState.currentSnackbarData and calls the dismiss() method. This effectively removes the Snackbar from the screen, providing a quick and user-friendly way to control its visibility without waiting for a timeout or user swipe.

The approach used here demonstrates the power of state management in Jetpack Compose. By utilizing the ScaffoldState and SnackbarHostState, developers can not only show but also control the lifecycle of Snackbars based on specific conditions in the app's flow.

Enhanced User Experience

In terms of user experience, the combination of responsive buttons and programmatic control over Snackbars allows for a more interactive and intuitive interface. Users can instantly dismiss notifications if they are no longer needed, which can be especially useful in real-time applications where new information continuously flows in. The ability to dismiss a Snackbar manually provides a greater level of control and customization to the user.

Additionally, this approach can be extended to handle other scenarios, such as showing a Snackbar only for specific user actions, dismissing it when an event completes, or even chaining multiple Snackbars with different messages based on application state.

Conclusion

By leveraging the power of Jetpack Compose and Kotlin coroutines, we can efficiently manage the display and dismissal of Snackbars in Android applications. This example demonstrates a flexible approach to handling transient messages, allowing developers to provide more responsive and user-friendly interactions in their apps. The use of Scaffold, combined with coroutine-based state management, ensures that the UI remains fluid and responsive, even when dealing with asynchronous events.

Whether you're building a simple notification system or a complex real-time application, understanding how to programmatically control Snackbars in Jetpack Compose will add to the overall usability and polish of your Android projects.


MainActivity.kt

package com.cfsuman.jetpackcompose

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Color
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.material.icons.filled.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            GetScaffold()
        }
    }


    @Composable
    fun GetScaffold(){
        val scaffoldState: ScaffoldState = rememberScaffoldState(
            snackbarHostState = SnackbarHostState()
        )

        Scaffold(
            scaffoldState = scaffoldState,
            topBar = {
                TopAppBar(
                    title = { Text(
                        text = "Compose - Snackbar Dismiss In Code"
                    )},
                    backgroundColor = Color(0xFFC0E8D5),
                )
            },
            content = {MainContent(scaffoldState)},
            backgroundColor = Color(0xFFEDEAE0),
        )
    }


    @Composable
    fun MainContent(scaffoldState: ScaffoldState){
        val scope = rememberCoroutineScope()
        var snackBarStatus by remember {
            mutableStateOf("Snackbar status")
        }

        Box(
            modifier = Modifier.fillMaxSize(),
            contentAlignment = Alignment.Center
        ){
            Column(
                verticalArrangement = Arrangement.spacedBy(24.dp),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(
                    text = snackBarStatus,
                    style = MaterialTheme.typography.h5
                )

                Button(onClick = {
                    scope.launch{
                        scaffoldState
                            .snackbarHostState.showSnackbar(
                                message = "This is an indefinite Snackbar",
                                duration = SnackbarDuration.Indefinite
                            ).let {
                                when(it){
                                    SnackbarResult.Dismissed -> {
                                        snackBarStatus = "Snackbar dismissed"
                                    }
                                }
                            }
                    }
                }) {
                    Text(text = "Show Snackbar")
                }

                Button(onClick = {
                    scaffoldState.snackbarHostState
                        .currentSnackbarData?.dismiss()
                }) {
                    Text(text = "Dismiss Snackbar")
                }
            }
        }
    }
}
More android jetpack compose tutorials