Reputation: 499
I'm implementing the Logout action in an application. I want that when user clicks Logout, go to Login. When the user takes this path: Login -> Home -> Settings (where he clicks on Logout) -> Login, when I press back, the app goes to the background and closes, which is the behavior I want.
However, when the user takes this route: Login -> Home -> ScreenOne -> ScreenTwo -> Home -> ScreenOne -> Settings (where you click on Logout) -> Login, when you press back, it goes back to Settings and if you press it again it goes to ScreenOne and so on .
That's the way I do the navigation to Login when I click on Logout:
navController.navigate(NavigationItem.Login.route) {
popUpTo(NavigationItem.Login.route) {
inclusive = true
}
}
Note: Already tried Navigation.Home.route as parameter on popUpTo.
I don't know if is related, but that's the way I do the navigation between Home -> ScreenOne -> ScreenTwo -> Home -> ScreenOne:
navController.navigate(item.route) {
navController.graph.startDestinationRoute?.let { route ->
popUpTo(route = route) {
saveState = true
}
}
launchSingleTop = true
restoreState = true
}
Does anyone knows how I can clear the back stack or guarantee that, in the second behavior, when I am on Login screen after Logout and I press "Back", the app goes to second plan?
EDIT: Added NavHost structure.
@Composable
@ExperimentalFoundationApi
@ExperimentalComposeUiApi
@ExperimentalMaterialApi
fun Navigation(navController: NavHostController, updateBottomBarVisibility: (Boolean) -> Unit) {
NavHost(
navController = navController,
startDestination = NavigationItem.Login.route
) {
composable(route = NavigationItem.Login.route) {
LoginScreen(navController)
}
composable(route = NavigationItem.Events.route) {
EventsScreen(updateBottomBarVisibility, navController)
}
composable(route = NavigationItem.Home.route) {
HomeScreen(updateBottomBarVisibility, navController)
}
composable(route = NavigationItem.Prizes.route) {
PrizesScreen(updateBottomBarVisibility, navController)
}
composable(route = NavigationItem.Account.route) {
AccountScreen(navController)
}
}
}
Upvotes: 5
Views: 6087
Reputation: 131
I know you might have figured it out, but for someone else still in this situation:
In your NavHost
set startDestination
to NavigationItem.Home.route
. This will ensure that at any given instance, cold boot or warm boot of your application will launch your Home screen, and when a user clicks the back button, the app will exit.
Now to solve your problem, you can have a condition at the start of your home screen that checks whether the user is logged in or not. If logged in, proceed (i.e., do nothing, the back button will be taken care of by the above NavHost
), else, pop the back stack then navigate the user to Login.
navController.popBackStack()
navController.navigateSingleTopTo(NavigationItem.Login.route)
The navigateSingleTopTo
is as below:
fun NavHostController.navigateSingleTopTo(route: String) =
this.navigate(route) {
// Ensures there will be at most one copy of a given destination on
// the top of the back stack i.e. re-tapping the same tab multiple
// times doesn't launch multiple copies of the same destination
launchSingleTop = true
}
Since you popped the back stack, clicking on the back button here will always kill the app (for a non-logged in user).
For Logout, since you want the user to be taken to the Login screen irrespective of their current destination, pop up to your Start Destination (in your case Home screen), then navigate to Login screen, as below:
navController.navigate(NavigationItem.Login.route) {
popUpTo(navController.graph.findStartDestination().id) {
inclusive = true
}
}
This will mean at that particular moment when a user clicks the back button they kill the app.
You will lastly need to update the condition you were checking in the Home screen so that the app can now know user has logged out. This means if the user perform a warm boot (i.e., kills and opens the app), Login will be launched, upon which clicking the back button will kill the app, as expected.
Let me know if it helps your case.
Happy coding!
Upvotes: 0
Reputation: 675
When you are in settings logout
:
navController.popBackStack(NavigationItem.Login.route, inclusive = false, saveState = false)
is not necessary to save the state since once the use is out has to login again with new updated data.
At this point if the user click back the app should go in foreground.
If this doesn't work is possible that Login screen is not on the backstack. I would try to add a route to the NavHost and popBack to it with exclusion and save state off.
Otherwise might be that you are invoking navigate method more times thank expected
fun Navigation(navController: NavHostController, updateBottomBarVisibility: (Boolean) -> Unit) {
NavHost(
route = "nav_host_route"
navController = navController,
startDestination = NavigationItem.Login.route
) {}
//navigate to top destinations like HOME
navController.navigate(item.route) {
popUpTo(route = "nav_host_route") {
inclusive = false
saveState = true
}
launchSingleTop = true
restoreState = true
}
log out:
navController.navigate(LogIn){
popUpTo("nav_host_route"){
saveState = false
inclusive = false
}
restoreState = false
launchSingleTop = true
}
Upvotes: 8
Reputation: 488
To close the app on backpress you can use the following piece of code in your "Log In" screen composable:
// Getting your activity in a composable function
val activity = (LocalContext.current as? Activity)
// Everything put inside this block will be done on each system backpress
BackHandler {
activity?.finish()
}
Upvotes: 0