State of Compose 2023 results are in! Click here to learn more
Published on

How to navigate using Jetpack Compose Navigation with examples

Authors

This tutorial will cover how to implement Navigation in your Jetpack Compose application using the AndroidX Compose Navigation library.

Required dependency

Include the required dependency in your project's build.gradle file:

dependencies {
    def nav_version = "2.5.0"

    implementation "androidx.navigation:navigation-compose:$nav_version"
}

How to navigate between screens

Create a NavHost() and pass a NavController and the start route.

The NavController is used to perform navigation operations (such as navigate to the next screen or go back). Within the lambda function pass the available destinations (screens) of your app.

A basic app with a home screen and an about screen would look like this:

val navController = rememberNavController()
NavHost(navController, startDestination = "home") {
    composable("home") {
        HomeScreen(onAboutClick = {
            navController.navigate("about")
        })
    }
    composable("about") {
        AboutScreen()
    }
}

How to pass data across destinations

When defining the route of your destination, pass in the argument(s) required as part of the route. The arguments need to be defined in a url like fashion:

route/{argument1}/{argument2}

named arguments are also supported:

route/argument1={argument1}/argument2={argument2}

pass the arguments as parameters in your destination. Each navArgument() requires a name and the expected type.

An example of a details screen requiring an id would look like this:

composable("details/{id}",
    arguments = listOf(navArgument("id") {
        type = NavType.LongType
    })) {
    val id = requireNotNull(it.arguments).getLong("id")
    DetailsScreen(id)
}

navigate to the destination using the NavController:

val id = 1000L
navController.navigate("details/id=$id")

How to pass optional data across destinations

Any optional data needs to be defined as a query parameter. Include them in your route appending your required arguments with a ?:

route/{requiredArg1}?{optional}

additionally, mark the navArgument() as nullable:

composable(
    "details/{id}?{name}",
    arguments = listOf(
        navArgument("id") {
            type = NavType.LongType
        },
        navArgument("name") {
            type = NavType.StringType
            nullable = true
        }
    ),
) { backStackEntry ->
    val arguments = requireNotNull(backStackEntry.arguments)
    val id = arguments.getLong("id")
    val name = arguments.getString("name")
    DetailsScreen(id, name)
}

navigate to the destination using the NavController:

val id = 1000L
val name = "Alex"
navController.navigate("details/id=$id?name=$name")

How to navigate to the previous screen

Use the navController.popBackStack() to remove the current destination from the back stack. This will cause the app to navigate to the previous destination, if there is one.

Keep in mind that the Navigation library follows the Principles of Navigation and will never terminate your application. If you wish to finish your activity if you are in the top-level destination of your app, use the return value of popBackStack() and call finish() if the value is true.

if(navController.popBackStack().not()) {
    finish()
}

Checkout the Sample on Github

Check out the repository on Github and try it out on your machine: https://github.com/alexstyl/navigation-tutorial


  • Principles of Navigation: The Navigation library expects you to follow the Principles of Navigation while implementing. It is worth being aware of them so that you can understand its behavior and how to structure you graphs.
  • Compose Destinations: A library made by Rafael Costa that dramatically removes the boilerplate required for navigation.