Can anyone enlighten me why this does not work? It...
# compose
k
Can anyone enlighten me why this does not work? It seems to trigger an infinite loop even after i disabled the
BackHandler
before i invoke`doSumthing()` .
Copy code
// this is inside my composable
val enabledBackHandler = remember { mutableStateOf(true) }

BackHandler(enableBackHandler.value) {
    enabledBackHandler.value = false
    doSumthing()     
}

// this is a hoisted lambda Function, in my fragment
fun doSumthing() {
    requireActivity().backPressDispatcher.onBackPress()
}
s
I doubt the state change is already propagated so that the BackHandler is off by the time the dispatcher is run. With that said, adding a back handler only to just call
backPressDispatcher.onBackPress()
looks super suspicious, what are you trying to do?
k
i am trying to simulate the old
super.onbackPress()
. My backhandler has a when statement where only on certain UI states it does sumthing in
BackHandler()
, and on certian UI states it juz lets the framework do its
onbackpressDispatcher.onbackpressed()
.
s
Sounds like you want that "sometimes" to be part of your
BackHandler(here)
instead then. Not having a Backhandler (or having it turned off) is the right way to let the system do its own thing.
Copy code
BackHandler(uiState is UiState.SpecialBackHandlingCase) {
  Do custom thing()
}
k
my custom thing is actually doing this below
Copy code
if(!composeNavHostController.navigateUp()) {
 // this shld actually juz do a navController.popBackStack()
 requireActivity().backPressDispatcher.onBackPress()
}
My code is like this as i am actually using a compose navigation in my fragment with its own navgraph. and my fragment is part of the overall apps navcontroller. so essentially its a compose navigation in a fragment, with the fragment as part of a larger navgraph.
s
You shouldn't be calling
navigateUp()
from a back press in the first place either. What are you trying to achieve with that there?
Are you in a place where you got a bit of a fragmented navigation setup due to some part being built in the past and some part built later on top of navigation compose for example, instead of nav with fragments?
k
yea, my app is currenlty in the legacy xml navigation
i create a new feature using compose navigation contained in a single fragment
i am using navigateUp to juz pop a destination in my compose navigation;s navgraph
i noe navigate up and popbackstack is a slightly different but the top left back arrow and backpress currently does the same thing in my feature
i could change it to popBackStack. make not difference in my case.
s
Alright, I am not sure how well this scenario is supposed to play out. There are some interop APIs I think that are being worked on or might already be there by now https://kotlinlang.slack.com/archives/C04TPPEQKEJ/p1710367270255379?thread_ts=1710256960.426379&cid=C04TPPEQKEJ so that you don't need to be in this fragmented world.
So if you just do nothing there, does back not do the right thing in the first place though? Since as you say popBackStack should be the same as navigateUp(), then the back press should properly propagate inside your NavHost and do the right thing automatically. If you remove the BackHandler completely, what goes wrong? Trying to understand your use case.
NavController will just not enable their internal BackHandler if there is only one destination in the backstack and will let the system handle the back then. In your case you should just end up back at the fragment you came from then, does that not happen?
k
i use backHandler to block user's backpress in the case where my composable is in a loading state. and also to handle back press to display a compose dialog for user confirmation. the problem with the current default system backpress is that is does popBackStack on the app's navcontroller navgraph, which pops a fragment. And not the compose's navHostController's which pops a composable. when the system's backPress pop the backstack, it does not care about my compose navigation's navgraph situation.
wrapping my fragments in composable look like a larger change than i have bandwidth for at the moment.
s
You can also do it the other way around. Make those destinations normal fragment destinations which simply wrap a composable. You are most likely going to avoid all headache doing it this way. With that said,
the problem with the current default system backpress is that is does popBackStack on the app's navcontroller navgraph, which pops a fragment. And not the compose's navHostController's which pops a composable.
So you got a NavHost on your screen, yet the back is consumed by the fragment above it all? NavHost includes a BackHandler internally, I am not sure this is expected here.
i use backHandler to block user's backpress in the case where my composable is in a loading state
I am gonna be honest and say that this feels like a very frustrating experience, why do you do that?
and also to handle back press to display a compose dialog for user confirmation.
Does compose's
Dialog()
not do that for you automatically as well?
k
hmm indeed navhost internally has a predictiveBackHandler. I will need to check my code again tomorrow, thanks for the discussion @Stylianos Gakis
s
Yes, if it does not work at all and your outer NavController somehow snatches the back press there may perhaps be a bug which needs reporting. At least that's what I feel like, again I am not sure how well these things are supposed to work in this hybrid world. I think the official docs nudge you to pick one or the other, and not mix compose and non compose nav in the same app. At least for now before those compat APIs come to help you out.
k
agree, will test it out again, if its a bug definitely will file it, but 9/10 times the bug is me....
s
It's always us, such is life 😅