I have written the code below, to try to, first clear everything and then navigate back to the very first screen. When I tap the button, it first goes back to the previous screen then when I go forward and tap again it goes to the Main screen.
rootNavHostController?.navigate("Main"){
popUpTo(rootNavHostController.graph.findStartDestination().id){
inclusive = true
}
}
Why is this strange behavior. And what can I do to resolve it?
EDIT: Even the code below gives the same behavior, it is like, it first in the stack goes one step back and then afterwards navigates to the correct screen. How can I clear everything and navigate directly to the Main screen?
rootNavHostController?.navigate(Graph.ENTRY){
popUpTo(rootNavHostController?.currentBackStackEntry?.destination?.route.toString()){
inclusive = true
}
}
Related
I am implementing a login system with Jetpack Compose and I'm using Compose Navigation. I have an "onboarding" screen where the user can choose whether they want to login or sign up. Fromn both the login and sign up screen the user can navigate to the other one, but let's say they keep navigating back and forth, then the back stack is full of those screens.
How should I handle the back stack in this case? I thought about checking if the destination is already present in the back stack and pop to that, but it looks like this has some pitfalls (the user could have visited one of those pages before and pop several destinations for example).
I'd also like to pop the whole login flow when done but I don't get how
How should I handle the back stack in this case? I thought about
checking if the destination is already present in the back stack and
pop to that
In a way you're right, but you don't need to do much of it yourself if you have circular navigation (this is what you describe). You can just use popUpTo from the signup page (if the login is supposed to be the "default" like such:
navController.navigate(loginRoute) {
popUpTo(loginRoute) {
inclusive = true
}
}
The inclusive = true is necessary because you also want to pop the login from the stack itself:
If your stack looks like AB and you navigate back to A, you would pop just B and be left with AA (thus able to navigate back to A again while you're at A).
I'd also like to pop the whole login flow when done but I don't get how
Similar to your initial idea, just pop the entire backstack after you're done using navController.navigate(yourRoute) { popUpTo(0) }
Some crashes have popped out on my Firebase's crashlytics with the exception
Fatal Exception: java.lang.IllegalArgumentException Navigation ACTION_XYZ cannot be found from the current destination
I've debugged this case and found out the problem:
I have a button which navigates from Fragment A to Fragment B and it works good.
But when you quickly click the button two times - first it navigates correctly, then tries to navigate again, thus the exception.
How should one avoid such a bug? I could just silently catch the exception from the button's click but that looks like a code smell to me.
I also could disable the button after the first click, but I'm wondering if there is a more elegant and cleaner way to avoid double navigation to FragmentB?
Thanks for all the answers, cheers!
Before navigating to any screen, you can check whether we have action to that particular destination from the current screen. Something like below. You can use this extension function to navigate safely to any destination.
fun NavController.navigateSafe(directions: NavDirections, navOptions: NavOptions? = null) {
currentDestination?.getAction(directions.actionId)?.let {
navigate(directions, navOptions)
}
}
I've got an xamarin forms/prism app, and my hardware back button does nothing on the initial page.
If I navigate to another page, it closes the app as expected. If I navigate to the initial page again, it also closes the app - but not if the app just started.
Is there something I'm missing?
My class App mainly has an OnInitialized that navigates to the initial page:
protected override void OnInitialized()
{
NavigationService.NavigateAsync( "MyMasterDetail/MyNavigationPage/StartPage", animated: false );
}
On MyMasterDetail, there are buttons that navigate to MyNavigationPage/SettingsPage and other pages like that.
It doesn't matter if I use Android 5 in Emulator or Android 6 on a real device, the behaviour is the same.
When using a MasterDetail as your root, you are not actually navigating anywhere else. You are simply changing the Detail property of the MasterDetail to another Page. This is not a navigation action. So you are not really navigating. If you want to fake it, you need to add the INavigationPageOptions to your MyNavigationPage and set the ClearNavigationStackOnNavigation property to false. This will continuously push new pages onto the MasterDetailPage.Detail MyNavigationPage without clearing the stack (PopToRoot). Then this will allow your bac button to behave like you are wanting.
I'm having problems coming up with a solution to this problem.
Basically I have a load of tabs in my ActionBar. When each is touched the fragments from the previous tab are detached and the fragments for the new tab are added using replace (if they haven't been instantiated yet) or attached (if they have). I think I got this method from Google and it was working fine until now.
Example of adding a tab's fragments:
if(tab.getText().equals(context.getString(R.string.title_class_tab))) {
if(browser == null) {
browser = CourseBrowserFragment.newInstance(false);
fragmentTransaction.replace(leftContainerId, browser);
} else {
fragmentTransaction.attach(browser);
}
if(lessonViewer == null) {
lessonViewer = LessonViewerFragment.newInstance(false);
fragmentTransaction.replace(rightContainerId, lessonViewer);
} else {
fragmentTransaction.attach(lessonViewer);
}
}
and removing:
if(tab.getText().equals(context.getString(R.string.title_class_tab))) {
if(browser != null) {
fragmentTransaction.detach(browser);
}
if(lessonViewer != null) {
fragmentTransaction.detach(lessonViewer);
}
}
The problem arises from the layout I need for one of the tabs. Basically it's like the Gmail app. There are two fragments (let's say Panel A and Panel B) and when you push a button Panel A slides out, Panel B slides to Panel A's old position and a new, third one (Panel C) slides in from the right.
I had this working fine but now I've added the sliding-in FragmentTransaction to the back stack so that the user can touch the back button and Panel C will slide back out and Panel A will come back. Again, like Gmail.
Except when the user goes to a different tab this transaction is still on the back stack and executes if the user presses back. The fragments end up in crazy places. What I need to do is remove it from the back stack when the user navigates to a different tab. Is there any way I can do this? FragmentManager doesn't seem to let you manually remove things from the back stack and using the popBackStack() method doesn't just remove the transaction, it executes it. I want to remove it when the user navigates away and put it back when the user returns.
I think I can get a hold of the "Back Stack Entry" for this transaction using "getBackStackEntryAt" but it's not much good if I can't remove it and put it back in place when the user comes back to the tab.
The only possible solution I can think of is not using the back stack and overriding onBackButtonPressed instead. From there I could just do a reverse of the transaction if necessary.
Thanks for any help and sorry if I'm being incoherent.
Not sure if this would qualify as a solution but I ended up just not adding the transaction to the back stack and just doing a fresh transaction when the user swiped or pressed back. The transaction just did the reverse of the original one with animations etc.
The way I managed the back button is I set a boolean to true if I was in the layout showing Panel C. If the user swipes back into the Panel A layout or navigates away the boolean is set to false. I then overrode the onBackButtonPressed method in the Activity and if the boolean was true (ie: we're in the Panel C layout) I run that reverse transaction otherwise I just call super.onBackButtonPressed() (ie: perform standard back button behaviour).
Here's my two scenarios.
1 -
User opens app for the first time ever from android home screen
User is presented with "first time" screen (backed by first time activity, lets call it A)
User hits back button
user is returned to android home screen
2 -
User opens app for second time
User is presented with the main list screen of the application (backed by a list activity, let's call it B)
User hits back button
User is returned to android home screen
I'm already aware of numerous ways to detect whether it's the first time opening the app or not.
The problem is in having the back button return to the home screen rather than a routing activity that decides which screen to forward to.
Currently My app has an activity to decide where to route (lets call it R) the problem is, My stack either looks like R -> A or R -> B
I want A or B to replace R on the stack when they open, and if the user hits back, then they go to the android home screen, not back to R.
Having a collaborator that sets the view for A and B is also not really workable as B extends androids concrete implementation of a list Activity to get most of its functionality.
Any ideas?
I want A or B to replace R on the
stack when they open, and if the user
hits back, then they go to the android
home screen, not back to R.
Call finish() in R after it calls startActivity() to trigger the opening of A or B.