Миграция с Swipeable на AnchoredDraggable

Swipeable is a Compose Material API that helps you build components that can be swiped between discrete states, such as bottom sheets, drawers, or swipe-to-dismiss. To better support advanced use cases, such as anchors that depend on the size of a component, a successor was published in Compose-Foundation 1.6.0-alpha01: AnchoredDraggable . AnchoredDraggable is a Foundation API for building draggable components with anchored states, such as bottom sheets, drawers, or swipe-to-dismiss.

API-интерфейсы Swipeable Material устарели в пользу AnchoredDraggable Foundation и будут удалены в будущем выпуске. В этом руководстве описывается, как перейти с API Swipeable на AnchoredDraggable .

Перенос SwipeableState в AnchoredDraggableState

Начните с выявления изменений в вашем держателе штата. AnchoredDraggableState не может быть унаследован, а смещение представлено как Float.NaN до его инициализации.

Обновите своего владельца штата

AnchoredDraggableState — это финальный класс, то есть от него нельзя унаследоваться. If your existing component inherits from SwipeableState , update your state holder to hold a reference to the AnchoredDraggableState instead of inheriting from it:

Перелистываемый

class MySwitchState: SwipeableState()

ПрикрепленныйПеретаскиваемый

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

Поскольку ваш держатель состояния больше не наследуется от SwipeableState , вам, возможно, придется предоставить API самостоятельно. Наиболее распространенные API, которые вы можете использовать, — это offset , progress , currentValue и targetValue .

Доступ к смещению

В отличие от Swipeable , offset AnchoredDraggableState равно Float.NaN до его инициализации. In AnchoredDraggable , the anchors can be passed to AnchoredDraggableState 's constructor or updated through AnchoredDraggableState#updateAnchors . Передача привязок конструктору AnchoredDraggableState немедленно инициализирует смещение.

If your anchors depend on layout or could change, use AnchoredDraggableState#updateAnchors to avoid recreating the state when the anchors change.

Если вы используете 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.swipeable в Modifier.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) }
    )
}

обновитьякоря

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 .

Определить позиционные пороги

Тип и имя параметра порогов изменились. Instead of having a separate ThresholdConfig interface, AnchoredDraggableState has a positionalThreshold parameter that takes a lambda function that returns the position of the threshold. Например, позиционный порог в 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 = …) open class SwipeableState (initialValue: T, animationSpec: AnimationSpec = …, confirmStateChange: (T) -> Boolean = …) 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 = …) class AnchoredDraggableState ( initialValue: T, animationSpec: AnimationSpec = …, confirmValueChange: (T) -> Boolean = …, positionalThreshold: Density.(Float) -> Float = …, velocityThreshold: Dp = …) 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(
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 .