Drop the back preview when the surface is shown again
This commit is contained in:
1 parent
1ab46897c3
commit
1d6b1ed2a9
3 files changed
+38
-10
No files matched your search
@@ -13,10 +13,13 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.withFrameNanos
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -65,9 +68,8 @@ fun Modifier.animateContentSizeMotion(reduceMotion: Boolean = rememberReduceMoti
|
||||
* swiped edge and rounds its corners, previewing what's behind. Completing the
|
||||
* gesture invokes [onBack] and keeps the preview transform, so the surface
|
||||
* leaves from where the finger let go instead of snapping back to full size —
|
||||
* pair it with [predictiveBackExit] on the surface's own exit. Should the surface
|
||||
* still be there shortly after (the back was taken without dismissing it), it
|
||||
* settles back. Cancelling springs it back.
|
||||
* pair it with [predictiveBackExit] on the surface's own exit. Cancelling
|
||||
* springs it back.
|
||||
*
|
||||
* A drop-in replacement for a screen's own `BackHandler(onBack)` — register it
|
||||
* once and apply the returned [Modifier] to that screen's root so the preview
|
||||
@@ -78,15 +80,27 @@ fun Modifier.animateContentSizeMotion(reduceMotion: Boolean = rememberReduceMoti
|
||||
*
|
||||
* @param enabled gate the handler — e.g. set false while a screen's own inner
|
||||
* back (a sub-section) should take the gesture instead.
|
||||
* @param visible whether the surface is currently shown — typically the
|
||||
* `visible` of the `AnimatedVisibility` it lives in. The preview is held while
|
||||
* the surface leaves and dropped the moment it is shown again, so reopening it
|
||||
* mid-exit never starts from the shrunk state; a back that leaves it visible
|
||||
* settles back. Null when the caller doesn't track it: the preview is then held
|
||||
* for a fixed time before settling back.
|
||||
*/
|
||||
@Composable
|
||||
fun Modifier.predictiveBack(
|
||||
onBack: () -> Unit,
|
||||
enabled: Boolean = true,
|
||||
visible: Boolean? = null,
|
||||
reduceMotion: Boolean = rememberReduceMotion(),
|
||||
): Modifier {
|
||||
val progress = remember { Animatable(0f) }
|
||||
var fromLeftEdge by remember { mutableStateOf(true) }
|
||||
val currentVisible by rememberUpdatedState(visible)
|
||||
|
||||
LaunchedEffect(visible) {
|
||||
if (visible == true) progress.snapTo(0f)
|
||||
}
|
||||
|
||||
PredictiveBackHandler(enabled = enabled) { events ->
|
||||
try {
|
||||
@@ -95,8 +109,14 @@ fun Modifier.predictiveBack(
|
||||
progress.snapTo(FastOutSlowInEasing.transform(event.progress))
|
||||
}
|
||||
onBack()
|
||||
delay(COMMIT_SETTLE_DELAY_MILLIS)
|
||||
progress.animateTo(0f)
|
||||
if (currentVisible == null) {
|
||||
delay(COMMIT_SETTLE_DELAY_MILLIS)
|
||||
progress.animateTo(0f)
|
||||
} else {
|
||||
// Two frames guarantee the recomposition that onBack triggered.
|
||||
repeat(2) { withFrameNanos { } }
|
||||
if (currentVisible == true) progress.animateTo(0f)
|
||||
}
|
||||
} catch (_: CancellationException) {
|
||||
progress.animateTo(0f)
|
||||
}
|
||||
@@ -115,8 +135,8 @@ fun Modifier.predictiveBack(
|
||||
}
|
||||
|
||||
/**
|
||||
* How long a committed preview is held before it settles back — past any exit
|
||||
* the surface would play, so it only fires for a back that left it in place.
|
||||
* How long a committed preview is held before it settles back when the caller
|
||||
* doesn't pass `visible` — past any exit the surface would play.
|
||||
*/
|
||||
private const val COMMIT_SETTLE_DELAY_MILLIS = 1_000L
|
||||
|
||||
|
||||
Reference in new issue
Block a user