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에서 상속받는 대신 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]

해당 사항 없음

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 생성자에 전달됨