Skip to content

Commit

Permalink
Make API for showing DialogNavigator destinations public
Browse files Browse the repository at this point in the history
Previously, the only way to use DialogNavigator
via public APIs was directly through usage of NavHost.

By making the DialogNavigator->Dialog Composable
function public, DialogNavigator can be reused in
any custom NavHost without making its internal state
public.

Test: DialogNavigatorTest suite still passes
Relnote: N/A
Change-Id: I125838dc978ee014af46a264deb1458596cce691
  • Loading branch information
ianhanniballake committed Jul 12, 2021
1 parent 6fd54d3 commit f3adbbd
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 28 deletions.
4 changes: 4 additions & 0 deletions navigation/navigation-compose/api/current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ package androidx.navigation.compose {
ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> 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<androidx.navigation.compose.DialogNavigator.Destination> {
ctor public DialogNavigator();
method public androidx.navigation.compose.DialogNavigator.Destination createDestination();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ package androidx.navigation.compose {
ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> 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<androidx.navigation.compose.DialogNavigator.Destination> {
ctor public DialogNavigator();
method public androidx.navigation.compose.DialogNavigator.Destination createDestination();
Expand Down
4 changes: 4 additions & 0 deletions navigation/navigation-compose/api/restricted_current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ package androidx.navigation.compose {
ctor public ComposeNavigator.Destination(androidx.navigation.compose.ComposeNavigator navigator, kotlin.jvm.functions.Function1<? super androidx.navigation.NavBackStackEntry,kotlin.Unit> 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<androidx.navigation.compose.DialogNavigator.Destination> {
ctor public DialogNavigator();
method public androidx.navigation.compose.DialogNavigator.Destination createDestination();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class DialogNavigatorTest {
navigator.onAttach(navigatorState)

rule.setContent {
navigator.Dialogs()
DialogHost(navigator)
}

rule.onNodeWithText(defaultText).assertDoesNotExist()
Expand All @@ -68,7 +68,7 @@ class DialogNavigatorTest {
navigator.navigate(listOf(entry), null, null)

rule.setContent {
navigator.Dialogs()
DialogHost(navigator)
}

rule.onNodeWithText(defaultText).assertIsDisplayed()
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -50,36 +47,20 @@ public class DialogNavigator : Navigator<Destination>() {
* the Navigator is attached, so we specifically return an empty flow if we
* aren't attached yet.
*/
private val backStack: StateFlow<List<NavBackStackEntry>> get() = if (attached) {
internal val backStack: StateFlow<List<NavBackStackEntry>> 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,5 +141,5 @@ public fun NavHost(
) as? DialogNavigator ?: return

// Show any dialog destinations
dialogNavigator.Dialogs()
DialogHost(dialogNavigator)
}

0 comments on commit f3adbbd

Please sign in to comment.