The NavController
holds a "back stack" that contains the destinations the user
has visited. As the user navigates to screens throughout your app, the
NavController
adds and removes destinations to and from the back stack.
In being a stack, the back stack is a "last in, first out" data structure. The
NavController
therefore pushes items to and pops items from the top of the
stack.
Basic behavior
These are the core facts you should consider regarding the behavior of the back stack:
- First destination: When the user opens the app, the
NavController
pushes the first destination to the top of the back stack. - Pushing to the stack: Each call
NavController.navigate()
pushes the given destination to the top of the stack. - Popping top destination: Tapping Up or Back calls the
NavController.navigateUp()
andNavController.popBackStack()
methods, respectively. They pop the top destination off the stack. See the Principles of Navigation page for more information about the difference between Up and Back.
Pop back
The NavController.popBackStack()
method attempts to pop the current
destination off the back stack and navigate to the previous destination. This
effectively moves the user back one step in their navigation history. It returns
a boolean indicating whether it successfully popped back to the destination.
Pop back to a particular destination
You can also use popBackStack()
to navigate to a particular destination. To do
so, use one of its overloads. There are several that allow you to pass in an
identifier, such as an integer id
or a string route
. These overloads take
the user to the destination associated with the given identifier. Critically,
they pop everything on the stack above that destination.
These overloads also take an inclusive
boolean. It determines whether the
NavController
should also pop the specified destination off the back stack
after having navigated to it.
Consider this brief snippet for an example:
navController.popBackStack(R.id.destinationId, true)
Here the NavController
pops back to the destination with the integer id
destinationId
. As the value of the inclusive
argument is true
, the
NavController
also pops the given destination from the back stack.
Handle a failed pop back
When the popBackStack()
returns false
, a subsequent call to
NavController.getCurrentDestination()
returns null
. This means the app has
popped the last destination off the back stack. In this case, the user sees only
a blank screen.
This can occur in the following cases:
popBackStack()
did not pop anything from the stack.popBackStack()
did pop a destination off the back stack and the stack is now empty.
To resolve this, you must then navigate to a new destination or call finish()
on your activity to end it. The following snippet demonstrates this:
kotlin
...
if (!navController.popBackStack()) {
// Call finish() on your Activity
finish()
}
java
...
if (!navController.popBackStack()) {
// Call finish() on your Activity
finish();
}
Pop up to a destination
To remove destinations from the back stack when navigating from one destination
to another, add a popUpTo()
argument to the associated navigate()
function
call. popUpTo()
instructs the Navigation library to remove some destinations
from the back stack as part of the call to navigate()
. The parameter value is
the identifier of a destination on the back stack. The identifier can be an
integer id
or string route
.
You can include an argument for the inclusive
parameter with a value of true
to indicate that the destination you have specified in popUpTo()
should also
pop off back stack.
To implement this programmatically, pass popUpTo()
to navigate()
as part of
NavOptions
with inclusive
set to true
. This works in both Compose and
Views.
Save state when popping up
When you use popUpTo
to navigate to a destination, you can optionally save the
back stack and the states of all destinations popped off the back stack. You can
then restore the back stack and destinations when navigating to that destination
at a later time. This lets you preserve state for a given destination and have
multiple back stacks.
To do this programmatically, specify saveState = true
when adding popUpTo
to
your navigation options.
You can also specify restoreState = true
in your navigation options to
automatically restore the back stack and the state associated with the
destination.
For example:
navController.navigate(
route = route,
navOptions = navOptions {
popUpTo<A>{ saveState = true }
restoreState = true
}
)
To enable saving and restoring state in XML, define popUpToSaveState
as true
and restoreState
as true
respectively in the associated action
.
XML example
Here is an example of popUpTo
in XML, using an action:
<action
android:id="@+id/action_a_to_b"
app:destination="@id/b"
app:popUpTo="@+id/a"
app:popUpToInclusive="true"
app:restoreState=”true”
app:popUpToSaveState="true"/>
Compose example
The following is a complete example of the same in Compose:
@Composable
fun MyAppNavHost(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
startDestination: Any = A
) {
NavHost(
modifier = modifier,
navController = navController,
startDestination = startDestination
) {
composable<A> {
DestinationA(
onNavigateToB = {
// Pop everything up to, and including, the A destination off
// the back stack, saving the back stack and the state of its
// destinations.
// Then restore any previous back stack state associated with
// the B destination.
// Finally navigate to the B destination.
navController.navigate(route = B) {
popUpTo<A> {
inclusive = true
saveState = true
}
restoreState = true
}
},
)
}
composable<B> { DestinationB(/* ... */) }
}
}
@Composable
fun DestinationA(onNavigateToB: () -> Unit) {
Button(onClick = onNavigateToB) {
Text("Go to A")
}
}
More granularly, you can change how you call NavController.navigate()
in the
following ways:
// Pop everything up to the destination_a destination off the back stack before
// navigating to the "destination_b" destination
navController.navigate("destination_b") {
popUpTo("destination_a")
}
// Pop everything up to and including the "destination_a" destination off
// the back stack before navigating to the "destination_b" destination
navController.navigate("destination_b") {
popUpTo("destination_a") { inclusive = true }
}
// Navigate to the "search” destination only if we’re not already on
// the "search" destination, avoiding multiple copies on the top of the
// back stack
navController.navigate("search") {
launchSingleTop = true
}
For general information about passing options to NavController.navigate()
, see
the Navigate with options guide.
Pop using actions
When navigating using an action, you can optionally pop additional destinations off of the back stack. For example, if your app has an initial login flow, once a user has logged in, you should pop all of the login-related destinations off of the back stack so that the Back button doesn't take users back into the login flow.
Additional reading
For more information, read the following pages:
- Circular navigation: Learn how you can avoid an overstuffed back stack in cases where navigation flows are circular.
- Dialog destinations: Read about how dialog destinations introduce unique considerations to how you manage your back stack.