Bermigrasi dari Swipeable ke AnchoredDraggable

Swipeable adalah Compose Material API yang membantu Anda membuat komponen yang dapat digeser di antara status terpisah, seperti lembar bawah, panel samping, atau geser untuk menutup. Untuk mendukung kasus penggunaan lanjutan dengan lebih baik, seperti anchor yang tergantung pada ukuran komponen, penggantinya dipublikasikan di Compose-Foundation 1.6.0-alpha01: AnchoredDraggable. AnchoredDraggable adalah Foundation API untuk membangun komponen yang dapat ditarik dengan status anchor, seperti sebagai sheet bawah, panel samping, atau geser untuk menutup.

Swipeable API Material tidak digunakan lagi dan digantikan oleh API Foundation AnchoredDraggable dan akan dihapus dalam rilis mendatang. Panduan ini menjelaskan cara melakukan migrasi dari Swipeable API ke AnchoredDraggable.

Migrasikan SwipeableState ke AnchoredDraggableState

Mulailah dengan mengidentifikasi perubahan pada holder status Anda. AnchoredDraggableState tidak dapat diwarisi, dan offset ditampilkan sebagai Float.NaN sebelumnya diinisialisasi.

Memperbarui holder status

AnchoredDraggableState adalah class akhir, yang berarti tidak dapat diwarisi dari mereka. Jika komponen yang ada mewarisi dari SwipeableState, update holder status Anda untuk menyimpan referensi ke AnchoredDraggableState, bukan mewarisi darinya:

Dapat Digeser

class MySwitchState: SwipeableState()

Dapat Ditarik

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

Karena holder status Anda tidak mewarisi dari SwipeableState lagi, Anda mungkin harus mengekspos API sendiri. API paling umum yang dapat Anda gunakan adalah offset, progress, currentValue, dan targetValue.

Mengakses offset

Tidak seperti di Swipeable, offset AnchoredDraggableState adalah Float.NaN sebelumnya sudah diinisialisasi. Di AnchoredDraggable, anchor dapat diteruskan ke Konstruktor AnchoredDraggableState atau diperbarui melalui AnchoredDraggableState#updateAnchors Meneruskan anchor ke Konstruktor AnchoredDraggableState langsung melakukan inisialisasi offset.

Jika anchor Anda bergantung pada tata letak atau dapat berubah, gunakan AnchoredDraggableState#updateAnchors untuk menghindari pembuatan ulang status saat perubahan anchor.

Jika Anda menggunakan updateAnchors, offsetnya akan menjadi Float.NaN sebelum meneruskan anchor ke updateAnchors. Untuk menghindari penerusan Float.NaN secara tidak sengaja ke gunakan AnchoredDraggableState#requireOffset untuk mewajibkan telah diinisialisasi saat pembacaannya. Hal ini membantu Anda untuk inkonsistensi atau kemungkinan bug sejak dini.

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

Migrasikan Modifier.swipeable ke Modifier.anchoredDraggable

Modifier.anchoredDraggable() menggantikan Modifier.swipeable. Agak besar dari parameter Modifier.swipeable() telah dipindahkan ke AnchoredDraggableState secara langsung, seperti yang dijelaskan di bagian berikut.

Menentukan anchor

Tentukan anchor menggunakan metode builder DraggableAnchors. Kemudian, teruskan mereka ke AnchoredDraggableState#updateAnchors atau AnchoredDraggableState konstruktor:

Konstruktor

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

updateAnchor

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

Jika anchor bersifat statis, teruskan ke konstruktor. Jika mereka bergantung pada atau tidak statis, gunakan updateAnchors.

Menentukan batas posisi

Jenis dan nama parameter nilai minimum telah berubah. Daripada memiliki antarmuka ThresholdConfig terpisah, AnchoredDraggableState memiliki Parameter positionalThreshold yang menggunakan fungsi lambda yang menampilkan posisi ambang batas. Misalnya, ambang batas posisi 50% bisa dinyatakan sebagai:

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

Nilai minimum posisi 56dp dapat dinyatakan sebagai:

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

Tentukan batas kecepatan

Batas kecepatan juga diteruskan ke konstruktor AnchoredDraggableState, dan juga dinyatakan sebagai lambda:

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

Perubahan pada platform API

Temukan ringkasan perubahan pada platform API di bawah.

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]

T/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)

Meneruskan ke konstruktor AnchoredDraggableState sebagai positionalThreshold

resistance: ResistanceConfig? = …

Belum didukung. Lihat b/288084801 untuk mengetahui status terbaru.

velocityThreshold: Dp = 125.dp

Diteruskan ke konstruktor AnchoredDraggable