M8f: instant navigation, no transitions (D-108)

NavHost enter, exit, pop and size transitions are None, so pushed screens,
tab switches, editors and the session gate change at once instead of the
default 700 ms cross-fade. NavTransitionsTest asserts the values.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
2026-10-18 21:51:20 +02:00
parent b4d9467389
commit 956908a9ba
3 changed files with 62 additions and 1 deletions
@@ -149,7 +149,16 @@ private fun AppGraph(viewModel: AppViewModel, session: SessionState, updateCheck
// Result snackbars are shown in the scaffold of the screen returned to (Home, S-5, S-8, S-10, S-12, S-16), which
// each host [snackbar] (D-071).
NavHost(nav, startDestination = start) {
// D-108: every destination change is instant (NavTransitions).
NavHost(
nav,
startDestination = start,
enterTransition = NavTransitions.enter,
exitTransition = NavTransitions.exit,
popEnterTransition = NavTransitions.enter,
popExitTransition = NavTransitions.exit,
sizeTransform = NavTransitions.size,
) {
onboarding(viewModel, shell)
topLevel(session, shell)
accountsAndExpenses(viewModel, shell)
@@ -0,0 +1,27 @@
package se.hagstedt.kvitt.nav
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.SizeTransform
import androidx.navigation.NavBackStackEntry
/**
* Destination changes are instant (D-108): no enter, exit, pop or size animation for pushed screens, tab switches,
* editors or the session gate (`NavHost` otherwise cross-fades every change over 700 ms). Motion inside a screen
* (ripples, expanding sections, the FAB, sheets) belongs to the components and is not affected.
*
* The `NavHost` lambdas only delegate to [enterTransition], [exitTransition] and [sizeTransform], which the unit
* test asserts (the transition scope is sealed and cannot be built outside a composition).
*/
internal object NavTransitions {
fun enterTransition(): EnterTransition = EnterTransition.None
fun exitTransition(): ExitTransition = ExitTransition.None
fun sizeTransform(): SizeTransform? = null
val enter: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = { enterTransition() }
val exit: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = { exitTransition() }
val size: AnimatedContentTransitionScope<NavBackStackEntry>.() -> SizeTransform? = { sizeTransform() }
}
@@ -0,0 +1,25 @@
package se.hagstedt.kvitt.nav
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import org.junit.Assert.assertNull
import org.junit.Assert.assertSame
import org.junit.Test
/** D-108: the app's `NavHost` changes destination without any animation. */
class NavTransitionsTest {
@Test
fun enterIsNone() {
assertSame(EnterTransition.None, NavTransitions.enterTransition())
}
@Test
fun exitIsNone() {
assertSame(ExitTransition.None, NavTransitions.exitTransition())
}
@Test
fun noSizeTransform() {
assertNull(NavTransitions.sizeTransform())
}
}