draggable

Functions summary

Modifier
Modifier.draggable(
    state: DraggableState,
    orientation: Orientation,
    enabled: Boolean,
    interactionSource: MutableInteractionSource?,
    startDragImmediately: Boolean,
    onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit,
    onDragStopped: suspend CoroutineScope.(velocity: Float) -> Unit,
    reverseDirection: Boolean
)

Configure touch dragging for the UI element in a single Orientation.

Cmn

Functions

Modifier.draggable

fun Modifier.draggable(
    state: DraggableState,
    orientation: Orientation,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource? = null,
    startDragImmediately: Boolean = false,
    onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = NoOpOnDragStarted,
    onDragStopped: suspend CoroutineScope.(velocity: Float) -> Unit = NoOpOnDragStopped,
    reverseDirection: Boolean = false
): Modifier

Configure touch dragging for the UI element in a single Orientation. The drag distance reported to DraggableState, allowing users to react on the drag delta and update their state.

The common usecase for this component is when you need to be able to drag something inside the component on the screen and represent this state via one float value

If you need to control the whole dragging flow, consider using pointerInput instead with the helper functions like detectDragGestures.

If you want to enable dragging in 2 dimensions, consider using draggable2D.

If you are implementing scroll/fling behavior, consider using scrollable.

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp

// Draw a seekbar-like composable that has a black background
// with a red square that moves along the 300.dp drag distance
val max = 300.dp
val min = 0.dp
val (minPx, maxPx) = with(LocalDensity.current) { min.toPx() to max.toPx() }
// this is the  state we will update while dragging
val offsetPosition = remember { mutableStateOf(0f) }

// seekbar itself
Box(
    modifier =
        Modifier.width(max)
            .draggable(
                orientation = Orientation.Horizontal,
                state =
                    rememberDraggableState { delta ->
                        val newValue = offsetPosition.value + delta
                        offsetPosition.value = newValue.coerceIn(minPx, maxPx)
                    },
            )
            .background(Color.Black)
) {
    Box(
        Modifier.offset { IntOffset(offsetPosition.value.roundToInt(), 0) }
            .size(50.dp)
            .background(Color.Red)
    )
}
Parameters
state: DraggableState

DraggableState state of the draggable. Defines how drag events will be interpreted by the user land logic.

orientation: Orientation

orientation of the drag

enabled: Boolean = true

whether or not drag is enabled

interactionSource: MutableInteractionSource? = null

MutableInteractionSource that will be used to emit DragInteraction.Start when this draggable is being dragged.

startDragImmediately: Boolean = false

when set to true, draggable will start dragging immediately and prevent other gesture detectors from reacting to "down" events (in order to block composed press-based gestures). This is intended to allow end users to "catch" an animating widget by pressing on it. It's useful to set it when value you're dragging is settling / animating.

onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = NoOpOnDragStarted

callback that will be invoked when drag is about to start at the starting position, allowing user to suspend and perform preparation for drag, if desired. This suspend function is invoked with the draggable scope, allowing for async processing, if desired. Note that the scope used here is the one provided by the draggable node, for long running work that needs to outlast the modifier being in the composition you should use a scope that fits the lifecycle needed.

onDragStopped: suspend CoroutineScope.(velocity: Float) -> Unit = NoOpOnDragStopped

callback that will be invoked when drag is finished, allowing the user to react on velocity and process it. This suspend function is invoked with the draggable scope, allowing for async processing, if desired. Note that the scope used here is the one provided by the draggable node, for long running work that needs to outlast the modifier being in the composition you should use a scope that fits the lifecycle needed.

reverseDirection: Boolean = false

reverse the direction of the scroll, so top to bottom scroll will behave like bottom to top and left to right will behave like right to left.