Swipeable에서 AnchoredDraggable로 이전

Swipeable은 하단 시트, 창 또는 스와이프하여 닫는 등 불연속 상태 간에 스와이프할 수 있는 구성요소를 빌드하는 데 도움이 되는 Compose Material API입니다. 구성요소의 크기에 따라 달라지는 앵커와 같은 고급 사용 사례를 더 효과적으로 지원하기 위해 Compose-Foundation 1.6.0-alpha01에서 후속 버전이 게시되었습니다(AnchoredDraggable). AnchoredDraggable는 하단 시트, 창 또는 스와이프하여 닫기와 같이 고정된 상태로 드래그 가능한 구성요소를 빌드하기 위한 Foundation API입니다.

Material의 Swipeable API가 지원 중단되고 Foundation의 AnchoredDraggable로 대체되었으며 향후 출시에서 삭제될 예정입니다. 이 가이드에서는 Swipeable API에서 AnchoredDraggable로 이전하는 방법을 설명합니다.

SwipeableStateAnchoredDraggableState로 이전

먼저 상태 홀더의 변경사항을 식별합니다. AnchoredDraggableState는 상속받을 수 없으며 오프셋은 초기화되기 전에 Float.NaN로 표시됩니다.

상태 홀더 업데이트

AnchoredDraggableState는 최종 클래스이므로 상속될 수 없습니다. 기존 구성요소가 SwipeableState에서 상속되는 경우 상속받는 대신 AnchoredDraggableState 참조를 보유하도록 상태 홀더를 업데이트합니다.

스와이프 가능

class MySwitchState: SwipeableState()

AnchoredDraggable

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

상태 홀더가 더 이상 SwipeableState에서 상속되지 않으므로 API를 직접 노출해야 할 수 있습니다. 사용할 수 있는 가장 일반적인 API는 offset, progress, currentValue, targetValue입니다.

오프셋 액세스

Swipeable와 달리 AnchoredDraggableStateoffset는 초기화되기 전 Float.NaN입니다. AnchoredDraggable에서 앵커는 AnchoredDraggableState의 생성자에 전달되거나 AnchoredDraggableState#updateAnchors를 통해 업데이트될 수 있습니다. 앵커를 AnchoredDraggableState의 생성자에 전달하면 오프셋이 즉시 초기화됩니다.

앵커가 레이아웃에 의존하거나 변경될 수 있는 경우 앵커가 변경될 때 상태가 다시 생성되지 않도록 AnchoredDraggableState#updateAnchors를 사용하세요.

updateAnchors를 사용하면 앵커를 updateAnchors에 전달하기 전에 오프셋이 Float.NaN가 됩니다. 실수로 Float.NaN를 구성요소에 전달하는 것을 방지하려면 AnchoredDraggableState#requireOffset를 사용하여 읽을 때 오프셋이 초기화되도록 요구하세요. 이렇게 하면 불일치나 발생할 수 있는 버그를 조기에 발견할 수 있습니다.

@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) }
    }
}

Modifier.swipeableModifier.anchoredDraggable로 이전

Modifier.anchoredDraggable()Modifier.swipeable를 대체합니다. Modifier.swipeable()의 매개변수 중 일부는 다음 섹션에 설명된 대로 AnchoredDraggableState로 직접 이동했습니다.

앵커 정의

DraggableAnchors 빌더 메서드를 사용하여 앵커를 정의합니다. 그런 다음 AnchoredDraggableState#updateAnchors 또는 AnchoredDraggableState의 생성자에 전달합니다.

생성자

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) }
    )
}

앵커가 정적이면 생성자에 전달합니다. 레이아웃에 의존하거나 정적이 아닌 경우 updateAnchors를 사용하세요.

위치 기준점 정의

임계값 매개변수의 유형과 이름이 변경되었습니다. 별도의 ThresholdConfig 인터페이스가 있는 대신 AnchoredDraggableState에는 기준 위치를 반환하는 람다 함수를 사용하는 positionalThreshold 매개변수가 있습니다. 예를 들어 위치 임곗값 50% 는 다음과 같이 표현할 수 있습니다.

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

56dp의 위치 임곗값은 다음과 같이 표현할 수 있습니다.

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

속도 기준 정의

속도 기준점도 AnchoredDraggableState의 생성자에 전달되며 람다로도 표현됩니다.

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

API 노출 영역 변경사항

아래에서 API 노출 영역의 변경사항을 간략하게 알아보세요.

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)

AnchoredDraggableState 생성자에 positionalThreshold로 전달됩니다.

resistance: ResistanceConfig? = …

아직 지원되지 않는 계정입니다. 최신 상태는 b/288084801을 참고하세요.

velocityThreshold: Dp = 125.dp

AnchoredDraggable 생성자에 전달됨