El componente SwipeToDismissBox permite que un usuario descarte o actualice un elemento deslizándolo hacia la izquierda o la derecha.
Superficie de la API
Usa el elemento de componibilidad SwipeToDismissBox para implementar acciones que se activan con gestos de deslizamiento. Entre los parámetros clave, se incluyen los siguientes:
state: Es el estadoSwipeToDismissBoxStatecreado para almacenar el valor producido por los cálculos en el elemento deslizable, que activa eventos cuando se produce.backgroundContent: Es un elemento componible personalizable que se muestra detrás del contenido del elemento que se revela cuando se desliza el contenido.
Ejemplo básico: Actualiza o descarta con un deslizamiento
Los fragmentos de este ejemplo muestran una implementación de deslizamiento que actualiza el elemento cuando se desliza de principio a fin o lo descarta cuando se desliza de fin a principio.
data class TodoItem( val itemDescription: String, var isItemDone: Boolean = false )
@Composable fun TodoListItem( todoItem: TodoItem, onToggleDone: (TodoItem) -> Unit, onRemove: (TodoItem) -> Unit, modifier: Modifier = Modifier, ) { val swipeToDismissBoxState = rememberSwipeToDismissBoxState( confirmValueChange = { if (it == StartToEnd) onToggleDone(todoItem) else if (it == EndToStart) onRemove(todoItem) // Reset item when toggling done status it != StartToEnd } ) SwipeToDismissBox( state = swipeToDismissBoxState, modifier = modifier.fillMaxSize(), backgroundContent = { when (swipeToDismissBoxState.dismissDirection) { StartToEnd -> { Icon( painter = rememberVectorPainter(if (todoItem.isItemDone) AppIcons.CheckBox else AppIcons.CheckBoxOutlineBlank), contentDescription = if (todoItem.isItemDone) "Done" else "Not done", modifier = Modifier .fillMaxSize() .background(Color.Blue) .wrapContentSize(Alignment.CenterStart) .padding(12.dp), tint = Color.White ) } EndToStart -> { Icon( painter = rememberVectorPainter(AppIcons.Delete), contentDescription = "Remove item", modifier = Modifier .fillMaxSize() .background(Color.Red) .wrapContentSize(Alignment.CenterEnd) .padding(12.dp), tint = Color.White ) } Settled -> {} } } ) { ListItem( headlineContent = { Text(todoItem.itemDescription) }, supportingContent = { Text("swipe me to update or remove.") } ) } }
Puntos clave sobre el código
swipeToDismissBoxStateadministra el estado del componente. Activa la devolución de llamadaconfirmValueChangeuna vez que se completa la interacción con el elemento. El cuerpo de la devolución de llamada controla las diferentes acciones posibles. La devolución de llamada muestra un valor booleano que le indica al componente si debe mostrar una animación de descarte. En este caso, ocurre lo siguiente:- Si se desliza el elemento de principio a fin, se llama a la lambda
onToggleDoney se pasa eltodoItemactual. Esto corresponde a la actualización del elemento de tareas pendientes. - Si el elemento se desliza de un extremo al otro, llama a la lambda
onRemovey pasa eltodoItemactual. Esto corresponde a borrar el elemento de tareas pendientes. it != StartToEnd: Esta línea devuelvetruesi la dirección de deslizamiento no esStartToEnd, yfalseen caso contrario. Si se devuelvefalse, se evita que elSwipeToDismissBoxdesaparezca de inmediato después de un deslizamiento para "marcar como completado", lo que permite una confirmación visual o una animación.
- Si se desliza el elemento de principio a fin, se llama a la lambda
SwipeToDismissBoxhabilita las interacciones de deslizamiento horizontal en cada elemento. En reposo, muestra el contenido interno del componente, pero cuando un usuario comienza a deslizar el dedo, el contenido se quita y aparece elbackgroundContent. Tanto el contenido normal como elbackgroundContentobtienen las restricciones completas del contenedor superior para renderizarse. Elcontentse dibuja sobre elbackgroundContent. En este caso, ocurre lo siguiente:backgroundContentse implementa como unIconcon un color de fondo basado enSwipeToDismissBoxValue:Bluecuando se desliza el dedo haciaStartToEnd: se activa o desactiva un elemento de tareas pendientes.Redcuando se deslizaEndToStart: se borra un elemento de la lista de tareas pendientes.- No se muestra nada en segundo plano para
Settled; cuando no se desliza el elemento, no se muestra nada en segundo plano. - Del mismo modo, el
Iconque se muestra se adapta a la dirección del deslizamiento: StartToEndmuestra un ícono deCheckBoxcuando se completa el elemento de tareas pendientes y un ícono deCheckBoxOutlineBlankcuando no se completa.EndToStartmuestra un ícono deDelete.
@Composable fun SwipeItemExample() { val todoItems = remember { mutableStateListOf( TodoItem("Pay bills"), TodoItem("Buy groceries"), TodoItem("Go to gym"), TodoItem("Get dinner") ) } LazyColumn { items( items = todoItems, key = { it.itemDescription } ) { todoItem -> TodoListItem( todoItem = todoItem, onToggleDone = { todoItem -> todoItem.isItemDone = !todoItem.isItemDone }, onRemove = { todoItem -> todoItems -= todoItem }, modifier = Modifier.animateItem() ) } } }
Puntos clave sobre el código
mutableStateListOf(...)crea una lista observable que puede contener objetosTodoItem. Cuando se agrega o quita un elemento de esta lista, Compose recompone las partes de la IU que dependen de ella.- Dentro de
mutableStateListOf(), se inicializan cuatro objetosTodoItemcon sus respectivas descripciones: "Pay bills", "Buy groceries", "Go to gym" y "Get dinner".
- Dentro de
LazyColumnmuestra una lista de desplazamiento vertical detodoItems.onToggleDone = { todoItem -> ... }es una función de devolución de llamada que se invoca desdeTodoListItemcuando el usuario marca un objeto como completado. Actualiza la propiedadisItemDonedetodoItem. ComotodoItemses unmutableStateListOf, este cambio activa una recomposición, lo que actualiza la IU.onRemove = { todoItem -> ... }es una función de devolución de llamada que se activa cuando el usuario quita el elemento. Quita eltodoItemespecífico de la listatodoItems. Esto también provoca una recomposición, y el elemento se quitará de la lista que se muestra.- Se aplica un modificador
animateItema cadaTodoListItempara que se llame alplacementSpecdel modificador cuando se descarte el elemento. Esto anima la eliminación del elemento, así como el reordenamiento de otros elementos de la lista.
Resultado
En el siguiente video, se muestra la funcionalidad básica de deslizar para descartar de los fragmentos anteriores:
Consulta el archivo de origen de GitHub para ver el código de muestra completo.
Ejemplo avanzado: Anima el color de fondo cuando se desliza el dedo
En los siguientes fragmentos, se muestra cómo incorporar un umbral posicional para animar el color de fondo de un elemento cuando se desliza el dedo.
data class TodoItem( val itemDescription: String, var isItemDone: Boolean = false )
@Composable fun TodoListItemWithAnimation( todoItem: TodoItem, onToggleDone: (TodoItem) -> Unit, onRemove: (TodoItem) -> Unit, modifier: Modifier = Modifier, ) { val swipeToDismissBoxState = rememberSwipeToDismissBoxState( confirmValueChange = { if (it == StartToEnd) onToggleDone(todoItem) else if (it == EndToStart) onRemove(todoItem) // Reset item when toggling done status it != StartToEnd } ) SwipeToDismissBox( state = swipeToDismissBoxState, modifier = modifier.fillMaxSize(), backgroundContent = { when (swipeToDismissBoxState.dismissDirection) { StartToEnd -> { Icon( painter = rememberVectorPainter(if (todoItem.isItemDone) AppIcons.CheckBox else AppIcons.CheckBoxOutlineBlank), contentDescription = if (todoItem.isItemDone) "Done" else "Not done", modifier = Modifier .fillMaxSize() .drawBehind { drawRect(lerp(Color.LightGray, Color.Blue, swipeToDismissBoxState.progress)) } .wrapContentSize(Alignment.CenterStart) .padding(12.dp), tint = Color.White ) } EndToStart -> { Icon( painter = rememberVectorPainter(AppIcons.Delete), contentDescription = "Remove item", modifier = Modifier .fillMaxSize() .background(lerp(Color.LightGray, Color.Red, swipeToDismissBoxState.progress)) .wrapContentSize(Alignment.CenterEnd) .padding(12.dp), tint = Color.White ) } Settled -> {} } } ) { OutlinedCard(shape = RectangleShape) { ListItem( headlineContent = { Text(todoItem.itemDescription) }, supportingContent = { Text("swipe me to update or remove.") } ) } } }
Puntos clave sobre el código
drawBehinddibuja directamente en el lienzo detrás del contenido del elemento componibleIcon.drawRect()dibuja un rectángulo en el lienzo y completa todos los límites del alcance del dibujo con elColorespecificado.
- Cuando se desliza el dedo, el color de fondo del elemento cambia de forma fluida con
lerp.- Para un deslizamiento desde
StartToEnd, el color de fondo cambia gradualmente de gris claro a azul. - Para un deslizamiento desde
EndToStart, el color de fondo cambia gradualmente de gris claro a rojo. - La cantidad de transición de un color al siguiente se determina con
swipeToDismissBoxState.progress.
- Para un deslizamiento desde
OutlinedCardagrega una separación visual sutil entre los elementos de la lista.
@Composable private fun SwipeItemWithAnimationExample() { val todoItems = remember { mutableStateListOf( TodoItem("Pay bills"), TodoItem("Buy groceries"), TodoItem("Go to gym"), TodoItem("Get dinner") ) } LazyColumn { items( items = todoItems, key = { it.itemDescription } ) { todoItem -> TodoListItemWithAnimation( todoItem = todoItem, onToggleDone = { todoItem -> todoItem.isItemDone = !todoItem.isItemDone }, onRemove = { todoItem -> todoItems -= todoItem }, modifier = Modifier.animateItem() ) } } }
Puntos clave sobre el código
- Para conocer los puntos clave sobre este código, consulta Puntos clave de una sección anterior, en la que se describe un fragmento de código idéntico.
Resultado
En el siguiente video, se muestra la funcionalidad avanzada con un color de fondo animado:
Consulta el archivo de origen de GitHub para ver el código de muestra completo.