Migrar de Swipeable para AnchoredDraggable

Swipeable é uma API do Material Design do Compose que ajuda a criar componentes que podem ser deslizados entre estados discretos, como páginas inferiores, gavetas ou deslize para fechar. Para oferecer melhor suporte a casos de uso avançados, como âncoras que dependem do tamanho de um componente, um sucessor foi publicado no Compose-Foundation 1.6.0-alpha01: AnchoredDraggable. AnchoredDraggable é uma API do Foundation para criar componentes arrastáveis com estados fixados, como folhas de fundo, gavetas ou deslize para fechar.

As APIs Swipeable do Material foram descontinuadas em favor da AnchoredDraggable do Foundation e serão removidas em uma versão futura. Este guia descreve como migrar das APIs Swipeable para AnchoredDraggable.

Migre SwipeableState para AnchoredDraggableState

Comece identificando as mudanças no titular do estado. AnchoredDraggableState não pode ser herdado, e o deslocamento é representado como Float.NaN antes de ser inicializado.

Atualizar o detentor de estado

AnchoredDraggableState é uma classe final, o que significa que ela não pode ser herdada. Se o componente atual herdar de SwipeableState, atualize o detentor de estado para manter uma referência ao AnchoredDraggableState em vez de herdar dele:

Deslizante

class MySwitchState: SwipeableState()

AnchoredDraggable

class MySwitchState {
    private val anchoredDraggableState = AnchoredDraggableState(...)
}

Como o detentor do estado não herda mais de SwipeableState, talvez seja necessário expor as APIs por conta própria. As APIs mais comuns que você pode usar são offset, progress, currentValue e targetValue.

Acessar o deslocamento

Ao contrário de Swipeable, o offset de AnchoredDraggableState é Float.NaN antes de ser inicializado. No AnchoredDraggable, as âncoras podem ser transmitidas para o construtor da AnchoredDraggableState ou atualizadas pelo AnchoredDraggableState#updateAnchors. A transmissão das âncoras para o construtor de AnchoredDraggableState inicializa o deslocamento imediatamente.

Se as âncoras dependem do layout ou podem mudar, use AnchoredDraggableState#updateAnchors para evitar recriar o estado quando as âncoras mudarem.

Se você usar updateAnchors, o deslocamento será Float.NaN antes de transmitir as âncoras para updateAnchors. Para evitar a transmissão acidental de Float.NaN para componentes, use AnchoredDraggableState#requireOffset para exigir que o offset tenha sido inicializado ao ser lido. Isso ajuda a detectar inconsistências ou possíveis bugs logo no início.

@Composable
fun AnchoredDraggableBox() {
    val state = remember { AnchoredDraggableState(...) }
    val density = LocalDensity.current
    val anchors = remember { DraggableAnchors { ... } }
    SideEffect {
        state.updateAnchors(anchors)
    }
    Box(
        Modifier.offset { IntOffset(x = state.requireOffset(), y = 0) }
    }
}

Migre Modifier.swipeable para Modifier.anchoredDraggable

Modifier.anchoredDraggable() substitui Modifier.swipeable. Alguns dos parâmetros de Modifier.swipeable() foram movidos para AnchoredDraggableState diretamente, conforme descrito nas seções a seguir.

Definir âncoras

Defina as âncoras usando o método de builder DraggableAnchors. Em seguida, transmita elas para o AnchoredDraggableState#updateAnchors ou o AnchoredDraggableState do construtor:

Construtor

enum class DragValue { Start, Center, End }

@Composable
fun AnchoredDraggableBox() {
    val anchors = DraggableAnchors {
        Start at -100.dp.toPx()
        Center at 0f
        End at 100.dp.toPx()
    }
    val state = remember {
        AnchoredDraggableState(anchors = anchors)
    }
    Box(
        Modifier.offset { IntOffset(x = state.requireOffset(), y = 0) }
    )
}

updateAnchors

enum class DragValue { Start, Center, End }

@Composable
fun AnchoredDraggableBox() {
    val state = remember { AnchoredDraggableState(...) }
    val density = LocalDensity.current
    val anchors = with (density) {
        DraggableAnchors {
            Start at -100.dp.toPx()
            Center at 0f
            End at 100.dp.toPx()
        }
    }
    SideEffect {
        state.updateAnchors(anchors)
    }
    Box(
        Modifier.offset { IntOffset(x = state.requireOffset(), y = 0) }
    )
}

Se as âncoras forem estáticas, transmita-as ao construtor. Se eles dependem do layout ou não são estáticos, use updateAnchors.

Definir limites de posição

O tipo e o nome do parâmetro de limites foram alterados. Em vez de ter uma interface ThresholdConfig separada, o AnchoredDraggableState tem um parâmetro positionalThreshold que usa uma função lambda que retorna a posição do limite. Por exemplo, um limite posicional de 50% pode ser expresso como:

val anchoredDraggableState = AnchoredDraggableState(
    positionalThreshold = { distance -> distance * 0.5f },
    ...
)

Um limite posicional de 56dp pode ser expresso como:

val density = LocalDensity.current
val anchoredDraggableState = AnchoredDraggableState(
    positionalThreshold = { with(density) { 56.dp.toPx() } },
    ...
)

Definir limites de velocidade

Os limites de velocidade também são transmitidos ao construtor de AnchoredDraggableState e expressos como uma lambda:

val density = LocalDensity.current
val anchoredDraggableState = AnchoredDraggableState(
    velocityThreshold = { with(density) { 125.dp.toPx() } },
    ...
)

Mudanças na plataforma da API

Confira uma visão geral das mudanças na API abaixo.

AnchoredDraggableState

SwipeableState

AnchoredDraggableState

open class SwipeableState(initialValue: T, animationSpec: AnimationSpec = …, confirmStateChange: (T) -> Boolean = …)

class AnchoredDraggableState( initialValue: T, animationSpec: AnimationSpec = …, confirmValueChange: (T) -> Boolean = …, positionalThreshold: Density.(Float) -> Float = …, velocityThreshold: Dp = …)

offset: State

offset: Float
requireOffset()

progress: SwipeProgress

progress: Float [0f..1f]

currentValue: T

currentValue: T

targetValue: T

targetValue: T

direction: Float [-1f, 0f, 1f]

N/A

suspend animateTo(
targetValue: T,
anim: AnimationSpec = …)

suspend animateTo(
targetState: T,
velocity: Float =
lastVelocity)

suspend snapTo(targetValue: T)

suspend snapTo(targetValue: T)

performDrag(delta: Float)

dispatchRawDelta(delta: Float)

suspend performFling(velocity: Float)

suspend settle(velocity: Float)

isAnimationRunning: Boolean

isAnimationRunning: Boolean

lastVelocity: Float

Modifier.anchoredDraggable

Modifier.swipeable

Modifier.anchoredDraggable

state: SwipeableState

state: AnchoredDraggableState

anchors: Map

AnchoredDraggableState#updateAnchors
or

AnchoredDraggableState#constructor

orientation: Orientation

orientation: Orientation

enabled: Boolean = true

enabled: Boolean = true

reverseDirection: Boolean = false

reverseDirection: Boolean = false

interactionSource: MutableInteractionSource? = null

interactionSource: MutableInteractionSource? = null

thresholds: (from: T, to: T) -> ThresholdConfig = FixedThreshold(56.dp)

Transmitido para o construtor AnchoredDraggableState como positionalThreshold

resistance: ResistanceConfig? = …

Sem suporte no momento. Consulte o status mais recente em b/288084801.

velocityThreshold: Dp = 125.dp

Transmitido para o construtor AnchoredDraggable