{% setvar book_path %}/reference/androidx/_book.yaml{% endsetvar %} {% include "_shared/_reference-head-tags.html" %}

PointerInputScope

public interface PointerInputScope extends Density


Receiver scope for Modifier.pointerInput that permits handling pointer input.

Summary

Public methods

abstract @NonNull R
<R extends Object> awaitPointerEventScope(
    @ExtensionFunctionType @NonNull SuspendFunction1<@NonNull AwaitPointerEventScope, @NonNull R> block
)

Suspend and install a pointer input block that can await input events and respond to them immediately.

default @NonNull Size

The additional space applied to each side of the layout area when the layout is smaller than ViewConfiguration.minimumTouchTargetSize.

default boolean

Intercept pointer input that children receive even if the pointer is out of bounds.

abstract @NonNull IntSize

The measured size of the pointer input region.

abstract @NonNull ViewConfiguration

The ViewConfiguration used to tune gesture detectors.

default void
setInterceptOutOfBoundsChildEvents(
    boolean interceptOutOfBoundsChildEvents
)

Intercept pointer input that children receive even if the pointer is out of bounds.

Extension functions

default final void
DragGestureDetectorKt.detectDragGestures(
    @NonNull PointerInputScope receiver,
    @NonNull Function1<@NonNull OffsetUnit> onDragStart,
    @NonNull Function0<Unit> onDragEnd,
    @NonNull Function0<Unit> onDragCancel,
    @NonNull Function2<@NonNull PointerInputChange, @NonNull OffsetUnit> onDrag
)

Gesture detector that waits for pointer down and touch slop in any direction and then calls onDrag for each drag event.

default final void
DragGestureDetectorKt.detectDragGesturesAfterLongPress(
    @NonNull PointerInputScope receiver,
    @NonNull Function1<@NonNull OffsetUnit> onDragStart,
    @NonNull Function0<Unit> onDragEnd,
    @NonNull Function0<Unit> onDragCancel,
    @NonNull Function2<@NonNull PointerInputChange, @NonNull OffsetUnit> onDrag
)

Gesture detector that waits for pointer down and long press, after which it calls onDrag for each drag event.

default final void
DragGestureDetectorKt.detectHorizontalDragGestures(
    @NonNull PointerInputScope receiver,
    @NonNull Function1<@NonNull OffsetUnit> onDragStart,
    @NonNull Function0<Unit> onDragEnd,
    @NonNull Function0<Unit> onDragCancel,
    @NonNull Function2<@NonNull PointerInputChange, @NonNull FloatUnit> onHorizontalDrag
)

Gesture detector that waits for pointer down and touch slop in the horizontal direction and then calls onHorizontalDrag for each horizontal drag event.

default final void
DragGestureDetectorKt.detectVerticalDragGestures(
    @NonNull PointerInputScope receiver,
    @NonNull Function1<@NonNull OffsetUnit> onDragStart,
    @NonNull Function0<Unit> onDragEnd,
    @NonNull Function0<Unit> onDragCancel,
    @NonNull Function2<@NonNull PointerInputChange, @NonNull FloatUnit> onVerticalDrag
)

Gesture detector that waits for pointer down and touch slop in the vertical direction and then calls onVerticalDrag for each vertical drag event.

default final void
TransformGestureDetectorKt.detectTransformGestures(
    @NonNull PointerInputScope receiver,
    boolean panZoomLock,
    @NonNull Function4<@NonNull Offset, @NonNull Offset, @NonNull Float, @NonNull FloatUnit> onGesture
)

A gesture detector for rotation, panning, and zoom.

Inherited methods

From androidx.compose.ui.unit.Density
abstract float

The logical density of the display.

abstract float

Current user preference for the scaling factor for fonts.

default int

Convert Dp to Int by rounding

default int

Convert Sp to Int by rounding

default @NonNull Dp

Convert Sp to Dp.

default @NonNull Dp
orgKt.toDp(int receiver)

Convert an Int pixel value to Dp.

default @NonNull Dp
orgKt.toDp(float receiver)

Convert a Float pixel value to a Dp

default @NonNull DpSize

Convert a Size to a DpSize.

default float
orgKt.toPx(@NonNull Dp receiver)

Convert Dp to pixels.

default float

Convert Sp to pixels.

default @NonNull Rect

Convert a DpRect to a Rect.

default @NonNull Size

Convert a DpSize to a Size.

default @NonNull TextUnit
orgKt.toSp(@NonNull Dp receiver)

Convert Dp to Sp.

default @NonNull TextUnit
orgKt.toSp(int receiver)

Convert an Int pixel value to Sp.

default @NonNull TextUnit
orgKt.toSp(float receiver)

Convert a Float pixel value to a Sp

Public methods

awaitPointerEventScope

abstract @NonNull R <R extends Object> awaitPointerEventScope(
    @ExtensionFunctionType @NonNull SuspendFunction1<@NonNull AwaitPointerEventScope, @NonNull R> block
)

Suspend and install a pointer input block that can await input events and respond to them immediately. A call to awaitPointerEventScope will resume with block's result after it completes.

More than one awaitPointerEventScope can run concurrently in the same PointerInputScope by using kotlinx.coroutines.launch. blocks are dispatched to in the order in which they were installed.

getExtendedTouchPadding

default @NonNull Size getExtendedTouchPadding()

The additional space applied to each side of the layout area when the layout is smaller than ViewConfiguration.minimumTouchTargetSize.

getInterceptOutOfBoundsChildEvents

default boolean getInterceptOutOfBoundsChildEvents()

Intercept pointer input that children receive even if the pointer is out of bounds.

If true, and a child has been moved out of this layout and receives an event, this will receive that event. If false, a child receiving pointer input outside of the bounds of this layout will not trigger any events in this.

getSize

abstract @NonNull IntSize getSize()

The measured size of the pointer input region. Input events will be reported with a coordinate space of (0, 0) to (size.width, size,height) as the input region, with (0, 0) indicating the upper left corner.

getViewConfiguration

abstract @NonNull ViewConfiguration getViewConfiguration()

The ViewConfiguration used to tune gesture detectors.

setInterceptOutOfBoundsChildEvents

default void setInterceptOutOfBoundsChildEvents(
    boolean interceptOutOfBoundsChildEvents
)

Intercept pointer input that children receive even if the pointer is out of bounds.

If true, and a child has been moved out of this layout and receives an event, this will receive that event. If false, a child receiving pointer input outside of the bounds of this layout will not trigger any events in this.

Extension functions

DragGestureDetectorKt.detectDragGestures

default final void DragGestureDetectorKt.detectDragGestures(
    @NonNull PointerInputScope receiver,
    @NonNull Function1<@NonNull OffsetUnit> onDragStart,
    @NonNull Function0<Unit> onDragEnd,
    @NonNull Function0<Unit> onDragCancel,
    @NonNull Function2<@NonNull PointerInputChange, @NonNull OffsetUnit> onDrag
)

Gesture detector that waits for pointer down and touch slop in any direction and then calls onDrag for each drag event. It follows the touch slop detection of awaitTouchSlopOrCancellation but will consume the position change automatically once the touch slop has been crossed.

onDragStart called when the touch slop has been passed and includes an Offset representing the last known pointer position relative to the containing element. The Offset can be outside the actual bounds of the element itself meaning the numbers can be negative or larger than the element bounds if the touch target is smaller than the ViewConfiguration.minimumTouchTargetSize.

onDragEnd is called after all pointers are up and onDragCancel is called if another gesture has consumed pointer input, canceling this gesture.

Example Usage:

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.drag
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
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.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize

val offsetX = remember { mutableStateOf(0f) }
val offsetY = remember { mutableStateOf(0f) }
var size by remember { mutableStateOf(Size.Zero) }
Box(
    Modifier.fillMaxSize()
        .onSizeChanged { size = it.toSize() }
) {
    Box(
        Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
            .size(50.dp)
            .background(Color.Blue)
            .pointerInput(Unit) {
                detectDragGestures { _, dragAmount ->
                    val original = Offset(offsetX.value, offsetY.value)
                    val summed = original + dragAmount
                    val newValue = Offset(
                        x = summed.x.coerceIn(0f, size.width - 50.dp.toPx()),
                        y = summed.y.coerceIn(0f, size.height - 50.dp.toPx())
                    )
                    offsetX.value = newValue.x
                    offsetY.value = newValue.y
                }
            }
    )
}

DragGestureDetectorKt.detectDragGesturesAfterLongPress

default final void DragGestureDetectorKt.detectDragGesturesAfterLongPress(
    @NonNull PointerInputScope receiver,
    @NonNull Function1<@NonNull OffsetUnit> onDragStart,
    @NonNull Function0<Unit> onDragEnd,
    @NonNull Function0<Unit> onDragCancel,
    @NonNull Function2<@NonNull PointerInputChange, @NonNull OffsetUnit> onDrag
)

Gesture detector that waits for pointer down and long press, after which it calls onDrag for each drag event.

onDragStart called when a long press is detected and includes an Offset representing the last known pointer position relative to the containing element. The Offset can be outside the actual bounds of the element itself meaning the numbers can be negative or larger than the element bounds if the touch target is smaller than the ViewConfiguration.minimumTouchTargetSize.

onDragEnd is called after all pointers are up and onDragCancel is called if another gesture has consumed pointer input, canceling this gesture. This function will automatically consume all the position change after the long press.

Example Usage:

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
import androidx.compose.foundation.gestures.drag
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
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.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize

val offsetX = remember { mutableStateOf(0f) }
val offsetY = remember { mutableStateOf(0f) }
var size by remember { mutableStateOf(Size.Zero) }
Box(
    Modifier.fillMaxSize()
        .onSizeChanged { size = it.toSize() }
) {
    Box(
        Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
            .size(50.dp)
            .background(Color.Blue)
            .pointerInput(Unit) {
                detectDragGesturesAfterLongPress { _, dragAmount ->
                    val original = Offset(offsetX.value, offsetY.value)
                    val summed = original + dragAmount
                    val newValue = Offset(
                        x = summed.x.coerceIn(0f, size.width - 50.dp.toPx()),
                        y = summed.y.coerceIn(0f, size.height - 50.dp.toPx())
                    )
                    offsetX.value = newValue.x
                    offsetY.value = newValue.y
                }
            }
    )
}

DragGestureDetectorKt.detectHorizontalDragGestures

default final void DragGestureDetectorKt.detectHorizontalDragGestures(
    @NonNull PointerInputScope receiver,
    @NonNull Function1<@NonNull OffsetUnit> onDragStart,
    @NonNull Function0<Unit> onDragEnd,
    @NonNull Function0<Unit> onDragCancel,
    @NonNull Function2<@NonNull PointerInputChange, @NonNull FloatUnit> onHorizontalDrag
)

Gesture detector that waits for pointer down and touch slop in the horizontal direction and then calls onHorizontalDrag for each horizontal drag event. It follows the touch slop detection of awaitHorizontalTouchSlopOrCancellation, but will consume the position change automatically once the touch slop has been crossed.

onDragStart called when the touch slop has been passed and includes an Offset representing the last known pointer position relative to the containing element. The Offset can be outside the actual bounds of the element itself meaning the numbers can be negative or larger than the element bounds if the touch target is smaller than the ViewConfiguration.minimumTouchTargetSize.

onDragEnd is called after all pointers are up and onDragCancel is called if another gesture has consumed pointer input, canceling this gesture.

This gesture detector will coordinate with detectVerticalDragGestures and awaitVerticalTouchSlopOrCancellation to ensure only vertical or horizontal dragging is locked, but not both.

Example Usage:

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
import androidx.compose.foundation.gestures.drag
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
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.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp

val offsetX = remember { mutableStateOf(0f) }
val offsetY = remember { mutableStateOf(0f) }
var width by remember { mutableStateOf(0f) }
Box(
    Modifier.fillMaxSize()
        .onSizeChanged { width = it.width.toFloat() }
) {
    Box(
        Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
            .fillMaxHeight()
            .width(50.dp)
            .background(Color.Blue)
            .pointerInput(Unit) {
                detectHorizontalDragGestures { _, dragAmount ->
                    val originalX = offsetX.value
                    val newValue = (originalX + dragAmount).coerceIn(0f, width - 50.dp.toPx())
                    offsetX.value = newValue
                }
            }
    )
}

DragGestureDetectorKt.detectVerticalDragGestures

default final void DragGestureDetectorKt.detectVerticalDragGestures(
    @NonNull PointerInputScope receiver,
    @NonNull Function1<@NonNull OffsetUnit> onDragStart,
    @NonNull Function0<Unit> onDragEnd,
    @NonNull Function0<Unit> onDragCancel,
    @NonNull Function2<@NonNull PointerInputChange, @NonNull FloatUnit> onVerticalDrag
)

Gesture detector that waits for pointer down and touch slop in the vertical direction and then calls onVerticalDrag for each vertical drag event. It follows the touch slop detection of awaitVerticalTouchSlopOrCancellation, but will consume the position change automatically once the touch slop has been crossed.

onDragStart called when the touch slop has been passed and includes an Offset representing the last known pointer position relative to the containing element. The Offset can be outside the actual bounds of the element itself meaning the numbers can be negative or larger than the element bounds if the touch target is smaller than the ViewConfiguration.minimumTouchTargetSize.

onDragEnd is called after all pointers are up and onDragCancel is called if another gesture has consumed pointer input, canceling this gesture.

This gesture detector will coordinate with detectHorizontalDragGestures and awaitHorizontalTouchSlopOrCancellation to ensure only vertical or horizontal dragging is locked, but not both.

Example Usage:

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectVerticalDragGestures
import androidx.compose.foundation.gestures.drag
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp

val offsetX = remember { mutableStateOf(0f) }
val offsetY = remember { mutableStateOf(0f) }
var height by remember { mutableStateOf(0f) }
Box(
    Modifier.fillMaxSize()
        .onSizeChanged { height = it.height.toFloat() }
) {
    Box(
        Modifier.offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
            .fillMaxWidth()
            .height(50.dp)
            .background(Color.Blue)
            .pointerInput(Unit) {
                detectVerticalDragGestures { _, dragAmount ->
                    val originalY = offsetY.value
                    val newValue = (originalY + dragAmount).coerceIn(0f, height - 50.dp.toPx())
                    offsetY.value = newValue
                }
            }
    )
}

TransformGestureDetectorKt.detectTransformGestures

default final void TransformGestureDetectorKt.detectTransformGestures(
    @NonNull PointerInputScope receiver,
    boolean panZoomLock,
    @NonNull Function4<@NonNull Offset, @NonNull Offset, @NonNull Float, @NonNull FloatUnit> onGesture
)

A gesture detector for rotation, panning, and zoom. Once touch slop has been reached, the user can use rotation, panning and zoom gestures. onGesture will be called when any of the rotation, zoom or pan occurs, passing the rotation angle in degrees, zoom in scale factor and pan as an offset in pixels. Each of these changes is a difference between the previous call and the current gesture. This will consume all position changes after touch slop has been reached. onGesture will also provide centroid of all the pointers that are down.

If panZoomLock is true, rotation is allowed only if touch slop is detected for rotation before pan or zoom motions. If not, pan and zoom gestures will be detected, but rotation gestures will not be. If panZoomLock is false, once touch slop is reached, all three gestures are detected.

Example Usage:

import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTransformGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput

/**
 * Rotates the given offset around the origin by the given angle in degrees.
 *
 * A positive angle indicates a counterclockwise rotation around the right-handed 2D Cartesian
 * coordinate system.
 *
 * See: [Rotation matrix](https://en.wikipedia.org/wiki/Rotation_matrix)
 */
fun Offset.rotateBy(angle: Float): Offset {
    val angleInRadians = angle * PI / 180
    return Offset(
        (x * cos(angleInRadians) - y * sin(angleInRadians)).toFloat(),
        (x * sin(angleInRadians) + y * cos(angleInRadians)).toFloat()
    )
}

var offset by remember { mutableStateOf(Offset.Zero) }
var zoom by remember { mutableStateOf(1f) }
var angle by remember { mutableStateOf(0f) }

Box(
    Modifier
        .pointerInput(Unit) {
            detectTransformGestures(
                onGesture = { centroid, pan, gestureZoom, gestureRotate ->
                    val oldScale = zoom
                    val newScale = zoom * gestureZoom

                    // For natural zooming and rotating, the centroid of the gesture should
                    // be the fixed point where zooming and rotating occurs.
                    // We compute where the centroid was (in the pre-transformed coordinate
                    // space), and then compute where it will be after this delta.
                    // We then compute what the new offset should be to keep the centroid
                    // visually stationary for rotating and zooming, and also apply the pan.
                    offset = (offset + centroid / oldScale).rotateBy(gestureRotate) -
                        (centroid / newScale + pan / oldScale)
                    zoom = newScale
                    angle += gestureRotate
                }
            )
        }
        .graphicsLayer {
            translationX = -offset.x * zoom
            translationY = -offset.y * zoom
            scaleX = zoom
            scaleY = zoom
            rotationZ = angle
            transformOrigin = TransformOrigin(0f, 0f)
        }
        .background(Color.Blue)
        .fillMaxSize()
)