From e03b0f7e31d9621b67f1701d10b4226a7136bbc4 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Thu, 18 Apr 2024 01:14:10 +0200 Subject: [PATCH 1/2] Make scene provide an AnimatedContentScope to the composable content This scope is the one that drives the navigation transition, which allows callers to use it to drive other animations inside the screen itself as it animates into or out of the NavHost. Notably, this allows callers to use the shared element transitions. --- .../tlaster/precompose/navigation/NavHost.kt | 9 ++--- .../precompose/navigation/RouteBuilder.kt | 33 +++++++++++++++++++ .../navigation/route/ComposeRoute.kt | 3 +- .../navigation/route/FloatingRoute.kt | 13 ++++++-- .../precompose/navigation/route/SceneRoute.kt | 26 ++++++++++++++- 5 files changed, 76 insertions(+), 8 deletions(-) diff --git a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt index bad2ec14..91c4bc79 100644 --- a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt +++ b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/NavHost.kt @@ -1,6 +1,7 @@ package moe.tlaster.precompose.navigation import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedContentScope import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.ContentTransform import androidx.compose.animation.core.ExperimentalTransitionApi @@ -258,7 +259,7 @@ fun NavHost( } @Composable -private fun NavHostContent( +private fun AnimatedContentScope.NavHostContent( stateHolder: SaveableStateHolder, entry: BackStackEntry, ) { @@ -268,7 +269,7 @@ private fun NavHostContent( LocalSavedStateHolder provides entry.savedStateHolder, LocalLifecycleOwner provides entry, content = { - entry.ComposeContent() + entry.ComposeContent(this@NavHostContent) }, ) } @@ -289,12 +290,12 @@ private fun GroupRoute.composeRoute(): ComposeRoute? { } @Composable -private fun BackStackEntry.ComposeContent() { +private fun BackStackEntry.ComposeContent(animatedContentScope: AnimatedContentScope) { if (route is GroupRoute) { (route as GroupRoute).composeRoute() } else { route as? ComposeRoute - }?.content?.invoke(this) + }?.content?.invoke(animatedContentScope, this) } @OptIn(ExperimentalFoundationApi::class) diff --git a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt index 85bd9152..41515119 100644 --- a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt +++ b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt @@ -1,10 +1,12 @@ package moe.tlaster.precompose.navigation +import androidx.compose.animation.AnimatedContentScope import androidx.compose.runtime.Composable import moe.tlaster.precompose.navigation.route.FloatingRoute import moe.tlaster.precompose.navigation.route.GroupRoute import moe.tlaster.precompose.navigation.route.Route import moe.tlaster.precompose.navigation.route.SceneRoute +import moe.tlaster.precompose.navigation.route.sceneRouteWithoutAnimatedContent import moe.tlaster.precompose.navigation.transition.NavTransition class RouteBuilder( @@ -19,12 +21,43 @@ class RouteBuilder( * @param swipeProperties swipe back navigation properties for current scene * @param content composable for the destination */ + @Deprecated( + message = "Deprecated in favor of scene that supports AnimatedContent", + level = DeprecationLevel.HIDDEN, + ) fun scene( route: String, deepLinks: List = emptyList(), navTransition: NavTransition? = null, swipeProperties: SwipeProperties? = null, content: @Composable (BackStackEntry) -> Unit, + ) { + addRoute( + @Suppress("DEPRECATION") + sceneRouteWithoutAnimatedContent( + route = route, + navTransition = navTransition, + deepLinks = deepLinks, + swipeProperties = swipeProperties, + content = content, + ), + ) + } + + /** + * Add the scene [Composable] to the [RouteBuilder] + * @param route route for the destination + * @param navTransition navigation transition for current scene + * @param swipeProperties swipe back navigation properties for current scene + * @param content composable for the destination. The AnimatedContentScope provided is the + * animation that drives the scene transition. That is either entering or exiting the NavHost + */ + fun scene( + route: String, + deepLinks: List = emptyList(), + navTransition: NavTransition? = null, + swipeProperties: SwipeProperties? = null, + content: @Composable AnimatedContentScope.(BackStackEntry) -> Unit, ) { addRoute( SceneRoute( diff --git a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt index bc4bc810..09c13bef 100644 --- a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt +++ b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/ComposeRoute.kt @@ -1,10 +1,11 @@ package moe.tlaster.precompose.navigation.route +import androidx.compose.animation.AnimatedContentScope import androidx.compose.runtime.Composable import moe.tlaster.precompose.navigation.BackStackEntry interface ComposeRoute : Route { - val content: @Composable (BackStackEntry) -> Unit + val content: @Composable AnimatedContentScope.(BackStackEntry) -> Unit } interface ComposeSceneRoute : ComposeRoute diff --git a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/FloatingRoute.kt b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/FloatingRoute.kt index 711c854e..44201496 100644 --- a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/FloatingRoute.kt +++ b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/FloatingRoute.kt @@ -1,9 +1,18 @@ package moe.tlaster.precompose.navigation.route +import androidx.compose.animation.AnimatedContentScope import androidx.compose.runtime.Composable import moe.tlaster.precompose.navigation.BackStackEntry internal class FloatingRoute( - override val content: @Composable (BackStackEntry) -> Unit, + override val content: @Composable AnimatedContentScope.(BackStackEntry) -> Unit, override val route: String, -) : ComposeRoute, ComposeFloatingRoute +) : ComposeRoute, ComposeFloatingRoute { + internal constructor( + route: String, + content: @Composable (BackStackEntry) -> Unit, + ) : this( + { entry -> content(entry) }, + route, + ) +} diff --git a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt index 5003d365..65b73115 100644 --- a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt +++ b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/SceneRoute.kt @@ -1,5 +1,6 @@ package moe.tlaster.precompose.navigation.route +import androidx.compose.animation.AnimatedContentScope import androidx.compose.runtime.Composable import moe.tlaster.precompose.navigation.BackStackEntry import moe.tlaster.precompose.navigation.SwipeProperties @@ -10,5 +11,28 @@ internal class SceneRoute( val deepLinks: List, val navTransition: NavTransition?, val swipeProperties: SwipeProperties?, - override val content: @Composable (BackStackEntry) -> Unit, + override val content: @Composable AnimatedContentScope.(BackStackEntry) -> Unit, ) : ComposeRoute, ComposeSceneRoute + +@Deprecated( + message = """ + Used as a backwards compatible for the old RouteBuilder APIs which do not expect the content to + be an extension function on AnimatedContentScope + """, + level = DeprecationLevel.WARNING, +) +internal fun sceneRouteWithoutAnimatedContent( + route: String, + deepLinks: List, + navTransition: NavTransition?, + swipeProperties: SwipeProperties?, + content: @Composable (BackStackEntry) -> Unit, +): SceneRoute { + return SceneRoute( + route, + deepLinks, + navTransition, + swipeProperties, + content = { entry -> content(entry) }, + ) +} From 069f5064a1320e56fb6b2eaa3554d0799951cb39 Mon Sep 17 00:00:00 2001 From: stylianosgakis Date: Fri, 19 Apr 2024 08:04:03 +0200 Subject: [PATCH 2/2] Fix Floating Route ambiguity --- .../precompose/navigation/RouteBuilder.kt | 5 ++-- .../navigation/route/FloatingRoute.kt | 23 +++++++++++++------ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt index 41515119..58bb850c 100644 --- a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt +++ b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/RouteBuilder.kt @@ -2,10 +2,10 @@ package moe.tlaster.precompose.navigation import androidx.compose.animation.AnimatedContentScope import androidx.compose.runtime.Composable -import moe.tlaster.precompose.navigation.route.FloatingRoute import moe.tlaster.precompose.navigation.route.GroupRoute import moe.tlaster.precompose.navigation.route.Route import moe.tlaster.precompose.navigation.route.SceneRoute +import moe.tlaster.precompose.navigation.route.floatingRouteWithoutAnimatedContent import moe.tlaster.precompose.navigation.route.sceneRouteWithoutAnimatedContent import moe.tlaster.precompose.navigation.transition.NavTransition @@ -119,7 +119,8 @@ class RouteBuilder( content: @Composable (BackStackEntry) -> Unit, ) { addRoute( - FloatingRoute( + @Suppress("DEPRECATION") + floatingRouteWithoutAnimatedContent( route = route, content = content, ), diff --git a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/FloatingRoute.kt b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/FloatingRoute.kt index 44201496..6c556ad9 100644 --- a/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/FloatingRoute.kt +++ b/precompose/src/commonMain/kotlin/moe/tlaster/precompose/navigation/route/FloatingRoute.kt @@ -7,12 +7,21 @@ import moe.tlaster.precompose.navigation.BackStackEntry internal class FloatingRoute( override val content: @Composable AnimatedContentScope.(BackStackEntry) -> Unit, override val route: String, -) : ComposeRoute, ComposeFloatingRoute { - internal constructor( - route: String, - content: @Composable (BackStackEntry) -> Unit, - ) : this( - { entry -> content(entry) }, - route, +) : ComposeRoute, ComposeFloatingRoute + +@Deprecated( + message = """ + Used as a backwards compatible for the old RouteBuilder APIs which do not expect the content to + be an extension function on AnimatedContentScope + """, + level = DeprecationLevel.WARNING, +) +internal fun floatingRouteWithoutAnimatedContent( + route: String, + content: @Composable (BackStackEntry) -> Unit, +): FloatingRoute { + return FloatingRoute( + route = route, + content = { entry -> content(entry) }, ) }