DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Jetpack Compose Gestures — Tap, Swipe, Drag Patterns

Jetpack Compose Gestures — Tap, Swipe, Drag Patterns

Building interactive Android apps requires responsive gesture handling. Jetpack Compose provides multiple APIs for different interaction patterns. Here's a practical guide.

1. Clickable — Simple Tap Detection

For basic button interactions:

Button(
    onClick = { /* handle click */ },
    modifier = Modifier
        .fillMaxWidth()
        .padding(16.dp)
) {
    Text("Tap Me")
}
Enter fullscreen mode Exit fullscreen mode

For custom clickable surfaces without button styling:

Text(
    "Click here",
    modifier = Modifier
        .clickable { /* action */ }
        .padding(16.dp)
)
Enter fullscreen mode Exit fullscreen mode

2. CombinedClickable — Long Press + Double Tap

Handle multiple tap patterns on the same element:

Text(
    "Try long press or double tap",
    modifier = Modifier
        .combinedClickable(
            onClick = { println("Clicked") },
            onDoubleClick = { println("Double tapped") },
            onLongClick = { println("Long pressed") }
        )
        .padding(16.dp)
)
Enter fullscreen mode Exit fullscreen mode

Use cases: Context menus on long press, favorite toggle on double tap.

3. SwipeToDismissBox — Swipe to Remove

Built-in support for dismissible list items:

var isDismissed by remember { mutableStateOf(false) }

if (!isDismissed) {
    SwipeToDismissBox(
        modifier = Modifier.fillMaxWidth(),
        onDismissed = { isDismissed = true },
        backgroundContent = {
            Box(
                Modifier
                    .fillMaxSize()
                    .background(Color.Red),
                contentAlignment = Alignment.CenterEnd
            ) {
                Icon(Icons.Default.Delete, contentDescription = null)
            }
        }
    ) {
        ListItem(headlineContent = { Text("Swipe to dismiss") })
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Draggable — Single-Direction Movement

Constrain dragging to horizontal or vertical axis:

var offsetX by remember { mutableStateOf(0f) }

Box(
    modifier = Modifier
        .offset { IntOffset(offsetX.roundToInt(), 0) }
        .draggable(
            state = rememberDraggableState { delta ->
                offsetX += delta
            },
            orientation = Orientation.Horizontal
        )
        .size(100.dp)
        .background(Color.Blue)
)
Enter fullscreen mode Exit fullscreen mode

Common use: Slider controls, horizontal scrollers.

5. PointerInput + DetectDragGestures — Multi-Direction & Custom Logic

For complex drag patterns requiring offset tracking and custom constraints:

var position by remember { mutableStateOf(Offset(0f, 0f)) }

Box(
    modifier = Modifier
        .fillMaxSize()
        .pointerInput(Unit) {
            detectDragGestures(
                onDrag = { change, offset ->
                    change.consume()
                    position += offset
                },
                onDragEnd = { /* snap to grid, for example */ },
                onDragCancel = { /* reset */ }
            )
        }
) {
    Box(
        modifier = Modifier
            .offset { IntOffset(position.x.toInt(), position.y.toInt()) }
            .size(80.dp)
            .background(Color.Green)
    )
}
Enter fullscreen mode Exit fullscreen mode

Key pattern: change.consume() prevents propagation to parent composables.

6. Pinch Zoom with DetectTransformGestures

Multi-touch scaling, rotation, and pan detection:

var scale by remember { mutableStateOf(1f) }
var offset by remember { mutableStateOf(Offset(0f, 0f)) }

Image(
    painter = painterResource(id = R.drawable.sample),
    contentDescription = null,
    modifier = Modifier
        .fillMaxSize()
        .pointerInput(Unit) {
            detectTransformGestures { centroid, pan, zoom, rotation ->
                scale *= zoom
                offset += pan
            }
        }
        .graphicsLayer(
            scaleX = scale,
            scaleY = scale,
            translationX = offset.x,
            translationY = offset.y
        )
)
Enter fullscreen mode Exit fullscreen mode

Use cases: Photo galleries, map viewers, design tools.

Pattern Selection Quick Reference

Pattern Trigger Orientation Use Case
clickable Single tap N/A Buttons, text links
combinedClickable Tap + long press + double tap N/A Context menus, favorites
SwipeToDismissBox Swipe Horizontal Dismissible lists
draggable Drag Single (H or V) Sliders, tabs
pointerInput + detectDragGestures Drag Multi-direction Complex drag, snapping
pointerInput + detectTransformGestures Multi-touch 2D scaling Pinch zoom, rotate

Performance Tips

  • Use rememberDraggableState to preserve state across recompositions
  • Call change.consume() in pointerInput to prevent gesture propagation
  • Avoid complex layout calculations during gesture callbacks
  • Test with ComposeTestRule and performTouchInput() for automated testing

Next Steps

Learn more about advanced pointer events and multi-gesture combination patterns in the official Compose documentation. Build interactive experiences that delight your users.


8 Android App Templates — Production-ready Kotlin + Compose apps ready to customize and ship to Google Play.

https://myougatheax.gumroad.com

Top comments (0)