From 24cf8fe331700a3f1c7720c794eac40e2ab9c219 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Thu, 18 Jun 2026 16:03:45 +0200 Subject: [PATCH] nav: spring slide/fade screen transitions (Calendula feel) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drive the NavHost with the motion scheme's fastSpatialSpec: forward fades in over a held, static background; back (and the predictive-back drag) slides the top screen out to the right, revealing the screen beneath unmoved — the same overlay feel as Calendula, instead of the default navigation-compose transition. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../floret/ui/navigation/FloretNavHost.kt | 14 +++++++++++++ .../floret/ui/navigation/FloretTransitions.kt | 21 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 app/src/main/java/de/jeanlucmakiola/floret/ui/navigation/FloretTransitions.kt diff --git a/app/src/main/java/de/jeanlucmakiola/floret/ui/navigation/FloretNavHost.kt b/app/src/main/java/de/jeanlucmakiola/floret/ui/navigation/FloretNavHost.kt index 6195f95..3c8e4b1 100644 --- a/app/src/main/java/de/jeanlucmakiola/floret/ui/navigation/FloretNavHost.kt +++ b/app/src/main/java/de/jeanlucmakiola/floret/ui/navigation/FloretNavHost.kt @@ -1,5 +1,10 @@ package de.jeanlucmakiola.floret.ui.navigation +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideOutHorizontally import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier @@ -28,11 +33,20 @@ import de.jeanlucmakiola.floret.ui.tasklist.TaskListViewModel @Composable fun FloretNavHost(modifier: Modifier = Modifier) { val nav = rememberNavController() + val slide = rememberNavSlideSpec() NavHost( navController = nav, startDestination = Dest.LISTS, modifier = modifier, + // Only the screen on top moves; the background is always held static. + // Forward is a plain fade-in over the held screen — calm, not a slide. + // Back (and the predictive-back drag, which drives popExit) is the snappy + // slide-out to the right, revealing the held screen unmoved. + enterTransition = { fadeIn() }, + exitTransition = { ExitTransition.None }, + popEnterTransition = { EnterTransition.None }, + popExitTransition = { slideOutHorizontally(slide) { it } + fadeOut() }, ) { composable(Dest.LISTS) { ListsScreen( diff --git a/app/src/main/java/de/jeanlucmakiola/floret/ui/navigation/FloretTransitions.kt b/app/src/main/java/de/jeanlucmakiola/floret/ui/navigation/FloretTransitions.kt new file mode 100644 index 0000000..1b327e9 --- /dev/null +++ b/app/src/main/java/de/jeanlucmakiola/floret/ui/navigation/FloretTransitions.kt @@ -0,0 +1,21 @@ +package de.jeanlucmakiola.floret.ui.navigation + +import androidx.compose.animation.core.FiniteAnimationSpec +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.IntOffset + +/** + * The horizontal slide spec for screen-to-screen navigation: the *fast* + * spring-physics spec from the active motion scheme — snappy with a subtle + * springy settle, rather than a fixed easing curve. Same choice as Calendula's + * calendar slide, so the two apps move the same way. + * + * Read in composable scope so the non-composable `NavHost` transition lambdas + * can capture it. + */ +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun rememberNavSlideSpec(): FiniteAnimationSpec = + MaterialTheme.motionScheme.fastSpatialSpec()