Improved navigation support in TwoPaneLayout

Kristen Halper

Hello Jetpack Compose developers,

This week, we’re excited to announce an update to our TwoPaneLayout library! You can now use TwoPaneLayoutNav with destinations that accept navigation arguments.

This new feature was originally suggested by two developers in our feedback forum, so special thanks to @tscholze and @Papes96 for staying involved in our developer community and helping improve the SDK for everyone.

Add TwoPaneLayout to your project

To import the new version of TwoPaneLayout, follow these steps:

  1. Make sure you have the mavenCentral() repository in your top-level build.gradle file:

      allprojects {
          repositories {
  2. Add dependencies to the module-level build.gradle file (current version may be different from what’s shown here):

      implementation ""
  3. Also ensure the compileSdkVersion is set to API 33 and the targetSdkVersion is set to API 32 or newer in the module-level build.gradle file:

      android { 
          compileSdkVersion 33
          defaultConfig { 
              targetSdkVersion 32
  4. Build layout with TwoPaneLayoutNav and add destinations with arguments.

With this new update, you can continue using TwoPaneLayoutNav in the same way as before, so there’s no need for any migration steps! You can continue using the same constructor for TwoPaneLayoutNav:

  fun TwoPaneLayoutNav(
      modifier: Modifier = Modifier,
      navController: NavHostController,
      paneMode: TwoPaneMode = TwoPaneMode.TwoPane,
      singlePaneStartDestination: String,
      pane1StartDestination: String,
      pane2StartDestination: String,
      builder: NavGraphBuilder.() -> Unit

The main difference now is in the content you can pass to the NavGraphBuilder. Previously, we only supported string literal routes for destinations, but now you can use TwoPaneLayoutNav like you would a typical NavHost, passing in arguments and specifying default values. This can all be done through the overloaded composable function we introduced in version 1.0.1-alpha05. An example usage would look like this:

  val navController = rememberNavController()

      navController = navController,
      singlePaneStartDestination = "list",
      pane1StartDestination = "list",
      pane2StartDestination = "profile?name={name}",
  ) {
      composable("list") {
          // list screen code
          route = "detail/{id}/{name}",
          arguments = listOf(
              navArgument("name") { type = NavType.StringType },
              navArgument("id") { type = NavType.IntType })
      ) { backStackEntry ->
          val id = backStackEntry.arguments?.getInt("id")
          val name = backStackEntry.arguments?.getString("name")
          // detail screen code
          route = "profile?name={name}",
          arguments = listOf(navArgument("name") { defaultValue = "guest" })
      ) {
          val name = it.arguments?.getString("name")
          // profile screen code

In the code snippet, you can see how we use three different destination formats:

  1. Without arguments – list
  2. With multiple typed arguments – detail/{id}/{name}
  3. With an optional argument and default value – profile?name={name}

The result of this setup can be in the animation below, where the id and name arguments are passed through to the detail destination, and the profile destination uses the name argument if present, otherwise relying on the default argument value.

The implementation for the example shown in the animation can be found in this PR in the compose-sdk repo.

To learn more about the different navigation argument options that are available for a NavHost and TwoPaneLayoutNav, check out the Navigating with Compose documentation page.

Resources and feedback

The code for TwoPaneLayout is available on GitHub in our Compose SDK repo.

If you have any questions, use the feedback forum or message us on Twitter @surfaceduodev.

There will be no livestream this week, but you can check out the archives on YouTube.


Discussion is closed.

Feedback usabilla icon