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")
}
For custom clickable surfaces without button styling:
Text(
"Click here",
modifier = Modifier
.clickable { /* action */ }
.padding(16.dp)
)
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)
)
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") })
}
}
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)
)
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)
)
}
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
)
)
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
rememberDraggableStateto preserve state across recompositions - Call
change.consume()inpointerInputto prevent gesture propagation - Avoid complex layout calculations during gesture callbacks
- Test with
ComposeTestRuleandperformTouchInput()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.
Top comments (0)