Skip to content

Commit

Permalink
Merge pull request #1025 from jbw0033/nested_host
Browse files Browse the repository at this point in the history
[Navigation Animation] Fix Nested NavHost animations
  • Loading branch information
jbw0033 committed Apr 27, 2022
2 parents abb08e5 + d59c882 commit b37cd07
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 57 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ okhttp = "3.12.13"
coil = "1.3.2"

androidxtest = "1.4.0"
androidxnavigation = "2.4.0"
androidxnavigation = "2.5.0-alpha04"

[libraries]
compose-ui-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,13 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.with
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavDestination
Expand All @@ -52,6 +48,7 @@ import androidx.navigation.compose.DialogNavigator
import androidx.navigation.compose.LocalOwnersProvider
import androidx.navigation.createGraph
import androidx.navigation.get
import kotlinx.coroutines.flow.map

/**
* Provides in place in the Compose hierarchy for self contained navigation to occur.
Expand Down Expand Up @@ -155,14 +152,16 @@ public fun AnimatedNavHost(
val composeNavigator = navController.navigatorProvider.get<Navigator<out NavDestination>>(
AnimatedComposeNavigator.NAME
) as? AnimatedComposeNavigator ?: return
val backStack by composeNavigator.backStack.collectAsState()
val transitionsInProgress by composeNavigator.transitionsInProgress.collectAsState()
val visibleTransitionsInProgress = rememberVisibleList(transitionsInProgress)
val visibleBackStack = rememberVisibleList(backStack)
visibleTransitionsInProgress.PopulateVisibleList(transitionsInProgress)
visibleBackStack.PopulateVisibleList(backStack)
val visibleEntries by remember(navController.visibleEntries) {
navController.visibleEntries.map {
it.filter {
entry ->
entry.destination.navigatorName == AnimatedComposeNavigator.NAME
}
}
}.collectAsState(emptyList())

val backStackEntry = visibleTransitionsInProgress.lastOrNull() ?: visibleBackStack.lastOrNull()
val backStackEntry = visibleEntries.lastOrNull()

if (backStackEntry != null) {
val finalEnter: AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition = {
Expand Down Expand Up @@ -200,20 +199,18 @@ public fun AnimatedNavHost(
contentAlignment,
contentKey = { it.id }
) {
val currentEntry = transitionsInProgress.lastOrNull { entry ->
it == entry
} ?: backStack.lastOrNull { entry ->
val currentEntry = visibleEntries.last { entry ->
it == entry
}
// while in the scope of the composable, we provide the navBackStackEntry as the
// ViewModelStoreOwner and LifecycleOwner
currentEntry?.LocalOwnersProvider(saveableStateHolder) {
currentEntry.LocalOwnersProvider(saveableStateHolder) {
(currentEntry.destination as AnimatedComposeNavigator.Destination)
.content(this, currentEntry)
}
}
if (transition.currentState == transition.targetState) {
transitionsInProgress.forEach { entry ->
visibleEntries.forEach { entry ->
composeNavigator.markTransitionComplete(entry)
}
}
Expand Down Expand Up @@ -243,43 +240,3 @@ internal val popEnterTransitions =
@ExperimentalAnimationApi
internal val popExitTransitions =
mutableMapOf<String?, (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)?>()

@Composable
private fun MutableList<NavBackStackEntry>.PopulateVisibleList(
transitionsInProgress: Collection<NavBackStackEntry>
) {
transitionsInProgress.forEach { entry ->
DisposableEffect(entry.lifecycle) {
val observer = LifecycleEventObserver { _, event ->
// ON_START -> add to visibleBackStack, ON_STOP -> remove from visibleBackStack
if (event == Lifecycle.Event.ON_START) {
// We want to treat the visible lists as Sets but we want to keep
// the functionality of mutableStateListOf() so that we recompose in response
// to adds and removes.
if (!contains(entry)) {
add(entry)
}
}
if (event == Lifecycle.Event.ON_STOP) {
remove(entry)
}
}
entry.lifecycle.addObserver(observer)
onDispose {
entry.lifecycle.removeObserver(observer)
}
}
}
}

@Composable
private fun rememberVisibleList(transitionsInProgress: Collection<NavBackStackEntry>) =
remember(transitionsInProgress) {
mutableStateListOf<NavBackStackEntry>().also {
it.addAll(
transitionsInProgress.filter { entry ->
entry.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)
}
)
}
}

0 comments on commit b37cd07

Please sign in to comment.