Introduction
Android Jetpack Compose has revolutionized how we build UI in Android applications, providing a modern, concise, and powerful approach. One essential aspect of building any non-trivial app is handling navigation between different screens. Jetpack Compose’s Navigation component provides a declarative way to handle navigation, offering features like navigation graphs and back stack management.
One useful feature in Jetpack Compose Navigation is the inclusive
attribute. Understanding when and how to use this attribute can help create more intuitive and user-friendly navigation experiences.
In this article, we’ll explore what the inclusive
attribute means, how it works, and look at practical examples of its usage in real-world applications.
What is the inclusive
Attribute?
In Jetpack Compose Navigation, the inclusive
attribute is used within the popUpTo
function. This attribute determines whether a particular destination should be removed from the back stack when navigating.
inclusive = true: The specified destination in the
popUpTo
call, along with all destinations added after it, will be removed from the back stack.inclusive = false: Only the destinations that come after the specified destination will be removed, leaving the specified destination itself on the stack.
This feature is particularly useful when you want to prevent users from returning to certain screens after navigating forward. It can help streamline user flows and ensure that users can’t go back to screens that don’t make sense to revisit.
Practical Use Cases
1. Login and Logout Flow
Consider an application with Login
, Home
, and Settings
screens. Once the user logs in successfully, we want to navigate them to the Home
screen and remove the Login
screen from the back stack. This prevents users from pressing the back button to return to the Login
screen, which would be confusing.
navController.navigate("home") {
popUpTo("login") { inclusive = true }
}
Here, navigating to the home
screen with inclusive = true
removes the login
screen from the back stack.
2. Multi-step Registration Process
Imagine a multi-step registration process: Step1 -> Step2 -> Step3 -> Summary
. After the user completes registration and reaches the Summary
screen, we want to remove all the previous steps from the back stack. This ensures users cannot go back to modify any step after submission.
navController.navigate("summary") {
popUpTo("step1") { inclusive = true }
}
This example pops all the way up to step1
and removes it, along with step2
and step3
.
3. Canceling an Order Flow
Consider an e-commerce app where a user can view Product -> Cart -> Checkout -> Confirmation
. If a user wants to cancel from the Checkout
screen and return to Product
, you can use the inclusive
attribute to clear all intermediary screens:
navController.navigate("product") {
popUpTo("cart") { inclusive = true }
}
This configuration ensures that both the cart
and checkout
screens are removed from the back stack.
4. Resetting the Application State
Sometimes, you may want to reset the application state entirely, like going back to the main screen of the app:
navController.navigate("main") {
popUpTo("main") { inclusive = true }
}
This example ensures all previous screens are cleared, returning the user to a fresh state at the main screen.
Practical Example: Building a Simple Compose Navigation Flow
Now, let’s build a practical example using Jetpack Compose Navigation, illustrating the use of the inclusive
attribute.
Project Structure
MainActivity.kt
: The main entry point containing navigation.NavGraph.kt
: Contains the navigation graph.LoginScreen.kt
,HomeScreen.kt
,ProfileScreen.kt
: Individual screens in the app.
1. Create a New Jetpack Compose Project
First, create a new Android project using Jetpack Compose. Add the necessary dependencies for Compose Navigation in your build.gradle
file:
dependencies {
implementation("androidx.navigation:navigation-compose:2.7.4")
}
2. Set Up MainActivity.kt
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.navigation.compose.rememberNavController
import com.example.composeapp.ui.theme.ComposeAppTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeAppTheme {
Surface(color = MaterialTheme.colorScheme.background) {
MainScreen()
}
}
}
}
}
@Composable
fun MainScreen() {
val navController = rememberNavController()
NavGraph(navController = navController)
}
3. Define the Navigation Graph in NavGraph.kt
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
@Composable
fun NavGraph(navController: NavHostController) {
NavHost(navController = navController, startDestination = "login") {
composable("login") { LoginScreen(navController) }
composable("home") { HomeScreen(navController) }
composable("profile") { ProfileScreen(navController) }
}
}
Job Offers
4. Create Screens
LoginScreen.kt
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
@Composable
fun LoginScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Login Screen")
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = {
// Navigate to Home and remove Login from the back stack
navController.navigate("home") {
popUpTo("login") { inclusive = true }
}
}) {
Text(text = "Login")
}
}
}
HomeScreen.kt
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
@Composable
fun HomeScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Home Screen")
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = {
// Navigate to the Profile screen
navController.navigate("profile")
}) {
Text(text = "Go to Profile")
}
}
}
ProfileScreen.kt
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
@Composable
fun ProfileScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Profile Screen")
Spacer(modifier = Modifier.height(20.dp))
Button(onClick = {
// Navigate back to Home
navController.navigate("home") {
popUpTo("home") { inclusive = true }
}
}) {
Text(text = "Back to Home")
}
}
}
Conclusion
The inclusive
attribute in Jetpack Compose Navigation provides a powerful mechanism to manage the back stack, ensuring users don’t navigate to unintended screens. By carefully designing navigation flows with attributes like inclusive
, you can create intuitive user experiences that prevent confusion and streamline application workflows.
As you build more complex applications, understanding these navigation techniques will become increasingly crucial. Experiment with different configurations to see how they fit into your app’s logic and requirements.
References
- https://developer.android.com/develop/ui/compose/navigation
- https://developer.android.com/develop/ui/compose/samples
Checkout here for full source code
This article is previously published on proandroiddev.com