I'm using compose material navigation so that I ca...
# compose
c
I'm using compose material navigation so that I can navigate to a bottom sheet destination. It works great, except on a single composable screen where I have
Copy code
BackHandler { myLambda() }
Even though the bottom sheet is showing, my backHandler still get triggered. What's the "right" way to prevent this?
b
did you mean even when not showing? If yes, checkout https://kotlinlang.slack.com/archives/CJLTWPH7S/p1628046266460700 tl;dr: use BackHandler conditionally when sheet is showing
c
Nope. My issue is that when my bottom sheet is showing, and I hit the back button. The bottom sheet doesn't dismiss, it just calls myLamda()
b
Don't you change sheet's state in myLambda then?
i.e it won't dismiss automatically, you need to change sheet's state to collapsed in BackHandler#onBack
c
Nope. compose navigation material with bottom sheet destinations takes care of state changes.
b
Your BackHandler is probably overriding navigation material's BackHandler, thus only myLambda gets invoked and sheet's state doesn't change
I haven't tried navigation material's bottomSheet yet, so I might be misunderstanding.
c
Yeah, that's pretty much the conclusion I've come to which is why I came here asking if there's another way I should be approaching this.
i
I mean, you said you want to handle back, so this seems expected? If you don't want to handle back, why are you using
BackHandler
at all?
c
I guess it's not "expected" to me because my screen isn't the one in focus. The bottom sheet is?
So it seems if that's purposeful (that backHandler gets triggered even when the screen isn't top-most) then I guess I need a way to tell when the screen isn't top most and use the enabled arg of BackHandler?
i
Yep.
BackHandler(enabled = navBackStackEntry.lifecycle.currentState == Lifecycle.State.RESUMED)
c
Ah. Thanks @Ian Lake! Question off of that though. My BackHandler is in my SignInScreen composable which doesn't have access to navBackStackEntry. Should I be passing that in, or should I be declaring this BackHandler up one level in my NavHost?
i
LocalLifecycleOwner.current.lifecycle
would point to the same object
c
Oh cool. so this ends up working. Thanks Ian!
Copy code
BackHandler(
    enabled = LocalLifecycleOwner.current.lifecycle.currentState == Lifecycle.State.RESUMED) {
    myLambda()
}
Okay. so uh. I'm gonna keep playing around with this, but I spoke too soon. The above code doesn't work.
Ian, any idea why the above didn't do what we thought it would do? i.e. stop handling back events when the bottom sheet is opened?
i
I'm just guessing, but I suspect that you aren't being recomposed when the Lifecycle state actually changes (so what you'd actually want is a
DisposableEffect
that manages a
LifecycleObserver
that sets your
enabled
state object)
I.e.,
ON_RESUME
set enabled to true,
ON_PAUSE
, back to false (which would be your default)
c
Oh interesting. i would have that it could recompose based on that code. I'll do a little more reading to see if I could come up with something. thanks for the tip.
For anyone else following along... this is what I came up with. A bit uglier than I thought it'd be but it works.
Copy code
var backEnabled by remember { mutableStateOf(false) }
val lifecycle = LocalLifecycleOwner.current.lifecycle
DisposableEffect(LocalLifecycleOwner) {
    backEnabled = lifecycle.currentState == Lifecycle.State.RESUMED
    onDispose {}
}

BackHandler(enabled = backEnabled) { myLabda() }
My main is was that I couldn't figure out how to call LocalLifecycleOwner.current in DisposableEffect because it's a composable itself.
i
Well, no. That won't recompose when the Lifecycle state changes
c
Ah snap. You're right. 😅 I was so excited that it didn't call myLamda() when the bottom sheet was up, now to realize that it never calls it.
i
I meant using a `LifecycleObserver`:
Copy code
var backEnabled by remember { mutableStateOf(false) }
val lifecycle = LocalLifecycleOwner.current.lifecycle
DisposableEffect(lifecycle) {
    val observer = LifecycleEventObserver { _, event ->
      if (event == Lifecycle.Event.ON_RESUME) {
        backEnabled = true
      else if (event == Lifecycle.Event.ON_PAUSE) {
        backEnabled = false
      }
    }
    lifecycle.addObserver(observer)
    onDispose {
      lifecycle.removeObserver(observer)
    }
}

BackHandler(enabled = backEnabled) { myLabda() }
c
Thanks. I will play around with this tomorrow. Threw that into my IDE and it can't resolve LifecycleStateObserver so it looks like I'm missing a dependency. Definitely underestimated this.
i
Ah, that's because it is called
LifecycleEventObserver
, whoops. Corrected the code snippet
🙏 1