[go: nahoru, domu]

Merge "Remember the graph that is passed to a NavHost" into androidx-master-dev
diff --git a/navigation/navigation-compose/build.gradle b/navigation/navigation-compose/build.gradle
index 0f3208c..e8377ac8 100644
--- a/navigation/navigation-compose/build.gradle
+++ b/navigation/navigation-compose/build.gradle
@@ -38,6 +38,7 @@
     api project(":compose:ui:ui")
     api "androidx.navigation:navigation-runtime-ktx:2.3.1"
 
+    androidTestImplementation project(":compose:material:material")
     androidTestImplementation 'androidx.navigation:navigation-testing:2.3.1'
     androidTestImplementation project(path: ':internal-testutils-navigation'), {
         exclude group: 'androidx.navigation', module: 'navigation-common-ktx'
diff --git a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
index 9a71f30..a1b852e 100644
--- a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
+++ b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
@@ -17,12 +17,21 @@
 package androidx.navigation.compose
 
 import android.net.Uri
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material.Button
+import androidx.compose.material.Text
 import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.savedinstancestate.rememberSavedInstanceState
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.ContextAmbient
 import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
 import androidx.core.net.toUri
 import androidx.navigation.NavGraph
 import androidx.navigation.NavHostController
@@ -30,6 +39,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.LargeTest
 import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
+import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Rule
 import org.junit.Test
@@ -84,6 +94,58 @@
     }
 
     @Test
+    fun testNavigateOutsideStateChange() {
+        lateinit var navController: NavHostController
+        val text = "myButton"
+        var counter = 0
+        composeTestRule.setContent {
+            navController = rememberNavController()
+            var state by remember { mutableStateOf(0) }
+            Column(Modifier.fillMaxSize()) {
+                NavHost(navController, startDestination = "first") {
+                    composable("first") { }
+                    composable("second") { }
+                }
+                Button(
+                    >
+                        state++
+                        counter = state
+                    }
+                ) {
+                    Text(text)
+                }
+            }
+        }
+
+        assertWithMessage("Destination should be added to the graph")
+            .that("first" in navController.graph)
+            .isTrue()
+
+        composeTestRule.runOnIdle {
+            navController.navigate("second")
+        }
+
+        composeTestRule.runOnIdle {
+            assertWithMessage("second destination should be current")
+                .that(
+                    navController.currentDestination?.hasDeepLink(Uri.parse(createRoute("second")))
+                ).isTrue()
+        }
+
+        composeTestRule.onNodeWithText(text)
+            .performClick()
+
+        composeTestRule.runOnIdle {
+            // ensure our click listener was fired
+            assertThat(counter).isEqualTo(1)
+            assertWithMessage("second destination should be current")
+                .that(
+                    navController.currentDestination?.hasDeepLink(Uri.parse(createRoute("second")))
+                ).isTrue()
+        }
+    }
+
+    @Test
     fun testPop() {
         lateinit var navController: TestNavHostController
         composeTestRule.setContent {
@@ -113,8 +175,8 @@
         lateinit var state: MutableState<String>
         composeTestRule.setContent {
             state = remember { mutableStateOf("first") }
-
-            navController = TestNavHostController(ContextAmbient.current)
+            val context = ContextAmbient.current
+            navController = remember { TestNavHostController(context) }
 
             NavHost(navController, startDestination = state.value) {
                 test("first")
@@ -127,9 +189,9 @@
         }
 
         composeTestRule.runOnIdle {
-            assertWithMessage("Second destination should be current")
+            assertWithMessage("First destination should be current")
                 .that(
-                    navController.currentDestination?.hasDeepLink(createRoute("second").toUri())
+                    navController.currentDestination?.hasDeepLink(createRoute("first").toUri())
                 ).isTrue()
         }
     }
diff --git a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
index e86802b..ed00cd4 100644
--- a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
+++ b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
@@ -42,6 +42,9 @@
  * Once this is called, any Composable within the given [NavGraphBuilder] can be navigated to from
  * the provided [navController].
  *
+ * The builder passed into this method is [remember]ed. This means that for this NavHost, the
+ * contents of the builder cannot be changed.
+ *
  * @sample androidx.navigation.compose.samples.BasicNav
  *
  * @param navController the navController for this host
@@ -70,6 +73,9 @@
  * Once this is called, any Composable within the given [NavGraphBuilder] can be navigated to from
  * the provided [navController].
  *
+ * The graph passed into this method is [remember]ed. This means that for this NavHost, the graph
+ * cannot be changed.
+ *
  * @param navController the navController for this host
  * @param graph the graph for this host
  */
@@ -79,6 +85,7 @@
     var context = ContextAmbient.current
     val lifecycleOwner = LifecycleOwnerAmbient.current
     val viewModelStore = ViewModelStoreOwnerAmbient.current.viewModelStore
+    val rememberedGraph = remember { graph }
 
     // on successful recompose we setup the navController with proper inputs
     // after the first time, this will only happen again if one of the inputs changes
@@ -98,8 +105,8 @@
         }
     }
 
-    onCommit(graph) {
-        navController.graph = graph
+    onCommit(rememberedGraph) {
+        navController.graph = rememberedGraph
     }
 
     val restorableStateHolder = rememberRestorableStateHolder<UUID>()