Skip to content

Commit f2f44f7

Browse files
committed
Fixed the same predictive back gesture animation repeating when quickly started again
If you start a new predictive back gesture before the current gesture is fully finished, the same animation is started again. Instead, it should do nothing and allow the current animation to finish.
1 parent 4fb267d commit f2f44f7

File tree

1 file changed

+47
-27
lines changed
  • extensions-compose-experimental/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation

1 file changed

+47
-27
lines changed

extensions-compose-experimental/src/commonMain/kotlin/com/arkivanov/decompose/extensions/compose/experimental/stack/animation/DefaultStackAnimation.kt

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ internal class DefaultStackAnimation<C : Any, T : Any>(
5353
) {
5454
var currentStack by remember { mutableStateOf(stack) }
5555
var items by remember { mutableStateOf(getAnimationItems(newStack = currentStack)) }
56-
var nextItems: Map<Any, AnimationItem<C, T>>? by remember { mutableStateOf(null) }
56+
var nextItems: Map<String, AnimationItem<C, T>>? by remember { mutableStateOf(null) }
5757
val stackKeys = remember(stack) { stack.items.map { it.key } }
5858
val currentStackKeys = remember(currentStack) { currentStack.items.map { it.key } }
5959

@@ -150,7 +150,7 @@ internal class DefaultStackAnimation<C : Any, T : Any>(
150150
}
151151
}
152152

153-
private fun getAnimationItems(newStack: ChildStack<C, T>, oldStack: ChildStack<C, T>? = null): Map<Any, AnimationItem<C, T>> =
153+
private fun getAnimationItems(newStack: ChildStack<C, T>, oldStack: ChildStack<C, T>? = null): Map<String, AnimationItem<C, T>> =
154154
when {
155155
(oldStack == null) || (newStack.active.key == oldStack.active.key) ->
156156
keyedItemsOf(
@@ -199,7 +199,7 @@ internal class DefaultStackAnimation<C : Any, T : Any>(
199199
private fun PredictiveBackController(
200200
stack: ChildStack<C, T>,
201201
predictiveBackParams: PredictiveBackParams,
202-
setItems: (Map<Any, AnimationItem<C, T>>) -> Unit,
202+
setItems: (Map<String, AnimationItem<C, T>>) -> Unit,
203203
) {
204204
val scope = rememberCoroutineScope()
205205

@@ -242,29 +242,30 @@ internal class DefaultStackAnimation<C : Any, T : Any>(
242242
private val stack: ChildStack<C, T>,
243243
private val scope: CoroutineScope,
244244
private val predictiveBackParams: PredictiveBackParams,
245-
private val setItems: (Map<Any, AnimationItem<C, T>>) -> Unit,
245+
private val setItems: (Map<String, AnimationItem<C, T>>) -> Unit,
246246
) : BackCallback() {
247-
private var animationHandler: AnimationHandler? = null
248-
private var initialBackEvent: BackEvent? = null
247+
private var state: State = State.Idle
249248

250249
override fun onBackStarted(backEvent: BackEvent) {
251-
initialBackEvent = backEvent
250+
if (state is State.Idle) {
251+
state = State.Started(backEvent)
252+
}
252253
}
253254

254255
override fun onBackProgressed(backEvent: BackEvent) {
255256
startIfNeeded()
257+
val currentState = state as? State.Progress ?: return
256258

257259
scope.launch {
258-
animationHandler?.progress(backEvent)
260+
currentState.animationHandler.progress(backEvent)
259261
}
260262
}
261263

262264
private fun startIfNeeded() {
263-
val backEvent = initialBackEvent ?: return
264-
initialBackEvent = null
265-
265+
val currentState = state as? State.Started ?: return
266+
val backEvent = currentState.initialBackEvent
266267
val animationHandler = AnimationHandler(animatable = predictiveBackParams.animatable(backEvent))
267-
this.animationHandler = animationHandler
268+
state = State.Progress(animationHandler)
268269
val exitChild = stack.active
269270
val enterChild = stack.backStack.last()
270271

@@ -295,32 +296,51 @@ internal class DefaultStackAnimation<C : Any, T : Any>(
295296
}
296297

297298
override fun onBackCancelled() {
298-
initialBackEvent = null
299+
when (val currentState = state) {
300+
is State.Progress -> {
301+
state = State.Finishing
302+
303+
scope.launch {
304+
currentState.animationHandler.cancel()
305+
state = State.Idle
306+
setItems(getAnimationItems(newStack = stack))
307+
}
308+
}
299309

300-
scope.launch {
301-
animationHandler?.also { handler ->
302-
handler.cancel()
303-
animationHandler = null
304-
setItems(getAnimationItems(newStack = stack))
310+
else -> {
311+
state = State.Idle
305312
}
306313
}
307314
}
308315

309316
override fun onBack() {
310-
initialBackEvent = null
311-
312-
scope.launch {
313-
animationHandler?.also { handler ->
314-
handler.finish()
315-
animationHandler = null
316-
setItems(getAnimationItems(newStack = stack.dropLast()))
317+
when (val currentState = state) {
318+
is State.Progress -> {
319+
state = State.Finishing
320+
321+
scope.launch {
322+
currentState.animationHandler.finish()
323+
state = State.Idle
324+
setItems(getAnimationItems(newStack = stack.dropLast()))
325+
predictiveBackParams.onBack()
326+
}
317327
}
318328

319-
predictiveBackParams.onBack()
329+
else -> {
330+
state = State.Idle
331+
predictiveBackParams.onBack()
332+
}
320333
}
321334
}
322335
}
323336

337+
private sealed interface State {
338+
data object Idle : State
339+
data class Started(val initialBackEvent: BackEvent) : State
340+
data class Progress(val animationHandler: AnimationHandler) : State
341+
data object Finishing : State
342+
}
343+
324344
private class AnimationHandler(
325345
val animatable: PredictiveBackAnimatable?,
326346
) {
@@ -380,7 +400,7 @@ private data class AnimationItem<out C : Any, out T : Any>(
380400
)
381401

382402
@ExperimentalDecomposeApi
383-
private fun <C : Any, T : Any> keyedItemsOf(vararg items: AnimationItem<C, T>): Map<Any, AnimationItem<C, T>> =
403+
private fun <C : Any, T : Any> keyedItemsOf(vararg items: AnimationItem<C, T>): Map<String, AnimationItem<C, T>> =
384404
items.associateBy { it.child.key }
385405

386406
/*

0 commit comments

Comments
 (0)