29

Currently I am using ModalBottomSheetLayout to display the bottom sheet.

I don't know is there a way to listen to the bottom page closing event?

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
MKD
  • 475
  • 1
  • 5
  • 11
  • Can you please share some code that you've written to display sheet? And I wanted to clarify you want `closing` event or completely `closed/collapsed` event? – Mayur Gajra Sep 04 '21 at 06:10
  • hi @MayurGajra. thank you for support. i was find my solution as below – MKD Sep 04 '21 at 09:18

3 Answers3

42

In Compose to listen for changes you need to check current values of your mutable states.

In case with ModalBottomSheetLayout you have ModalBottomSheetState, and you can check on currentValue of this state. If you need you can modify your views state depending on this value.

If you wanna perform some action on state changes, you need to use side effects. The most basic one is LaunchedEffect, you can use it in combination with snapshotFlow to watch any state value:

LaunchedEffect(Unit) {
    snapshotFlow { modalBottomSheetState.currentValue }
        .collect {
            println(it.toString())
        }
}

Also I'll be called first time when you launch a screen, and you'll have Hidden there too, so depending on your needs it may not be an ideal solution.


To get the most close to listening on becoming hidden, you can use DisposableEffect.

if (modalBottomSheetState.currentValue != ModalBottomSheetValue.Hidden) {
    DisposableEffect(Unit) {
        onDispose {
            println("hidden")
        }
    }
}

Here I'm launching DisposableEffect when your sheet appears, and when it disappears - I'm removing it from the view hierarchy, which will cause the onDispose to be called.

Full example of basically everything you can do with the state:

val modalBottomSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
val scope = rememberCoroutineScope()
LaunchedEffect(modalBottomSheetState.currentValue) {
    println(modalBottomSheetState.currentValue)
}
if (modalBottomSheetState.currentValue != ModalBottomSheetValue.Hidden) {
    DisposableEffect(Unit) {
        onDispose {
            println("hidden")
        }
    }
}
ModalBottomSheetLayout(
    sheetState = modalBottomSheetState,
    sheetContent = {
        Text(
            "sheetContent",
            modifier = Modifier.fillMaxHeight()
        )
    }
) {
    Column {
        Text(modalBottomSheetState.currentValue.toString())
        Button(onClick = {
            scope.launch {
                modalBottomSheetState.show()
            }
        }) {
            Text("Show bottom sheet")
        }
    }
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
26

You can just use confirmStateChange from rememberModalBottomSheetState:

val state = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden, confirmStateChange = {
   if (it == ModalBottomSheetValue.Hidden) {
       println(it)
   }
   true
})
Senocico Stelian
  • 1,378
  • 15
  • 23
9

Using confirmStateChange won't be invoked however if you show/hide the modal yourself with a clickable for example - it seems to only be invoked on clicking the scrim or swiping.

A way around this is to just observe the modalBottomSheetState.targetValue which will change anytime the modal is animating between two different states:

LaunchedEffect(modalBottomSheetState.targetValue) {
    if (modalBottomSheetState.targetValue == ModalBottomSheetValue.Hidden) {
        // do something when animating to Hidden state
      } else {
        // expanding
      }
}

This is closer to the timing of confirmStateChange which is invoked before the call to show/hide. Observing modalBottomSheetState.currentValue will change at the end of the animation, while modalBottomSheetState.targetValue will change before the animation begins.

lecker909
  • 181
  • 3
  • 6