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 127a1bfc..8e3ba335 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 @@ -69,7 +69,7 @@ internal class DefaultStackAnimation( if (updateItems) { val newItems = getAnimationItems(newStack = currentStack, oldStack = oldStack) - if (items.size == 1) { + if ((items.size == 1) || items.values.last().transitionState.isSeekable()) { items = newItems } else { nextItems = newItems @@ -401,3 +401,6 @@ private fun TransitionState<*>.isIdle(): Boolean = is SeekableTransitionState -> false else -> false } + +private fun TransitionState<*>.isSeekable(): Boolean = + this is SeekableTransitionState 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 4ca770a6..f5a9bd90 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 @@ -213,6 +213,29 @@ class PredictiveBackGestureTest { composeRule.onNodeWithText("3").assertDoesNotExist() } + @Test + fun GIVEN_gesture_started_WHEN_stack_popped_THEN_gesture_cancelled() { + var stack by mutableStateOf(stack("1", "2")) + val animation = DefaultStackAnimation(animator = fade(), onBack = { stack = stack.dropLast() },) + + composeRule.setContent { + animation(stack, Modifier) { + Text(text = it.configuration) + } + } + + backDispatcher.startPredictiveBack(BackEvent(progress = 0F)) + composeRule.waitForIdle() + backDispatcher.progressPredictiveBack(BackEvent(progress = 0.5F)) + composeRule.waitForIdle() + + stack = stack.dropLast() + composeRule.mainClock.autoAdvance = false + composeRule.mainClock.advanceTimeByFrame() + + composeRule.onNodeWithText("2").assertTestTagToRootDoesNotExist { it.startsWith(TEST_TAG_PREFIX) } + } + @Test fun GIVEN_gesture_started_WHEN_stack_pushed_and_back_THEN_new_child_popped_and_gesture_finished() { var stack by mutableStateOf(stack("1", "2")) @@ -471,6 +494,7 @@ class PredictiveBackGestureTest { private fun DefaultStackAnimation( predictiveBackAnimatable: (initialBackEvent: BackEvent) -> PredictiveBackAnimatable? = ::TestAnimatable, + animator: StackAnimator? = null, onBack: () -> Unit, ): DefaultStackAnimation = DefaultStackAnimation( @@ -482,7 +506,7 @@ class PredictiveBackGestureTest { animatable = predictiveBackAnimatable, ) }, - selector = { _, _, _ -> null }, + selector = { _, _, _ -> animator }, ) private fun stack(configs: List): ChildStack =