{"id":3274,"date":"2023-06-08T17:06:04","date_gmt":"2023-06-09T00:06:04","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/surface-duo\/?p=3274"},"modified":"2023-06-08T17:06:04","modified_gmt":"2023-06-09T00:06:04","slug":"twopanelayout-navigation","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/surface-duo\/twopanelayout-navigation\/","title":{"rendered":"Improved navigation support in TwoPaneLayout"},"content":{"rendered":"<p>\n  Hello Jetpack Compose developers,\n<\/p>\n<p>\n  This week, we\u2019re excited to announce an update to our TwoPaneLayout library! You can now use <code>TwoPaneLayoutNav<\/code> with destinations that accept navigation arguments. \n<\/p>\n<p>\n  This new feature was originally suggested by <a href=\"https:\/\/techcommunity.microsoft.com\/t5\/surface-duo-sdk\/is-twopanebackstackentry-composable-list-of-routes-incompatible\/td-p\/3790655\">two developers in our feedback forum<\/a>, so special thanks to <a href=\"https:\/\/github.com\/tscholze\">@tscholze<\/a> and <a href=\"https:\/\/github.com\/Papes96\">@Papes96<\/a> for staying involved in our developer community and helping improve the SDK for everyone.\n<\/p>\n<h2>Add TwoPaneLayout to your project<\/h2>\n<p>\n  To import the new version of TwoPaneLayout, follow these steps:\n<\/p>\n<ol>\n<li>\n  Make sure you have the <code>mavenCentral()<\/code> repository in your top-level build.gradle file:<\/p>\n<pre>  allprojects {\r\n      repositories {\r\n          google()\r\n          mavenCentral()\r\n       }\r\n  }<\/pre>\n<\/li>\n<li>\n  Add dependencies to the module-level build.gradle file (current version may be different from what&#8217;s shown here):<\/p>\n<pre>  implementation \"com.microsoft.device.dualscreen:twopanelayout:1.0.1-alpha07\"<\/pre>\n<\/li>\n<li>\n  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:<\/p>\n<pre>  android { \r\n      compileSdkVersion 33\r\n   \r\n      defaultConfig { \r\n          targetSdkVersion 32\r\n      } \r\n  }<\/pre>\n<\/li>\n<li>\n  Build layout with TwoPaneLayoutNav and add destinations with arguments.\n<\/li>\n<\/ol>\n<h2>Navigate with arguments<\/h2>\n<p>\n  With this new update, you can continue using <code>TwoPaneLayoutNav<\/code> in the same way as before, so there\u2019s no need for any migration steps! You can continue using the same constructor for <code>TwoPaneLayoutNav<\/code>:\n<\/p>\n<pre>  @Composable\r\n  fun TwoPaneLayoutNav(\r\n      modifier: Modifier = Modifier,\r\n      navController: NavHostController,\r\n      paneMode: TwoPaneMode = TwoPaneMode.TwoPane,\r\n      singlePaneStartDestination: String,\r\n      pane1StartDestination: String,\r\n      pane2StartDestination: String,\r\n      builder: NavGraphBuilder.() -&gt; Unit\r\n  )<\/pre>\n<p>\n  The main difference now is in the content you can pass to the <code>NavGraphBuilder<\/code>. Previously, we only supported string literal routes for destinations, but now you can use <code>TwoPaneLayoutNav<\/code> like you would a typical <code>NavHost<\/code>, passing in arguments and specifying default values. This can all be done through the <a href=\"https:\/\/github.com\/microsoft\/surface-duo-compose-sdk\/blob\/main\/TwoPaneLayout\/library\/src\/main\/java\/com\/microsoft\/device\/dualscreen\/twopanelayout\/twopanelayoutnav\/TwoPaneBackStackEntry.kt#L42\">overloaded <code>composable<\/code> function<\/a> we introduced in version <code>1.0.1-alpha05<\/code>. An example usage would look like this:\n<\/p>\n<pre>  val navController = rememberNavController()\r\n\r\n  TwoPaneLayoutNav(\r\n      navController = navController,\r\n      singlePaneStartDestination = \"list\",\r\n      pane1StartDestination = \"list\",\r\n      pane2StartDestination = \"profile?name={name}\",\r\n  ) <strong>{<\/strong>\r\n  <strong>    <\/strong>composable(\"list\") <strong>{<\/strong>\r\n  <strong>        <\/strong>\/\/ list screen code\r\n      <strong>}<\/strong>\r\n  <strong>    <\/strong>composable(\r\n          route = \"detail\/{id}\/{name}\",\r\n          arguments = listOf(\r\n              navArgument(\"name\") <strong>{ <\/strong>type = NavType.StringType <strong>}<\/strong>,\r\n              navArgument(\"id\") <strong>{ <\/strong>type = NavType.IntType <strong>}<\/strong>)\r\n      ) <strong>{ <\/strong>backStackEntry <strong>-&gt;<\/strong>\r\n  <strong>        <\/strong>val id = backStackEntry.arguments?.getInt(\"id\")\r\n          val name = backStackEntry.arguments?.getString(\"name\")\r\n          \/\/ detail screen code\r\n      <strong>}<\/strong>\r\n  <strong>    <\/strong>composable(\r\n          route = \"profile?name={name}\",\r\n          arguments = listOf(navArgument(\"name\") <strong>{ <\/strong>defaultValue = \"guest\" <strong>}<\/strong>)\r\n      ) <strong>{<\/strong>\r\n  <strong>        <\/strong>val name = it.arguments?.getString(\"name\")\r\n          \/\/ profile screen code\r\n      <strong>}<\/strong>\r\n  <strong>}<\/strong><\/pre>\n<p>\n  In the code snippet, you can see how we use three different destination formats:\n<\/p>\n<ol>\n<li>\n  Without arguments &#8211; <code>list<\/code>\n<\/li>\n<li>\n  With multiple typed arguments &#8211; <code>detail\/{id}\/{name}<\/code>\n<\/li>\n<li>\n  With an optional argument and default value &#8211; <code>profile?name={name}<\/code>\n<\/li>\n<\/ol>\n<p>\n  The result of this setup can be in the animation below, where the <code>id<\/code> and <code>name<\/code> arguments are passed through to the <code>detail<\/code> destination, and the <code>profile<\/code> destination uses the <code>name<\/code> argument if present, otherwise relying on the default argument value.\n<\/p>\n<p>\n  <img decoding=\"async\" width=\"800\" height=\"596\" src=\"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-content\/uploads\/sites\/53\/2023\/06\/word-image-3274-2.gif\" class=\"wp-image-3276\" \/>\n<\/p>\n<p>\n  The implementation for the example shown in the animation can be found in <a href=\"https:\/\/github.com\/microsoft\/surface-duo-compose-sdk\/pull\/62\">this PR in the compose-sdk repo<\/a>.\n<\/p>\n<p>\n  To learn more about the different navigation argument options that are available for a <code>NavHost<\/code> and <code>TwoPaneLayoutNav<\/code>, check out the <a href=\"https:\/\/developer.android.com\/jetpack\/compose\/navigation\">Navigating with Compose documentation page.<\/a>\n<\/p>\n<h2>Resources and feedback<\/h2>\n<p>\n  The code for <code>TwoPaneLayout<\/code> is available on GitHub in our <a href=\"https:\/\/github.com\/microsoft\/surface-duo-compose-sdk\">Compose SDK repo<\/a>.  \n<\/p>\n<p>\n  If you have any questions, use the <a href=\"http:\/\/aka.ms\/SurfaceDuoSDK-Feedback\">feedback forum<\/a> or message us on <a href=\"https:\/\/twitter.com\/surfaceduodev\">Twitter @surfaceduodev<\/a>.\n<\/p>\n<p>\n  There will be no livestream this week, but you can check out the <a href=\"https:\/\/youtube.com\/c\/surfaceduodev\">archives on YouTube<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello Jetpack Compose developers, This week, we\u2019re 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 [&hellip;]<\/p>\n","protected":false},"author":72597,"featured_media":3275,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[717,692],"class_list":["post-3274","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-surface-duo-sdk","tag-foldable","tag-jetpack-compose"],"acf":[],"blog_post_summary":"<p>Hello Jetpack Compose developers, This week, we\u2019re 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 [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/3274","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/users\/72597"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/comments?post=3274"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/posts\/3274\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media\/3275"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/media?parent=3274"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/categories?post=3274"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/surface-duo\/wp-json\/wp\/v2\/tags?post=3274"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}