Bermigrasi dari Swipeable ke AnchoredDraggable

Swipeable adalah API Material Compose yang membantu Anda membuat komponen yang dapat di-swipe di antara status diskret, seperti sheet bawah, panel samping, atau swipe untuk menutup. Untuk mendukung kasus penggunaan lanjutan dengan lebih baik, seperti penanda yang bergantung pada ukuran komponen, penerusnya dipublikasikan di Compose-Foundation 1.6.0-alpha01: AnchoredDraggable. AnchoredDraggable adalah Foundation API untuk membuat komponen yang dapat ditarik dengan status yang ditambatkan, seperti sheet bawah, panel samping, atau geser untuk menutup.

API Swipeable Material tidak digunakan lagi dan digantikan dengan AnchoredDraggable Foundation serta akan dihapus dalam rilis mendatang. Panduan ini menjelaskan cara bermigrasi dari API Swipeable ke AnchoredDraggable.

Migrasikan SwipeableState ke AnchoredDraggableState

Mulailah dengan mengidentifikasi perubahan pada pemegang status Anda. AnchoredDraggableState tidak dapat diwarisi, dan offset direpresentasikan sebagai Float.NaN sebelum diinisialisasi.

Memperbarui holder status Anda

AnchoredDraggableState adalah class final, yang berarti tidak dapat diwariskan. Jika komponen yang ada mewarisi dari SwipeableState, perbarui holder status Anda untuk menyimpan referensi ke AnchoredDraggableState, bukan mewarisinya:

Dapat digeser

class MySwitchState: SwipeableState()

AnchoredDraggable

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

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

Mengakses offset

Tidak seperti di Swipeable, offset AnchoredDraggableState adalah Float.NaN sebelum diinisialisasi. Di AnchoredDraggable, penanda dapat diteruskan ke konstruktor AnchoredDraggableState atau diperbarui melalui AnchoredDraggableState#updateAnchors. Meneruskan penanda ke konstruktor AnchoredDraggableState akan segera menginisialisasi offset.

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

Jika Anda menggunakan updateAnchors, offset akan menjadi Float.NaN sebelum meneruskan anchor ke updateAnchors. Untuk menghindari pengiriman Float.NaN ke komponen secara tidak sengaja, gunakan AnchoredDraggableState#requireOffset untuk mewajibkan offset diinisialisasi saat membacanya. Hal ini membantu Anda menemukan inkonsistensi atau kemungkinan bug sejak awal.

@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. Beberapa parameter Modifier.swipeable() telah dipindahkan langsung ke AnchoredDraggableState, seperti yang dijelaskan di bagian berikut.

Menentukan anchor

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

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

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

Jika penanda bersifat statis, teruskan ke konstruktor. Jika bergantung pada tata letak, atau tidak statis, gunakan updateAnchors.

Menentukan batas posisi

Jenis dan nama parameter thresholds telah berubah. Daripada memiliki antarmuka ThresholdConfig terpisah, AnchoredDraggableState memiliki parameter positionalThreshold yang menggunakan fungsi lambda yang menampilkan posisi batas. Misalnya, nilai minimum posisi 50% dapat 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() } },
    ...
)

Menentukan batas kecepatan

Nilai minimum 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)

Diteruskan ke konstruktor AnchoredDraggableState sebagai positionalThreshold

resistance: ResistanceConfig? = …

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

velocityThreshold: Dp = 125.dp

Diteruskan ke konstruktor AnchoredDraggable