https://kotlinlang.org logo
#compose
Title
# compose
a

Adib Faramarzi

08/10/2021, 9:55 AM
Is there a way to simulate activity's
back pressed
state for compose-navigation?
popBackStack
does not finish navigation (and instead just removes the destination) but pressing the back key always works perfectly. This becomes a problem when you have nested navigation as the inner navigation stacks can never pop themselves
For example, If in the scenario below, you are in the screen C and you pop the back stack, you will see a white screen (because C has been popped off, but it has not been navigated back to A). If you press the back button, it works fine and you will see A. • A • B ◦ C ◦ D
n

nitrog42

08/10/2021, 10:00 AM
it kinda looks like a bug 🤔
which version of navigation-compose are you using ?
a

Adib Faramarzi

08/10/2021, 10:01 AM
Copy code
2.4.0-alpha06
n

nitrog42

08/10/2021, 10:01 AM
can you try with alpha04 to check if the same happens ? (I believe alpha05 had some critical bugs)
a

Adib Faramarzi

08/10/2021, 10:02 AM
Yes
I hope it doesn't conflict with accompanist's navigation
It seems to be present in alpha-04 too.
n

nitrog42

08/10/2021, 10:05 AM
you should probably open a bug here then : https://issuetracker.google.com/issues?q=componentid:409828%20status:open , with a reproducible sample if you can
i

Ian Lake

08/10/2021, 1:52 PM
The docs (https://developer.android.com/guide/navigation/navigation-navigate#back-stack) do specifically state:
NavController.popBackStack() returns a boolean indicating whether it successfully popped back to another destination. The most common case when this returns false is when you manually pop the start destination of your graph.
When the method returns false, NavController.getCurrentDestination() returns null. You are responsible for either navigating to a new destination or handling the pop by calling finish() on your Activity
So it sounds like you'll want to use
false
as your signal to call
popBackStack()
on your outer NavController
a

Adib Faramarzi

08/10/2021, 1:53 PM
Thanks
This makes nested navigation very hard though
Is there a way to access a parent nav controller within one?
i

Ian Lake

08/10/2021, 1:56 PM
Taking a step back, nested nav hosts are rarely actually the right answer, as we've talked about before https://kotlinlang.slack.com/archives/CJLTWPH7S/p1627490432392100?thread_ts=1627477759.324500&cid=CJLTWPH7S
In Compose, state goes down the tree and events go up - the thing manually calling
popBackStack()
should also be taking a lambda to trigger when that returns
false
- that would be how you'd encapsulate the access to the outer NavController
Maybe if you explain your use case, we can help build a better architecture for what you are trying to do?
a

Adib Faramarzi

08/10/2021, 2:00 PM
Thanks for the info. Will check shortly. I totally agree about the events going up (in which, this case is in violation of by calling
popBackStack()
) I have a back button in the inner-navigation page which should pop its page. Currently it is using
popBackStack
. I think what might be better is passing a
onBackPressed
into the function that is rendering this page (which is a
NavHost
and handle the back there (pop the backstack of the parent nav controller).
i

Ian Lake

08/10/2021, 2:04 PM
Yeah, if you're taking the events as lambdas thing to heart, none of your screens should have access to a NavController at all (as per our testing docs: https://developer.android.com/jetpack/compose/navigation#testing) so it would be really easy to pass a different lambda to the start destination of that inner graph that just triggers a lambda passed from the outer NavHost
a

Adib Faramarzi

08/10/2021, 2:05 PM
That's correct. Thanks 🙂
i

Ian Lake

08/10/2021, 2:06 PM
But again, I'd verify if a nested nav host is actually the right architecture choice in the first place - it is not the right choice if you're just using it to hide/show global UI ala a bottom nav, app bar, etc
a

Adib Faramarzi

08/10/2021, 2:07 PM
The back button is not general (it exists only in the page)
n

nitrog42

08/10/2021, 2:40 PM
I might misunderstood the initial issue, but I believe it was : • one navhost • a main graph containing a subgraph • when on a destination of the subgraph, using navController.popBackStack won't return the current destination to the main graph and display a blank screen
let me check 😄 edit : Yeah I misunderstood, I thought the use case was this :
Copy code
NavHost(navController = navController, startDestination = nav_home) {
    composable("home") {
        Text("Home")
    }
    navigation(startDestination = "profile/", route = "profile") {
        composable("profile/") {
            Button(onClick = { navController.popBackStack() }) {
                Text("Should go to home") // But display blank page instead
            }
        }
    }
}
c

Colton Idle

08/10/2021, 3:17 PM
Ian, I had a similar question to this that I was able to answer myself, but maybe I'll tag it along here. I have ScreenA and ScreenB. Screen B has an up button. ScreenB also has a pager with 5 pages and my UX person wants every touch of the up button to move back through the view pager if I'm on page > 1.
Copy code
val dispatcher = LocalOnBackPressedDispatcherOwner.current!!.onBackPressedDispatcher

BackHandler(enabled = pagerState.currentPage > 0) {
    viewModel.updateCurrentPage(pagerState.currentPage.minus(1))
    scope.launch { pagerState.animateScrollToPage(pagerState.currentPage.minus(1)) }
}
And my top bar is declared like this
Copy code
topBar = {
    MyTopAppBar(navBackIconClicked = { dispatcher.onBackPressed() })
@Adib Faramarzi maybe you'll find this useful.
i

Ian Lake

08/10/2021, 3:25 PM
Seems reasonable, but note that your top app bar will do a lot more than just trigger page changes if any page adds their own
BackHandler
. Just double check that behavior is actually what you want or if you want to bypass those to only swap back pages, no matter what internal BackHandlers exist (i.e., make the signal explicit as we've been talking about)
👍 1
340 Views