Ashish Dwivedi
Ashish Dwivedi

Reputation: 8196

Android Jetpack Compose Snackbar: How to immediately close the previous Snackbar when a new one is triggered?

I'm facing an issue with Android Jetpack Compose Snackbar in my application. I have a LazyColumn where each row is a SwipeToDismiss composable. On each swipe, I remove the row and show a Snackbar. However, if I swipe rows very frequently, a queue of Snackbars is created, and they appear one by one. I want to modify this behavior so that only the latest Snackbar is shown, and it immediately closes the previous one.

    val scaffoldState = rememberScaffoldState()

   LaunchedEffect(key1 = true) {
       viewModel.uiEvent.collect { event ->
           when (event) {
               is UiEvent.ShowSnackBar -> {
                   val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(
                       message = event.message,
                       actionLabel = event.action
                   )
                   when (snackbarResult) {
                       SnackbarResult.Dismissed -> {}
                       SnackbarResult.ActionPerformed ->   viewModel.onEvent(MessageListEvent.OnUndoDeleteClick)
                   }
               }
               else -> Unit
           }
       }
   }

Upvotes: 5

Views: 2673

Answers (2)

Lenoarod
Lenoarod

Reputation: 3620

I would suggest you cancel the previous snackBar before show next.

the reason why we could do that is SnackbarHostState has a publish property: currentSnackbarData. it is the The current SnackbarData being shown by the SnackbarHost, of null if none.

  • firstly, get the previous snackBar
scaffoldState.currentSnackbarData
  • then, call dismiss method

  • next show the new snackbar

scaffoldState.currentSnackbarData.dismiss()

so the final answer is:

val scaffoldState = rememberScaffoldState()

   LaunchedEffect(key1 = true) {
       viewModel.uiEvent.collect { event ->
           when (event) {
               is UiEvent.ShowSnackBar -> {
                    scaffoldState.currentSnackbarData.dismiss()
                   val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(
                       message = event.message,
                       actionLabel = event.action
                   )
                   when (snackbarResult) {
                       SnackbarResult.Dismissed -> {}
                       SnackbarResult.ActionPerformed ->   viewModel.onEvent(MessageListEvent.OnUndoDeleteClick)
                   }
               }
               else -> Unit
           }
       }
   }

Upvotes: 3

Jan Itor
Jan Itor

Reputation: 4256

You could try something like:

val uiEvent = viewModel.uiEvent.collectAsStateWithLifecycle()
LaunchedEffect(uiEvent.value) {
    when (val event = uiEvent.value) {
        is UiEvent.ShowSnackBar -> {
            val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(
                message = event.message,
                actionLabel = event.action
            )
            when (snackbarResult) {
                SnackbarResult.Dismissed -> {}
                SnackbarResult.ActionPerformed -> viewModel.onEvent(MessageListEvent.OnUndoDeleteClick)
            }
        }

        else -> Unit
    }
}

This way, when uiEvent.value changes, LaunchedEffect will cancel the previous coroutine and its snackbar will be automatically dismissed.

Upvotes: 0

Related Questions