I'm trying to detect swipe direction in Compose. I'm using the draggable modifier for this. But draggable allows only one direction to detect (Vertical or Horizontal). I want to detect swipes for all directions (left, right, up, down). Can anyone help me how can I do this? Thanks!
7 Answers
You can use the pointerInput
modifier controlling the dragging gesture with the detectDragGestures
function.
Something like:
Box(modifier = Modifier.fillMaxSize()) {
var offsetX by remember { mutableStateOf(0f) }
var offsetY by remember { mutableStateOf(0f) }
Box(
Modifier
.offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
.size(100.dp, 100.dp)
.background(Color.Blue)
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
change.consume()
val (x,y) = dragAmount
when {
x > 0 ->{ /* right */ }
x < 0 ->{ /* left */ }
}
when {
y > 0 -> { /* down */ }
y < 0 -> { /* up */ }
}
offsetX += dragAmount.x
offsetY += dragAmount.y
}
}
)
}

- 320,139
- 94
- 887
- 841
-
I found I needed to access the mutable state offsets with `.value` to update them: `offsetX.value += dragAmount.x` `offsetY.value += dragAmount.y` – pontiac_ventura Jun 20 '21 at 22:37
-
Inside Box if we have scrollable content or LazyColumn, detectDragGestures is not working. – sandeepmaaram May 30 '23 at 11:21
Modifier.dragGestureFilter
detects dragging in any direction. Pass an instance of DragObserver
and override onDrag
. Here you can detect the swipe direction based on the Offset
. This object has x
and y
values, which are positive or negative based on the direction.
Here's what your code could look like:
Box(
Modifier.dragGestureFilter(
dragObserver = object : DragObserver() {
override fun onDrag(dragDistance: Offset): Offset {
val (x, y) = dragDistance
when {
x > 0 -> { /* right */ }
x < 0 -> { /* left */ }
}
when {
y > 0 -> { /* down */ }
y < 0 -> { /* up */ }
}
}
}
)
)
To actually move the object, you would have to apply Modifier.offset
with values that are updated in onDrag
.

- 2,718
- 3
- 17
- 23
-
-
3Note that "Modifier.dragGestureFilter has been deprecated. Use Modifier.pointerInput { detectDragGestures (...)} instead. Alternatively, use Modifier.draggable for one axis drags" (https://developer.android.com/jetpack/androidx/releases/compose-foundation#1.0.0-alpha02) – Luis Feb 09 '22 at 08:45
This is a more modified way to get the direction without conflict horizontal swipes with vertical swipes, and to make sure to return the direction after the user end swiping.
var direction by remember { mutableStateOf(-1)}
Box(
modifier = Modifier
.pointerInput(Unit) {
detectDragGestures(
onDrag = { change, dragAmount ->
change.consumeAllChanges()
val (x, y) = dragAmount
if(abs(x) > abs(y)){
when {
x > 0 -> {
//right
direction = 0
}
x < 0 -> {
// left
direction = 1
}
}
}else{
when {
y > 0 -> {
// down
direction = 2
}
y < 0 -> {
// up
direction = 3
}
}
}
},
onDragEnd = {
when (direction){
0 -> {
//right swipe code here }
1 -> {
// left swipe code here
}
2 -> {
// down swipe code here
}
3 -> {
// up swipe code here
}
}
}
)
)

- 76
- 4
-
1Inside Box if we have scrollable content or LazyColumn, detectDragGestures is not working. – sandeepmaaram May 30 '23 at 11:22
If you need to get the corner directions, here's an angle-based algorithm:
fun swipeDirection(x: Float, y: Float): SwipeDirection {
val angleDir = (atan2(x.toDouble(), y.toDouble()) / Math.PI * 180)
val angle = if (angleDir < 0) {
360 + angleDir
} else {
angleDir
}
return when (angle) {
in 22.5..67.5 -> {
SwipeDirection.BOTTOM_RIGHT
}
in 67.5..112.5 -> {
SwipeDirection.RIGHT
}
in 112.5..157.5 -> {
SwipeDirection.TOP_RIGHT
}
in 157.5..202.5 -> {
SwipeDirection.TOP
}
in 202.5..247.5 -> {
SwipeDirection.TOP_LEFT
}
in 247.5..292.5 -> {
SwipeDirection.LEFT
}
in 292.5..337.5 -> {
SwipeDirection.BOTTOM_LEFT
}
else -> {
SwipeDirection.BOTTOM
}
}
}

- 6,352
- 5
- 42
- 59
To complement the answers from @Gabriel and @J-Abdo I did something similar based on this answers, for me I need only left/right swipe and I added an "offset" with the min lenght that I want to be considered "swipe"
BoxWithConstraints {
var offsetX by remember { mutableStateOf(0f) }
val minSwipeOffset by remember {
mutableStateOf(
constraints.maxWidth / 4
)
}
Box(
Modifier
.fillMaxSize()
.pointerInput(Unit) {
detectDragGestures(
onDrag = { change, dragAmount ->
change.consume()
val (x, y) = dragAmount
offsetX += dragAmount.x
},
onDragEnd = {
when {
(offsetX < 0F && abs(offsetX) > minSwipeOffset) -> {
SwipeDirection.Left
}
(offsetX > 0 && abs(offsetX) > minSwipeOffset) -> {
SwipeDirection.Right
}
else -> null
}?.let(onSwipe)
offsetX = 0F
}
)
}) {
}
}
and I'm using just a sealed class for the directions
sealed class SwipeDirection {
object Left : SwipeDirection()
object Right : SwipeDirection()
}

- 1,469
- 2
- 20
- 38
-
Why do you need a sealed class for the direction? Why not just an enum? – dessalines Mar 11 '23 at 15:56
-
@thouliha in this case it does not matter, yes it can be a enum, but if in the future you need to pass the offset or more data you might need a sealed class, in my case I knew that I will pass more data, but as it is, yes a enum is also ok – Javier Mar 16 '23 at 11:48
My modification:
suspend fun PointerInputScope.detectSwipe(
swipeState: MutableIntState = mutableIntStateOf(-1),
onSwipeLeft: () -> Unit = {},
onSwipeRight: () -> Unit = {},
onSwipeUp: () -> Unit = {},
onSwipeDown: () -> Unit = {},
) = detectDragGestures(
onDrag = { change, dragAmount ->
change.consume()
val (x, y) = dragAmount
if (abs(x) > abs(y)) {
when {
x > 0 -> swipeState.intValue = 0
x < 0 -> swipeState.intValue = 1
}
} else {
when {
y > 0 -> swipeState.intValue = 2
y < 0 -> swipeState.intValue = 3
}
}
},
onDragEnd = {
when (swipeState.intValue) {
0 -> onSwipeRight()
1 -> onSwipeLeft()
2 -> onSwipeDown()
3 -> onSwipeUp()
}
}
)
usage:
Modifier.pointerInput(Unit) {
detectSwipe(
onSwipeLeft = {
},
onSwipeRight = {
},
)
}

- 557
- 3
- 6
You can use detectHorizontalDragGestures/detectVerticalDragGestures modifiers to detect Horizontal or Vertical drag gestures,
If you need to add more than one gesture listener to a composable, use separate pointerInput modifier instances
For Vertical Drag:
Modifier.pointerInput(Unit) {
detectVerticalDragGestures { _, dragAmount ->
if (dragAmount > 0) {
// swipe down
}else {
// swipe up
}
}
}

- 121
- 2
- 5