Created
July 6, 2023 19:18
-
-
Save grumpyshoe/c6117a485c55dd6363d8f16f30446992 to your computer and use it in GitHub Desktop.
Compose: Tinder like card dragging
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fun Modifier.swiper( | |
enabled: Boolean, | |
state: Swipe, | |
onDragReset: () -> Unit = {}, | |
onTap: () -> Unit, | |
onLongPress: () -> Unit, | |
onDrag: (SwipeDirection?, Float) -> Unit = { _, _ -> }, | |
onDragRight: () -> Unit, | |
onDragLeft: () -> Unit, | |
): Modifier = composed { | |
val scope = rememberCoroutineScope() | |
var dragging by remember { mutableStateOf(false) } | |
val m = Modifier | |
.pointerInput(Unit) { | |
detectTapGestures( | |
onTap = { | |
onTap() | |
}, | |
onLongPress = { | |
onLongPress() | |
}, | |
) | |
} | |
.pointerInput(Unit) { | |
if (enabled) { | |
detectDragGestures( | |
onDragEnd = { | |
dragging = false | |
when { | |
abs(state.offsetX.targetValue) < state.maxWidth / 5 -> { | |
state | |
.reset(scope) | |
.invokeOnCompletion { onDragReset() } | |
} | |
state.offsetX.targetValue > 0 -> { | |
state | |
.accepted(scope) | |
.invokeOnCompletion { onDragRight() } | |
} | |
state.offsetX.targetValue < 0 -> { | |
state | |
.rejected(scope) | |
.invokeOnCompletion { onDragLeft() } | |
} | |
} | |
}, | |
onDrag = { change, dragAmount -> | |
dragging = true | |
val original = Offset(state.offsetX.targetValue, state.offsetY.targetValue) | |
val summed = original + dragAmount | |
val newValue = Offset( | |
x = summed.x.coerceIn(-state.maxWidth, state.maxWidth), | |
y = state.offsetY.targetValue, | |
) | |
if (change.positionChange() != Offset.Zero) change.consume() | |
val direction = when { | |
summed.x > 0 && abs(summed.x) > state.maxWidth / 5 -> SwipeDirection.RIGHT | |
summed.x < 0 && abs(summed.x) > state.maxWidth / 5 -> SwipeDirection.LEFT | |
else -> null | |
} | |
onDrag(direction, summed.x) | |
state.drag( | |
scope, | |
newValue.x, | |
newValue.y, | |
) | |
}, | |
) | |
} | |
} | |
/** | |
* Doing translation on the graphics layer | |
* which mimics the rotation and translation of tinder swipeable card. This can be improved | |
* if I start swiping a card it first rotates along edges left or right according to drag | |
*/ | |
.graphicsLayer( | |
translationX = state.offsetX.value, | |
translationY = state.offsetY.value, | |
rotationZ = (state.offsetX.value / 60).coerceIn(-40f, 40f), | |
) | |
m | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment