diff --git a/extensions-compose-experimental/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/DefaultStackAnimation.kt b/extensions-compose-experimental/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/DefaultStackAnimation.kt index 1ade1239..b4bed2a0 100644 --- a/extensions-compose-experimental/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/DefaultStackAnimation.kt +++ b/extensions-compose-experimental/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/DefaultStackAnimation.kt @@ -264,7 +264,7 @@ internal class DefaultStackAnimation( ) scope.launch { - animationHandler.start(backEvent) + animationHandler.progress(backEvent) } } @@ -301,14 +301,6 @@ internal class DefaultStackAnimation( val exitTransitionState: SeekableTransitionState = SeekableTransitionState(EnterExitState.Visible) val enterTransitionState: SeekableTransitionState = SeekableTransitionState(EnterExitState.PreEnter) - suspend fun start(backEvent: BackEvent) { - awaitAll( - { exitTransitionState.seekTo(fraction = backEvent.progress, targetState = EnterExitState.PostExit) }, - { enterTransitionState.seekTo(fraction = backEvent.progress, targetState = EnterExitState.Visible) }, - { animatable?.animate(backEvent) }, - ) - } - suspend fun progress(backEvent: BackEvent) { animatable?.run { animate(backEvent) diff --git a/extensions-compose-experimental/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/PredictiveBackGestureTest.kt b/extensions-compose-experimental/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/PredictiveBackGestureTest.kt index aae5038d..c68c68f4 100644 --- a/extensions-compose-experimental/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/PredictiveBackGestureTest.kt +++ b/extensions-compose-experimental/src/jvmTest/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/PredictiveBackGestureTest.kt @@ -1,8 +1,13 @@ package com.arkivanov.decompose.extensions.compose.experimental.stack.animation import androidx.compose.animation.EnterExitState +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.Transition import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.tween import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -408,6 +413,76 @@ class PredictiveBackGestureTest { assertFalse(values.any { it < 1F }) } + @Test + fun GIVEN_predictive_animatable_not_null_and_two_children_in_stack_WHEN_gesture_progressed_THEN_animation_transition_not_animating() { + var stack by mutableStateOf(stack("1", "2")) + + val animation = + DefaultStackAnimation( + predictiveBackAnimatable = ::TestAnimatable, + onBack = { stack = stack.dropLast() }, + ) + + val values = HashMap() + + composeRule.setContent { + animation(stack, Modifier) { + val value by transition.animateFloat() + values[it.configuration] = value + } + } + + backDispatcher.startPredictiveBack(BackEvent(progress = 0.1F)) + composeRule.waitForIdle() + backDispatcher.progressPredictiveBack(BackEvent(progress = 0.2F)) + composeRule.waitForIdle() + backDispatcher.progressPredictiveBack(BackEvent(progress = 0.3F)) + composeRule.waitForIdle() + + assertEquals(0F, values["1"]) + assertEquals(1F, values["2"]) + } + + @Test + fun GIVEN_predictive_animatable_null_and_two_children_in_stack_WHEN_gesture_progressed_THEN_animation_transition_animating() { + var stack by mutableStateOf(stack("1", "2")) + + val animation = + DefaultStackAnimation( + predictiveBackAnimatable = { null }, + onBack = { stack = stack.dropLast() }, + ) + + val values = HashMap() + + composeRule.setContent { + animation(stack, Modifier) { + val value by transition.animateFloat() + values[it.configuration] = value + } + } + + backDispatcher.startPredictiveBack(BackEvent(progress = 0.1F)) + composeRule.waitForIdle() + backDispatcher.progressPredictiveBack(BackEvent(progress = 0.2F)) + composeRule.waitForIdle() + backDispatcher.progressPredictiveBack(BackEvent(progress = 0.3F)) + composeRule.waitForIdle() + + assertEquals(0.3F, values["1"]) + assertEquals(0.7F, values["2"]) + } + + @Composable + private fun Transition.animateFloat(): State = + animateFloat(transitionSpec = { tween(easing = LinearEasing) }) { state -> + when (state) { + EnterExitState.PreEnter -> 0F + EnterExitState.Visible -> 1F + EnterExitState.PostExit -> 0F + } + } + private fun DefaultStackAnimation( predictiveBackAnimatable: (initialBackEvent: BackEvent) -> PredictiveBackAnimatable? = ::TestAnimatable, onBack: () -> Unit,