diff --git a/navigation/navigation-compose/api/current.txt b/navigation/navigation-compose/api/current.txt index 89e6e04e76c01..ecad6d421fe5c 100644 --- a/navigation/navigation-compose/api/current.txt +++ b/navigation/navigation-compose/api/current.txt @@ -10,6 +10,10 @@ package androidx.navigation.compose { ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1 content); } + public final class DialogHostKt { + method @androidx.compose.runtime.Composable public static void DialogHost(androidx.navigation.compose.DialogNavigator dialogNavigator); + } + @androidx.navigation.Navigator.Name("dialog") public final class DialogNavigator extends androidx.navigation.Navigator { ctor public DialogNavigator(); method public androidx.navigation.compose.DialogNavigator.Destination createDestination(); diff --git a/navigation/navigation-compose/api/public_plus_experimental_current.txt b/navigation/navigation-compose/api/public_plus_experimental_current.txt index 89e6e04e76c01..ecad6d421fe5c 100644 --- a/navigation/navigation-compose/api/public_plus_experimental_current.txt +++ b/navigation/navigation-compose/api/public_plus_experimental_current.txt @@ -10,6 +10,10 @@ package androidx.navigation.compose { ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1 content); } + public final class DialogHostKt { + method @androidx.compose.runtime.Composable public static void DialogHost(androidx.navigation.compose.DialogNavigator dialogNavigator); + } + @androidx.navigation.Navigator.Name("dialog") public final class DialogNavigator extends androidx.navigation.Navigator { ctor public DialogNavigator(); method public androidx.navigation.compose.DialogNavigator.Destination createDestination(); diff --git a/navigation/navigation-compose/api/restricted_current.txt b/navigation/navigation-compose/api/restricted_current.txt index 89e6e04e76c01..ecad6d421fe5c 100644 --- a/navigation/navigation-compose/api/restricted_current.txt +++ b/navigation/navigation-compose/api/restricted_current.txt @@ -10,6 +10,10 @@ package androidx.navigation.compose { ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1 content); } + public final class DialogHostKt { + method @androidx.compose.runtime.Composable public static void DialogHost(androidx.navigation.compose.DialogNavigator dialogNavigator); + } + @androidx.navigation.Navigator.Name("dialog") public final class DialogNavigator extends androidx.navigation.Navigator { ctor public DialogNavigator(); method public androidx.navigation.compose.DialogNavigator.Destination createDestination(); diff --git a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/DialogNavigatorTest.kt b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/DialogNavigatorTest.kt index eb6ffb4d371d1..98b57cb7a9456 100644 --- a/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/DialogNavigatorTest.kt +++ b/navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/DialogNavigatorTest.kt @@ -42,7 +42,7 @@ class DialogNavigatorTest { navigator.onAttach(navigatorState) rule.setContent { - navigator.Dialogs() + DialogHost(navigator) } rule.onNodeWithText(defaultText).assertDoesNotExist() @@ -68,7 +68,7 @@ class DialogNavigatorTest { navigator.navigate(listOf(entry), null, null) rule.setContent { - navigator.Dialogs() + DialogHost(navigator) } rule.onNodeWithText(defaultText).assertIsDisplayed() diff --git a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogHost.kt b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogHost.kt new file mode 100644 index 0000000000000..c8ef92c624b80 --- /dev/null +++ b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogHost.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package androidx.navigation.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.saveable.rememberSaveableStateHolder +import androidx.compose.ui.window.Dialog +import androidx.lifecycle.Lifecycle +import androidx.navigation.compose.DialogNavigator.Destination + +/** + * Show each [Destination] on the [DialogNavigator]'s back stack as a [Dialog]. + * + * Note that [NavHost] will call this for you; you do not need to call it manually. + */ +@Composable +public fun DialogHost(dialogNavigator: DialogNavigator) { + val saveableStateHolder = rememberSaveableStateHolder() + val dialogBackStack by dialogNavigator.backStack.collectAsState() + + dialogBackStack.filter { backStackEntry -> + backStackEntry.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) + }.forEach { backStackEntry -> + val destination = backStackEntry.destination as Destination + Dialog( + onDismissRequest = { dialogNavigator.dismiss(backStackEntry) }, + properties = destination.dialogProperties + ) { + // while in the scope of the composable, we provide the navBackStackEntry as the + // ViewModelStoreOwner and LifecycleOwner + backStackEntry.LocalOwnersProvider(saveableStateHolder) { + destination.content(backStackEntry) + } + } + } +} diff --git a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogNavigator.kt b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogNavigator.kt index a882259737d45..e23b3a8334c8f 100644 --- a/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogNavigator.kt +++ b/navigation/navigation-compose/src/main/java/androidx/navigation/compose/DialogNavigator.kt @@ -17,14 +17,11 @@ package androidx.navigation.compose import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveableStateHolder import androidx.compose.runtime.setValue import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties -import androidx.lifecycle.Lifecycle import androidx.navigation.FloatingWindow import androidx.navigation.NavBackStackEntry import androidx.navigation.NavDestination @@ -50,36 +47,20 @@ public class DialogNavigator : Navigator() { * the Navigator is attached, so we specifically return an empty flow if we * aren't attached yet. */ - private val backStack: StateFlow> get() = if (attached) { + internal val backStack: StateFlow> get() = if (attached) { state.backStack } else { MutableStateFlow(emptyList()) } /** - * Show each [Destination] on the back stack as a [Dialog]. - * - * Note that [NavHost] will call this for you; you do not need to call it manually. + * Dismiss the dialog destination associated with the given [backStackEntry]. */ - internal val Dialogs: @Composable () -> Unit = @Composable { - val saveableStateHolder = rememberSaveableStateHolder() - val dialogBackStack by backStack.collectAsState() - - dialogBackStack.filter { backStackEntry -> - backStackEntry.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED) - }.forEach { backStackEntry -> - val destination = backStackEntry.destination as Destination - Dialog( - onDismissRequest = { state.pop(backStackEntry, false) }, - properties = destination.dialogProperties - ) { - // while in the scope of the composable, we provide the navBackStackEntry as the - // ViewModelStoreOwner and LifecycleOwner - backStackEntry.LocalOwnersProvider(saveableStateHolder) { - destination.content(backStackEntry) - } - } + internal fun dismiss(backStackEntry: NavBackStackEntry) { + check(attached) { + "The DialogNavigator must be attached to a NavController to call dismiss" } + state.pop(backStackEntry, false) } override fun onAttach(state: NavigatorState) { 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 1f609a8d21354..e18528dfc0752 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 @@ -141,5 +141,5 @@ public fun NavHost( ) as? DialogNavigator ?: return // Show any dialog destinations - dialogNavigator.Dialogs() + DialogHost(dialogNavigator) }