From 9eb0e05885ae3f1568ab1aafced974dfea6721dc Mon Sep 17 00:00:00 2001 From: Rob Orgiu Date: Mon, 3 Nov 2025 13:33:39 +0100 Subject: [PATCH 01/12] Remove TwoPaneScene in favor of List-Detail --- .../navigation3/scenes/ScenesSnippets.kt | 158 +++++++++--------- 1 file changed, 82 insertions(+), 76 deletions(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt index ab160b1d4..892298468 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt @@ -19,12 +19,11 @@ package com.example.compose.snippets.navigation3.scenes import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.Button -import androidx.compose.material3.Text import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.navigation3.runtime.NavBackStack import androidx.navigation3.runtime.NavEntry import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.entryProvider @@ -35,6 +34,8 @@ import androidx.navigation3.scene.SceneStrategyScope import androidx.navigation3.ui.NavDisplay import androidx.window.core.layout.WindowSizeClass import androidx.window.core.layout.WindowSizeClass.Companion.WIDTH_DP_MEDIUM_LOWER_BOUND +import com.example.compose.snippets.navigation3.scenes.ListDetailScene.Companion.DETAIL_KEY +import com.example.compose.snippets.navigation3.scenes.ListDetailScene.Companion.LIST_KEY import kotlinx.serialization.Serializable interface SceneExample { @@ -73,87 +74,84 @@ public class SinglePaneSceneStrategy : SceneStrategy { // [END android_compose_navigation3_scenes_2] // [START android_compose_navigation3_scenes_3] -// --- TwoPaneScene --- +// --- ListDetailScene --- /** - * A custom [Scene] that displays two [NavEntry]s side-by-side in a 50/50 split. + * A [Scene] that displays a list and a detail [NavEntry] side-by-side in a 40/60 split. + * */ -class TwoPaneScene( +class ListDetailScene( override val key: Any, override val previousEntries: List>, - val firstEntry: NavEntry, - val secondEntry: NavEntry + val listEntry: NavEntry, + val detailEntry: NavEntry, ) : Scene { - override val entries: List> = listOf(firstEntry, secondEntry) + override val entries: List> = listOf(listEntry, detailEntry) override val content: @Composable (() -> Unit) = { Row(modifier = Modifier.fillMaxSize()) { - Column(modifier = Modifier.weight(0.5f)) { - firstEntry.Content() + Column(modifier = Modifier.weight(0.4f)) { + listEntry.Content() } - Column(modifier = Modifier.weight(0.5f)) { - secondEntry.Content() + Column(modifier = Modifier.weight(0.6f)) { + detailEntry.Content() } } } companion object { - internal const val TWO_PANE_KEY = "TwoPane" + internal const val LIST_KEY = "ListDetailScene-List" + internal const val DETAIL_KEY = "ListDetailScene-Detail" + + /** + * Helper function to add metadata to a [NavEntry] indicating it can be displayed + * as a list in the [ListDetailScene]. + */ + fun listPane() = mapOf(LIST_KEY to true) + /** * Helper function to add metadata to a [NavEntry] indicating it can be displayed - * in a two-pane layout. + * as a list in the [ListDetailScene]. */ - fun twoPane() = mapOf(TWO_PANE_KEY to true) + fun detailPane() = mapOf(DETAIL_KEY to true) } } @Composable -fun rememberTwoPaneSceneStrategy(): TwoPaneSceneStrategy { +fun rememberListDetailSceneStrategy(): ListDetailSceneStrategy { val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass return remember(windowSizeClass) { - TwoPaneSceneStrategy(windowSizeClass) + ListDetailSceneStrategy(windowSizeClass) } } -// --- TwoPaneSceneStrategy --- +// --- ListDetailSceneStrategy --- /** - * A [SceneStrategy] that activates a [TwoPaneScene] if the window is wide enough - * and the top two back stack entries declare support for two-pane display. + * A [SceneStrategy] that returns a [ListDetailScene] if the window is wide enough + * and the last two back stack entries are list and detail. */ -class TwoPaneSceneStrategy(val windowSizeClass: WindowSizeClass) : SceneStrategy { +class ListDetailSceneStrategy(val windowSizeClass: WindowSizeClass) : SceneStrategy { + override fun SceneStrategyScope.calculateScene(entries: List>): Scene? { - // Condition 1: Only return a Scene if the window is sufficiently wide to render two panes. - // We use isWidthAtLeastBreakpoint with WIDTH_DP_MEDIUM_LOWER_BOUND (600dp). + if (!windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_MEDIUM_LOWER_BOUND)) { return null } - val lastTwoEntries = entries.takeLast(2) - - // Condition 2: Only return a Scene if there are two entries, and both have declared - // they can be displayed in a two pane scene. - return if (lastTwoEntries.size == 2 && - lastTwoEntries.all { it.metadata.containsKey(TwoPaneScene.TWO_PANE_KEY) } - ) { - val firstEntry = lastTwoEntries.first() - val secondEntry = lastTwoEntries.last() - - // The scene key must uniquely represent the state of the scene. - val sceneKey = Pair(firstEntry.contentKey, secondEntry.contentKey) - - TwoPaneScene( - key = sceneKey, - // Where we go back to is a UX decision. In this case, we only remove the top - // entry from the back stack, despite displaying two entries in this scene. - // This is because in this app we only ever add one entry to the - // back stack at a time. It would therefore be confusing to the user to add one - // when navigating forward, but remove two when navigating back. - previousEntries = entries.dropLast(1), - firstEntry = firstEntry, - secondEntry = secondEntry - ) - } else { - null - } + val detailEntry = + entries.lastOrNull()?.takeIf { it.metadata.containsKey(DETAIL_KEY) } ?: return null + val listEntry = entries.findLast { it.metadata.containsKey(LIST_KEY) } ?: return null + + // We use the list's contentKey to uniquely identify the scene. + // This allows the detail panes to be animated in and out by the scene, rather than + // having NavDisplay animate the whole scene out when the selected detail item changes. + val sceneKey = listEntry.contentKey + + return ListDetailScene( + key = sceneKey, + previousEntries = entries.dropLast(1), + listEntry = listEntry, + detailEntry = detailEntry + ) } } // [END android_compose_navigation3_scenes_3] @@ -161,44 +159,52 @@ class TwoPaneSceneStrategy(val windowSizeClass: WindowSizeClass) : Scen // [START android_compose_navigation3_scenes_4] // Define your navigation keys @Serializable -data object ProductList : NavKey +data object ConversationList : NavKey + @Serializable -data class ProductDetail(val id: String) : NavKey +data class ConversationDetail( + val id: Int, + val colorId: Int +) : NavKey + +@Serializable data object Profile : NavKey @Composable fun MyAppContent() { - val backStack = rememberNavBackStack(ProductList) + val backStack = rememberNavBackStack(ConversationList) + val listDetailStrategy = rememberListDetailSceneStrategy() NavDisplay( backStack = backStack, + onBack = { backStack.removeLastOrNull() }, + sceneStrategy = listDetailStrategy, entryProvider = entryProvider { - entry( - // Mark this entry as eligible for two-pane display - metadata = TwoPaneScene.twoPane() - ) { key -> - Column { - Text("Product List") - Button(onClick = { backStack.add(ProductDetail("ABC")) }) { - Text("View Details for ABC (Two-Pane Eligible)") - } - } + entry( + metadata = ListDetailScene.listPane() + ) { + // Add new Detail with backStack.addDetail(detailRoute) } - - entry( - // Mark this entry as eligible for two-pane display - metadata = TwoPaneScene.twoPane() - ) { key -> - Text("Product Detail: ${key.id} (Two-Pane Eligible)") + entry( + metadata = ListDetailScene.detailPane() + ) { + // Show Detail Composable } - // ... other entries ... - }, - // Simply provide your custom strategy. NavDisplay will fall back to SinglePaneSceneStrategy automatically. - sceneStrategy = rememberTwoPaneSceneStrategy(), - onBack = { - if (backStack.isNotEmpty()) { - backStack.removeLastOrNull() + entry { + // Profile Composable } } ) } + +// Manage List-Detail backstack +private fun NavBackStack.addDetail(detailRoute: ConversationDetail) { + + // Remove any existing detail routes + removeIf { it is ConversationDetail } + + // Avoid adding the same detail route to the back stack twice. + if (!contains(detailRoute)) { + add(detailRoute) + } +} // [END android_compose_navigation3_scenes_4] From 3714927a4a3192496706c777af5d0bd8ae75d4aa Mon Sep 17 00:00:00 2001 From: Roberto Orgiu Date: Mon, 3 Nov 2025 13:44:59 +0100 Subject: [PATCH 02/12] Update compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt Co-authored-by: Don Turner --- .../compose/snippets/navigation3/scenes/ScenesSnippets.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt index 892298468..c9ab16ad2 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt @@ -142,7 +142,7 @@ class ListDetailSceneStrategy(val windowSizeClass: WindowSizeClass) : S val listEntry = entries.findLast { it.metadata.containsKey(LIST_KEY) } ?: return null // We use the list's contentKey to uniquely identify the scene. - // This allows the detail panes to be animated in and out by the scene, rather than + // This allows the detail panes to be displayed instantly through recomposition, rather than // having NavDisplay animate the whole scene out when the selected detail item changes. val sceneKey = listEntry.contentKey From d2e2ed68b192361cdaf53c46f821a615ce4dff84 Mon Sep 17 00:00:00 2001 From: Roberto Orgiu Date: Mon, 3 Nov 2025 13:45:19 +0100 Subject: [PATCH 03/12] Update compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt Co-authored-by: Don Turner --- .../compose/snippets/navigation3/scenes/ScenesSnippets.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt index c9ab16ad2..cddfaba7a 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt @@ -201,7 +201,7 @@ private fun NavBackStack.addDetail(detailRoute: ConversationDetail) { // Remove any existing detail routes removeIf { it is ConversationDetail } - +add(detailRoute) // Avoid adding the same detail route to the back stack twice. if (!contains(detailRoute)) { add(detailRoute) From 4a335d3960bbbaa53ec995c2d0b59a90ce2418f4 Mon Sep 17 00:00:00 2001 From: Roberto Orgiu Date: Mon, 3 Nov 2025 13:45:30 +0100 Subject: [PATCH 04/12] Update compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt Co-authored-by: Don Turner --- .../compose/snippets/navigation3/scenes/ScenesSnippets.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt index cddfaba7a..14b0c595c 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt @@ -199,7 +199,7 @@ fun MyAppContent() { // Manage List-Detail backstack private fun NavBackStack.addDetail(detailRoute: ConversationDetail) { - // Remove any existing detail routes + // Remove any existing detail routes, then add the new detail route removeIf { it is ConversationDetail } add(detailRoute) // Avoid adding the same detail route to the back stack twice. From 076c199769288ac44b09f4a7f871201e8ec719eb Mon Sep 17 00:00:00 2001 From: tiwiz <2534841+tiwiz@users.noreply.github.com> Date: Mon, 3 Nov 2025 12:47:34 +0000 Subject: [PATCH 05/12] Apply Spotless --- .../compose/snippets/navigation3/scenes/ScenesSnippets.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt index 14b0c595c..502d610da 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt @@ -201,7 +201,7 @@ private fun NavBackStack.addDetail(detailRoute: ConversationDetail) { // Remove any existing detail routes, then add the new detail route removeIf { it is ConversationDetail } -add(detailRoute) + add(detailRoute) // Avoid adding the same detail route to the back stack twice. if (!contains(detailRoute)) { add(detailRoute) From 2e6179d0d98af8d07797906f1729ad4374ed7842 Mon Sep 17 00:00:00 2001 From: Rob Orgiu Date: Mon, 3 Nov 2025 13:50:18 +0100 Subject: [PATCH 06/12] Add List placeholder --- .../snippets/navigation3/scenes/ScenesSnippets.kt | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt index 14b0c595c..57bfe969e 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt @@ -16,9 +16,11 @@ package com.example.compose.snippets.navigation3.scenes +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.Text import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -167,7 +169,6 @@ data class ConversationDetail( val colorId: Int ) : NavKey -@Serializable data object Profile : NavKey @Composable fun MyAppContent() { @@ -183,15 +184,15 @@ fun MyAppContent() { metadata = ListDetailScene.listPane() ) { // Add new Detail with backStack.addDetail(detailRoute) + Text(text = "I'm a Conversation List", Modifier.clickable(onClick = { + backStack.addDetail(ConversationDetail(1, 2)) + })) } entry( metadata = ListDetailScene.detailPane() ) { // Show Detail Composable } - entry { - // Profile Composable - } } ) } @@ -201,10 +202,6 @@ private fun NavBackStack.addDetail(detailRoute: ConversationDetail) { // Remove any existing detail routes, then add the new detail route removeIf { it is ConversationDetail } -add(detailRoute) - // Avoid adding the same detail route to the back stack twice. - if (!contains(detailRoute)) { - add(detailRoute) - } + add(detailRoute) } // [END android_compose_navigation3_scenes_4] From 4da8a6aef262a4c4e92776f10e5b266d0eb4ff34 Mon Sep 17 00:00:00 2001 From: tiwiz <2534841+tiwiz@users.noreply.github.com> Date: Mon, 3 Nov 2025 12:55:33 +0000 Subject: [PATCH 07/12] Apply Spotless --- .../snippets/navigation3/scenes/ScenesSnippets.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt index 57bfe969e..7e6a4c3d1 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt @@ -169,7 +169,6 @@ data class ConversationDetail( val colorId: Int ) : NavKey - @Composable fun MyAppContent() { val backStack = rememberNavBackStack(ConversationList) @@ -184,9 +183,12 @@ fun MyAppContent() { metadata = ListDetailScene.listPane() ) { // Add new Detail with backStack.addDetail(detailRoute) - Text(text = "I'm a Conversation List", Modifier.clickable(onClick = { - backStack.addDetail(ConversationDetail(1, 2)) - })) + Text( + text = "I'm a Conversation List", + Modifier.clickable(onClick = { + backStack.addDetail(ConversationDetail(1, 2)) + }) + ) } entry( metadata = ListDetailScene.detailPane() From 7699383d2029a99056daec08fb52c8b989776e33 Mon Sep 17 00:00:00 2001 From: Roberto Orgiu Date: Mon, 3 Nov 2025 16:19:44 +0100 Subject: [PATCH 08/12] Update compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt Co-authored-by: Don Turner --- .../compose/snippets/navigation3/scenes/ScenesSnippets.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt index 7e6a4c3d1..ef00f44ae 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt @@ -186,7 +186,7 @@ fun MyAppContent() { Text( text = "I'm a Conversation List", Modifier.clickable(onClick = { - backStack.addDetail(ConversationDetail(1, 2)) + backStack.addDetail(ConversationDetail("123")) }) ) } From 3e97e292cc04c36e39ff84a877d610d1e0b6d115 Mon Sep 17 00:00:00 2001 From: Rob Orgiu Date: Mon, 3 Nov 2025 16:32:18 +0100 Subject: [PATCH 09/12] Fix latest comments --- .../navigation3/scenes/ScenesSnippets.kt | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt index ef00f44ae..22a40a727 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt @@ -16,7 +16,6 @@ package com.example.compose.snippets.navigation3.scenes -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize @@ -38,6 +37,7 @@ import androidx.window.core.layout.WindowSizeClass import androidx.window.core.layout.WindowSizeClass.Companion.WIDTH_DP_MEDIUM_LOWER_BOUND import com.example.compose.snippets.navigation3.scenes.ListDetailScene.Companion.DETAIL_KEY import com.example.compose.snippets.navigation3.scenes.ListDetailScene.Companion.LIST_KEY +import com.example.compose.snippets.touchinput.Button import kotlinx.serialization.Serializable interface SceneExample { @@ -164,10 +164,7 @@ class ListDetailSceneStrategy(val windowSizeClass: WindowSizeClass) : S data object ConversationList : NavKey @Serializable -data class ConversationDetail( - val id: Int, - val colorId: Int -) : NavKey +data class ConversationDetail(val id: String) : NavKey @Composable fun MyAppContent() { @@ -182,18 +179,18 @@ fun MyAppContent() { entry( metadata = ListDetailScene.listPane() ) { - // Add new Detail with backStack.addDetail(detailRoute) - Text( - text = "I'm a Conversation List", - Modifier.clickable(onClick = { - backStack.addDetail(ConversationDetail("123")) - }) - ) + Column(modifier = Modifier.fillMaxSize()) { + Text(text = "I'm a Conversation List") + Button(onClick = { backStack.addDetail(ConversationDetail("123")) }) { + Text(text = "Open detail") + } + } + } entry( metadata = ListDetailScene.detailPane() ) { - // Show Detail Composable + Text(text = "I'm a Conversation Detail") } } ) From 66301f49e37fe1f126f57d4eee18cfdffd31514f Mon Sep 17 00:00:00 2001 From: tiwiz <2534841+tiwiz@users.noreply.github.com> Date: Mon, 3 Nov 2025 15:34:45 +0000 Subject: [PATCH 10/12] Apply Spotless --- .../compose/snippets/navigation3/scenes/ScenesSnippets.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt index 22a40a727..1323b0252 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt @@ -185,7 +185,6 @@ fun MyAppContent() { Text(text = "Open detail") } } - } entry( metadata = ListDetailScene.detailPane() From 2e3a1ad296c426c4e612aa298f1d90c834b3520a Mon Sep 17 00:00:00 2001 From: Don Turner Date: Tue, 4 Nov 2025 08:57:53 +0000 Subject: [PATCH 11/12] Apply suggestion from @dturner --- .../compose/snippets/navigation3/scenes/ScenesSnippets.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt index 1323b0252..64310fdba 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt @@ -195,7 +195,6 @@ fun MyAppContent() { ) } -// Manage List-Detail backstack private fun NavBackStack.addDetail(detailRoute: ConversationDetail) { // Remove any existing detail routes, then add the new detail route From 770abafc80ce665fa05d41ac0a85eb89055dde4c Mon Sep 17 00:00:00 2001 From: Rob Orgiu Date: Tue, 4 Nov 2025 11:43:54 +0100 Subject: [PATCH 12/12] Update the SceneStrategy to own the metadata and helper functions --- .../navigation3/scenes/ScenesSnippets.kt | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt index 64310fdba..8de59b51f 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/navigation3/scenes/ScenesSnippets.kt @@ -35,8 +35,6 @@ import androidx.navigation3.scene.SceneStrategyScope import androidx.navigation3.ui.NavDisplay import androidx.window.core.layout.WindowSizeClass import androidx.window.core.layout.WindowSizeClass.Companion.WIDTH_DP_MEDIUM_LOWER_BOUND -import com.example.compose.snippets.navigation3.scenes.ListDetailScene.Companion.DETAIL_KEY -import com.example.compose.snippets.navigation3.scenes.ListDetailScene.Companion.LIST_KEY import com.example.compose.snippets.touchinput.Button import kotlinx.serialization.Serializable @@ -98,23 +96,6 @@ class ListDetailScene( } } } - - companion object { - internal const val LIST_KEY = "ListDetailScene-List" - internal const val DETAIL_KEY = "ListDetailScene-Detail" - - /** - * Helper function to add metadata to a [NavEntry] indicating it can be displayed - * as a list in the [ListDetailScene]. - */ - fun listPane() = mapOf(LIST_KEY to true) - - /** - * Helper function to add metadata to a [NavEntry] indicating it can be displayed - * as a list in the [ListDetailScene]. - */ - fun detailPane() = mapOf(DETAIL_KEY to true) - } } @Composable @@ -128,8 +109,8 @@ fun rememberListDetailSceneStrategy(): ListDetailSceneStrategy { // --- ListDetailSceneStrategy --- /** - * A [SceneStrategy] that returns a [ListDetailScene] if the window is wide enough - * and the last two back stack entries are list and detail. + * A [SceneStrategy] that returns a [ListDetailScene] if the window is wide enough, the last item + * is the backstack is a detail, and before it, at any point in the backstack is a list. */ class ListDetailSceneStrategy(val windowSizeClass: WindowSizeClass) : SceneStrategy { @@ -155,6 +136,23 @@ class ListDetailSceneStrategy(val windowSizeClass: WindowSizeClass) : S detailEntry = detailEntry ) } + + companion object { + internal const val LIST_KEY = "ListDetailScene-List" + internal const val DETAIL_KEY = "ListDetailScene-Detail" + + /** + * Helper function to add metadata to a [NavEntry] indicating it can be displayed + * as a list in the [ListDetailScene]. + */ + fun listPane() = mapOf(LIST_KEY to true) + + /** + * Helper function to add metadata to a [NavEntry] indicating it can be displayed + * as a list in the [ListDetailScene]. + */ + fun detailPane() = mapOf(DETAIL_KEY to true) + } } // [END android_compose_navigation3_scenes_3] @@ -177,7 +175,7 @@ fun MyAppContent() { sceneStrategy = listDetailStrategy, entryProvider = entryProvider { entry( - metadata = ListDetailScene.listPane() + metadata = ListDetailSceneStrategy.listPane() ) { Column(modifier = Modifier.fillMaxSize()) { Text(text = "I'm a Conversation List") @@ -187,7 +185,7 @@ fun MyAppContent() { } } entry( - metadata = ListDetailScene.detailPane() + metadata = ListDetailSceneStrategy.detailPane() ) { Text(text = "I'm a Conversation Detail") }