The predictive back gesture is no longer optional. For apps targeting Android 16 (API level 36), predictive back system animations are enabled by default. Since August 31, 2026, Google Play requires new apps and app updates to target API level 36. So any app still shipping updates is affected. If your app has a custom back animation in Compose, you’re likely using PredictiveBackHandler. One part of its contract is easy to get wrong. It shows up when the user starts the swipe and then changes their mind.
What PredictiveBackHandler actually gives you
PredictiveBackHandler hands you a suspend lambda that receives a Flow<BackEventCompat>. You collect that flow to read the gesture’s progress. Then you drive any animation you want. A scale, a fade or a slide all work. The official pattern in Android’s documentation looks like this.
PredictiveBackHandler(enabled = isBackHandlerEnabled) { progress: Flow<BackEventCompat> ->
try {
progress.collect { backEvent ->
// Update your UI or animation based on backEvent.progress.
}
// Handle the final back action (e.g., navigate back).
} catch (e: CancellationException) {
// Back gesture was cancelled, reset your UI.
}
}
The part worth noticing is the catch (e: CancellationException) block. It carries real weight.
Why a canceled swipe cancels your coroutine
The lambda you pass to PredictiveBackHandler runs in a coroutine scoped to the gesture. Say the user swipes partway back and then lets go without completing it. That coroutine is canceled. Collecting a Flow in a canceled coroutine throws CancellationException at the collection point. That’s normal coroutine behavior. It isn’t a crash.
The bug appears when nothing resets the animation state on that path. Some developers leave out the catch entirely. The exception then ends the lambda. The reset code never runs. Others write a catch that only logs. The result is the same either way. The state you were updating inside progress.collect stops at its last value. Nothing tells it to return to rest. An element can sit frozen at 40% scale, or a fade can stay stuck partway. It stays that way until some unrelated recomposition happens to reset it.
Canceled gestures are easy to get wrong, even for library authors. In January 2025, AndroidX merged a fix titled “Fix PredictiveBack’s animation when a back action was canceled.” Bugs like this rarely show up until someone starts a swipe on a device and abandons it.
Resetting state correctly when the gesture cancels
Treat CancellationException as the signal that the gesture ended without completing. Reset state deliberately in response.
var progress by remember { mutableFloatStateOf(0f) }
PredictiveBackHandler { backEvent: Flow<BackEventCompat> ->
try {
backEvent.collect { event ->
progress = event.progress
}
// gesture completed, navigate away
} catch (e: CancellationException) {
progress = 0f
} finally {
progress = 0f
}
}
The finally block is what guarantees the reset here. It runs whether the collection completed, threw CancellationException or failed for another reason. The explicit catch is still worth keeping if you want to react to a canceled gesture specifically. But the finally is what stops the animation from getting stuck.
Why this belongs in an interview
This makes a good interview topic because it joins two things that usually get tested separately. One is whether a candidate understands structured concurrency and cancellation. The other is whether they know a current Compose API, beyond a memorized definition of coroutines. Can you explain why collecting a flow in a canceled coroutine throws CancellationException? Can you explain why that’s a normal control-flow signal and not an error? That’s a reasonable bar for anyone claiming mid-level Android experience. Knowing to reset state on that path is a reasonable bar for anyone who has shipped Compose navigation.
How to answer this in an interview
- Say that the lambda runs in a coroutine scoped to the gesture, and an abandoned swipe cancels it.
- Explain that
collectthen throwsCancellationException. That’s normal control flow, not a crash. - Name the bug. If nothing resets the animation state on that path, it freezes at its last value.
- Give the fix. Reset state in a
finallyblock. Keep acatchonly if you need to react to cancellation specifically.
A short spoken version might sound like this.
When the user abandons the swipe, the handler’s coroutine is cancelled, so collect throws a CancellationException. If I only update progress inside collect, it stays at its last value, like 40 percent. So I reset it in finally. That runs whether the gesture completed or was cancelled.
If you’re prepping for an Android interview now, have a clean answer ready. Predictive back is no longer a niche corner of the platform. It’s the default. Code that ignores gesture cancellation will misbehave the first time someone starts a swipe and stops halfway.