Arrastra, desliza y desliza

El modificador draggable es el punto de entrada de alto nivel para arrastrar gestos en una sola orientación e informa la distancia de arrastre en píxeles.

Es importante tener en cuenta que este modificador es similar a scrollable, ya que solo detecta el gesto. Debes conservar el estado y representarlo en la pantalla, por ejemplo, para mover el elemento mediante el modificador offset:

@Composable
private fun DraggableText() {
    var offsetX by remember { mutableStateOf(0f) }
    Text(
        modifier = Modifier
            .offset { IntOffset(offsetX.roundToInt(), 0) }
            .draggable(
                orientation = Orientation.Horizontal,
                state = rememberDraggableState { delta ->
                    offsetX += delta
                }
            ),
        text = "Drag me!"
    )
}

Si necesitas controlar todo el gesto de arrastre, procura usar el detector de gestos de arrastre en su lugar, a través del modificador pointerInput.

@Composable
private fun DraggableTextLowLevel() {
    Box(modifier = Modifier.fillMaxSize()) {
        var offsetX by remember { mutableStateOf(0f) }
        var offsetY by remember { mutableStateOf(0f) }

        Box(
            Modifier
                .offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
                .background(Color.Blue)
                .size(50.dp)
                .pointerInput(Unit) {
                    detectDragGestures { change, dragAmount ->
                        change.consume()
                        offsetX += dragAmount.x
                        offsetY += dragAmount.y
                    }
                }
        )
    }
}

Un elemento de la IU que se arrastra mediante el dedo

Deslices

El modificador swipeable te permite arrastrar elementos que, cuando se lanzan, se suelen animar en dos o más puntos de anclaje definidos en una orientación. Un uso común es implementar un patrón de "deslizar para descartar".

Es importante tener en cuenta que este modificador no mueve el elemento; solo detecta el gesto. Debes conservar el estado y representarlo en la pantalla, por ejemplo, para mover el elemento mediante el modificador offset.

El estado deslizable es obligatorio en el modificador swipeable y se puede crear y recordar con rememberSwipeableState(). Este estado también proporciona un conjunto de métodos útiles para animar anclajes de manera programática (consulta snapTo, animateTo, performFling y performDrag), así como en las propiedades para observar el progreso de arrastre.

El gesto de deslizar se puede configurar para que tenga diferentes tipos de umbrales, como FixedThreshold(Dp) y FractionalThreshold(Float), y pueden ser diferentes para cada punto de anclaje de combinación "from-to".

Para obtener más flexibilidad, puedes configurar resistance cuando deslices los límites y también el velocityThreshold que animará el deslizamiento al siguiente estado, incluso si no se alcanza el thresholds posicional.

@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun SwipeableSample() {
    val width = 96.dp
    val squareSize = 48.dp

    val swipeableState = rememberSwipeableState(0)
    val sizePx = with(LocalDensity.current) { squareSize.toPx() }
    val anchors = mapOf(0f to 0, sizePx to 1) // Maps anchor points (in px) to states

    Box(
        modifier = Modifier
            .width(width)
            .swipeable(
                state = swipeableState,
                anchors = anchors,
                thresholds = { _, _ -> FractionalThreshold(0.3f) },
                orientation = Orientation.Horizontal
            )
            .background(Color.LightGray)
    ) {
        Box(
            Modifier
                .offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) }
                .size(squareSize)
                .background(Color.DarkGray)
        )
    }
}

Un elemento de IU que responde a un gesto de deslizar