diff --git a/enro-core/src/main/java/dev/enro/destination/compose/ComposableDestinationExtensions.kt b/enro-core/src/main/java/dev/enro/destination/compose/ComposableDestinationExtensions.kt index c099c639..e3d01394 100644 --- a/enro-core/src/main/java/dev/enro/destination/compose/ComposableDestinationExtensions.kt +++ b/enro-core/src/main/java/dev/enro/destination/compose/ComposableDestinationExtensions.kt @@ -1,9 +1,14 @@ package dev.enro.core.compose +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.EnterTransition import androidx.compose.animation.ExitTransition +import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.core.MutableTransitionState import androidx.compose.animation.core.Transition +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeOut import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.platform.LocalInspectionMode @@ -69,4 +74,41 @@ public fun OverrideNavigationAnimations( ) onDispose { } } -} \ No newline at end of file +} + +@OptIn(ExperimentalAnimationApi::class) +@Composable +@AdvancedEnroApi +public fun OverrideNavigationAnimations( + enter: EnterTransition, + exit: ExitTransition, + content: @Composable AnimatedVisibilityScope.() -> Unit +) { + // If we are in inspection mode, we need to ignore this call, as it relies on items like navigationContext + // which are only available in actual running applications + val isInspection = LocalInspectionMode.current + if (isInspection) return + + val navigationContext = navigationContext + val destination = navigationContext.contextReference as ComposableDestination + DisposableEffect(Unit) { + destination.owner.animationOverride = NavigationAnimation.Composable( + enter = EnterTransition.None, + // We need a little fade out here to keep the animation active while the animated visibility below has a chance to run + // and attach child transitions. This is a bit of a hack, but it's the only way to ensure that child exit transitions + // are fully run. + exit = fadeOut( + targetAlpha = 0.99f, + animationSpec = tween(512), + ), + ) + onDispose { } + } + navigationTransition.AnimatedVisibility( + visible = {it}, + enter = enter, + exit = exit, + ) { + content() + } +} diff --git a/tests/application/src/main/java/dev/enro/tests/application/compose/results/ComposeManagedResultFlow.kt b/tests/application/src/main/java/dev/enro/tests/application/compose/results/ComposeManagedResultFlow.kt index 7eb942ad..e8258620 100644 --- a/tests/application/src/main/java/dev/enro/tests/application/compose/results/ComposeManagedResultFlow.kt +++ b/tests/application/src/main/java/dev/enro/tests/application/compose/results/ComposeManagedResultFlow.kt @@ -1,5 +1,9 @@ package dev.enro.tests.application.compose.results +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -22,6 +26,7 @@ import dev.enro.annotations.NavigationDestination import dev.enro.core.NavigationKey import dev.enro.core.close import dev.enro.core.closeWithResult +import dev.enro.core.compose.OverrideNavigationAnimations import dev.enro.core.compose.dialog.DialogDestination import dev.enro.core.compose.navigationHandle import dev.enro.core.compose.rememberNavigationContainer @@ -72,10 +77,7 @@ class ComposeManagedResultViewModel( flow = { val firstResult = push { ComposeManagedResultFlow.FirstResult() } val presentedResult = present { ComposeManagedResultFlow.PresentedResult() } - val secondResult = push { - default("default") - ComposeManagedResultFlow.SecondResult() - } + val secondResult = push { ComposeManagedResultFlow.SecondResult() } val transientResult = push { transient() dependsOn(secondResult) @@ -173,17 +175,24 @@ fun SecondResultScreen() { @NavigationDestination(ComposeManagedResultFlow.TransientResult::class) @Composable -fun SkipOnBackResultScreen() { +fun TransientResultScreen() { val navigation = navigationHandle() - TitledColumn(title = "Transient Result") { - Text(text = "This screen will only be displayed if the result from the second screen has changed") - - Button(onClick = { navigation.closeWithResult("A") }) { - Text("Continue (A)") - } + OverrideNavigationAnimations( + enter = fadeIn() + slideInVertically { it / 4 }, + exit = fadeOut() + slideOutVertically { it / 4 }, + ) { + TitledColumn(title = "Transient Result") { + Text( + text = "This screen will only be displayed if the result from the second screen has changed" + ) + + Button(onClick = { navigation.closeWithResult("A") }) { + Text("Continue (A)") + } - Button(onClick = { navigation.closeWithResult("B") }) { - Text("Continue (B)") + Button(onClick = { navigation.closeWithResult("B") }) { + Text("Continue (B)") + } } } }