Passer de Swipeable à AnchoredDraggable

Swipeable est une API Material Compose qui vous aide à créer des composants pouvant être balayés entre des états distincts, tels que des bottom sheets, des panneaux ou des balayages pour ignorer. Pour mieux prendre en charge les cas d'utilisation avancés, tels que les ancrages qui dépendent de la taille d'un composant, un successeur a été publié dans Compose-Foundation 1.6.0-alpha01: AnchoredDraggable. AnchoredDraggable est une API Foundation permettant de créer des composants déplaçables avec des états ancrés, tels que des bottom sheets, des tiroirs ou des balayages pour ignorer.

Les API Swipeable de Material ont été abandonnées au profit de AnchoredDraggable de Foundation et seront supprimées dans une prochaine version. Ce guide explique comment passer des API Swipeable à AnchoredDraggable.

Migrer SwipeableState vers AnchoredDraggableState

Commencez par identifier les modifications apportées à votre titulaire de l'état. AnchoredDraggableState ne peut pas être hérité, et le décalage est représenté par Float.NaN avant son initialisation.

Modifier le conteneur d'état

AnchoredDraggableState est une classe finale, ce qui signifie qu'elle ne peut pas être héritée. Si votre composant existant hérite de SwipeableState, modifiez votre conteneur d'état pour qu'il contienne une référence à AnchoredDraggableState au lieu de l'hériter:

Balayable

class MySwitchState: SwipeableState()

AnchoredDraggable

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

Étant donné que votre détenteur d'état n'hérite plus de SwipeableState, vous devrez peut-être exposer vous-même les API. Les API les plus courantes que vous pouvez utiliser sont offset, progress, currentValue et targetValue.

Accéder au décalage

Contrairement à Swipeable, le offset de AnchoredDraggableState est Float.NaN avant d'être initialisé. Dans AnchoredDraggable, les ancrages peuvent être transmis au constructeur de AnchoredDraggableState ou mis à jour via AnchoredDraggableState#updateAnchors. Transmettre les ancrages au constructeur de AnchoredDraggableState initialise immédiatement le décalage.

Si vos ancrages dépendent de la mise en page ou peuvent changer, utilisez AnchoredDraggableState#updateAnchors pour éviter de recréer l'état lorsque les ancrages changent.

Si vous utilisez updateAnchors, le décalage sera Float.NaN avant de transmettre les ancrages à updateAnchors. Pour éviter de transmettre accidentellement Float.NaN aux composants, utilisez AnchoredDraggableState#requireOffset pour exiger que le décalage ait été initialisé lors de sa lecture. Cela vous permet de détecter rapidement les incohérences ou les bugs potentiels.

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

Migrer Modifier.swipeable vers Modifier.anchoredDraggable

Modifier.anchoredDraggable() remplace Modifier.swipeable. Certains des paramètres de Modifier.swipeable() ont été déplacés directement vers AnchoredDraggableState, comme décrit dans les sections suivantes.

Définir des ancres

Définissez les ancrages à l'aide de la méthode de compilation DraggableAnchors. Transmettez-les ensuite au constructeur de AnchoredDraggableState#updateAnchors ou AnchoredDraggableState:

Constructeur

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

Si les ancres sont statiques, transmettez-les au constructeur. Si elles dépendent de la mise en page ou ne sont pas statiques, utilisez updateAnchors.

Définir des seuils de position

Le type et le nom du paramètre "thresholds" ont changé. Au lieu d'avoir une interface ThresholdConfig distincte, AnchoredDraggableState dispose d'un paramètre positionalThreshold qui utilise une fonction lambda qui renvoie la position du seuil. Par exemple, un seuil de position de 50% peut être exprimé comme suit:

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

Un seuil de position de 56dp peut être exprimé comme suit:

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

Définir des seuils de vitesse

Les seuils de vitesse sont également transmis au constructeur de AnchoredDraggableState et exprimés sous forme de lambda:

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

Modifications apportées à la surface de l'API

Vous trouverez ci-dessous un aperçu des modifications apportées à la surface de l'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)

Transmis au constructeur AnchoredDraggableState en tant que positionalThreshold

resistance: ResistanceConfig? = …

Pas encore compatible. Pour connaître l'état le plus récent, consultez b/288084801.

velocityThreshold: Dp = 125.dp

Transmis au constructeur AnchoredDraggable