SwipeToDismiss
Material | Material 3 |
---|---|
![]() | ![]() |
El componente SwipeToDismiss
permite a los usuarios realizar acciones en un elemento
de la lista deslizando horizontalmente sobre él (generalmente hacia la izquierda o la derecha).
Aunque comúnmente se utiliza para eliminar elementos, este componente también puede configurarse
para llevar a cabo otras acciones como archivar, marcar como leído/no leído, agregar a favoritos,
entre otros. Esta interacción mejora la experiencia del usuario al proporcionar una forma rápida y
eficiente de manejar elementos en una lista.
Implementación
Definición del componente
@Composablefun SwipeToDismiss( state: DismissState, background: @Composable RowScope.() -> Unit, dismissContent: @Composable RowScope.() -> Unit, modifier: Modifier = Modifier, directions: Set<DismissDirection> = setOf(EndToStart, StartToEnd), dismissThresholds: (DismissDirection) -> ThresholdConfig = { FixedThreshold(DISMISS_THRESHOLD) },)
Atributo | Descripción |
---|---|
state | Estado que controla el deslizamiento del elemento, indicando si ha sido descartado o no. |
background | Función lambda que define el contenido que se muestra detrás del elemento mientras se está deslizando. |
dismissContent | Función lambda que define el contenido principal del elemento que se puede deslizar. |
modifier | Modificador opcional para personalizar el estilo y el diseño del componente. |
directions | Conjunto de direcciones en las que se permite el deslizamiento del elemento (por defecto, de derecha a izquierda y de izquierda a derecha). |
dismissThresholds | Configura que tanto deber ser deslizado un elemento para realizar una acción. |
@Composablefun SwipeToDismissBox( state: SwipeToDismissBoxState, backgroundContent: @Composable RowScope.() -> Unit, modifier: Modifier = Modifier, enableDismissFromStartToEnd: Boolean = true, enableDismissFromEndToStart: Boolean = true, gesturesEnabled: Boolean = true, content: @Composable RowScope.() -> Unit): Unit
Atributo | Descripción |
---|---|
state | Estado que controla el deslizamiento del elemento, indicando si ha sido descartado o no. |
backgroundContent | Función lambda que define el contenido que se muestra detrás del elemento mientras se está deslizando. |
modifier | Modificador opcional para personalizar el estilo y el diseño del componente. |
enableDismissFromStartToEnd | Indica si se puede deslizar de izquierda a derecha |
enableDismissFromEndToStart | Indica si se puede delizar de derecha a izquierda |
gesturesEnabled | Indica si se puede interactuar con gestos |
content | Función lambda que define el contenido principal del elemento que se puede deslizar. |
Ejemplos

@file:OptIn(ExperimentalMaterialApi::class)
package com.example.swipes
import android.os.Bundleimport androidx.activity.ComponentActivityimport androidx.activity.compose.setContentimport androidx.activity.enableEdgeToEdgeimport androidx.compose.animation.AnimatedVisibilityimport androidx.compose.animation.core.tweenimport androidx.compose.animation.fadeOutimport androidx.compose.animation.shrinkVerticallyimport androidx.compose.foundation.backgroundimport androidx.compose.foundation.layout.Boximport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.foundation.lazy.LazyColumnimport androidx.compose.foundation.lazy.itemsimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Deleteimport androidx.compose.material.DismissDirectionimport androidx.compose.material.DismissStateimport androidx.compose.material.DismissValueimport androidx.compose.material.ExperimentalMaterialApiimport androidx.compose.material.Iconimport androidx.compose.material.MaterialThemeimport androidx.compose.material.Scaffoldimport androidx.compose.material.Surfaceimport androidx.compose.material.SwipeToDismissimport androidx.compose.material.Textimport androidx.compose.material.TopAppBarimport androidx.compose.material.rememberDismissStateimport androidx.compose.runtime.Composableimport androidx.compose.runtime.LaunchedEffectimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateListOfimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.saveable.Saverimport androidx.compose.runtime.saveable.listSaverimport androidx.compose.runtime.saveable.rememberSaveableimport androidx.compose.runtime.setValueimport androidx.compose.ui.Alignmentimport androidx.compose.ui.Modifierimport androidx.compose.ui.graphics.Colorimport androidx.compose.ui.unit.dpimport com.example.swipes.ui.theme.SwipesThemeimport kotlinx.coroutines.delay
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { SwipesTheme { Surface( modifier = Modifier .fillMaxSize() .background(MaterialTheme.colors.background), color = MaterialTheme.colors.background ) { val programmingLanguages = rememberSaveable( saver = ListSaver ) { mutableStateListOf( "Kotlin", "C++", "C#", "Java", "JavaScript", "Swift", "Python" ) } Scaffold ( topBar = { TopAppBar( title = { Text(text = "Programming Languages") } ) } ){padding -> LazyColumn( modifier = Modifier .fillMaxSize() .padding(padding) ) { items(items = programmingLanguages, key = { it }) { language -> SwipeToDeleteContainer( item = language, onDelete = { programmingLanguages.remove(language) }) { item -> Text( text = item, modifier = Modifier .fillMaxWidth() .background(MaterialTheme.colors.background) .padding(16.dp), color = MaterialTheme.colors.primary ) } } } } } } } }}
@Composablefun <T> SwipeToDeleteContainer( item: T, onDelete: (T) -> Unit, animationDuration: Int = 500, content: @Composable (T) -> Unit) { var isRemoved by remember { mutableStateOf(false) } val dismissState = rememberDismissState(confirmStateChange = { value -> if (value == DismissValue.DismissedToStart) { isRemoved = true true } else { false } })
LaunchedEffect(key1 = isRemoved) { if (isRemoved) { delay(animationDuration.toLong()) onDelete(item) } }
AnimatedVisibility( visible = !isRemoved, exit = shrinkVertically( animationSpec = tween(durationMillis = animationDuration), shrinkTowards = Alignment.Top ) + fadeOut() ) { SwipeToDismiss( state = dismissState, background = { DeleteBackground(swipeDismissState = dismissState) }, dismissContent = { content(item) }, directions = setOf(DismissDirection.EndToStart) ) }}
@Composablefun DeleteBackground(swipeDismissState: DismissState) { val color = if (swipeDismissState.dismissDirection == DismissDirection.EndToStart) { Color.Red } else Color.Transparent
Box( modifier = Modifier .fillMaxSize() .background(color) .padding(16.dp), contentAlignment = Alignment.CenterEnd ) { Icon( imageVector = Icons.Default.Delete, contentDescription = null, tint = Color.White ) }}
val ListSaver: Saver<MutableList<String>, Any> = listSaver( save = { stateList -> stateList.toList() }, restore = { list -> mutableStateListOf<String>().apply { addAll(list) } })

@file:OptIn(ExperimentalMaterial3Api::class)
package com.example.swipes
import android.os.Bundleimport androidx.activity.ComponentActivityimport androidx.activity.compose.setContentimport androidx.activity.enableEdgeToEdgeimport androidx.compose.animation.AnimatedVisibilityimport androidx.compose.animation.animateColorAsStateimport androidx.compose.animation.core.tweenimport androidx.compose.animation.fadeOutimport androidx.compose.animation.shrinkVerticallyimport androidx.compose.foundation.backgroundimport androidx.compose.foundation.layout.Boximport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.foundation.lazy.LazyColumnimport androidx.compose.foundation.lazy.itemsimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Deleteimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.MaterialThemeimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.Surfaceimport androidx.compose.material3.SwipeToDismissBoximport androidx.compose.material3.SwipeToDismissBoxStateimport androidx.compose.material3.SwipeToDismissBoxValueimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.material3.rememberSwipeToDismissBoxStateimport androidx.compose.runtime.Composableimport androidx.compose.runtime.LaunchedEffectimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateListOfimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.saveable.Saverimport androidx.compose.runtime.saveable.listSaverimport androidx.compose.runtime.saveable.rememberSaveableimport androidx.compose.runtime.setValueimport androidx.compose.ui.Alignmentimport androidx.compose.ui.Modifierimport androidx.compose.ui.graphics.Colorimport androidx.compose.ui.unit.dpimport com.example.swipes.ui.theme.SwipesThemeimport kotlinx.coroutines.delay
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { SwipesTheme { Surface( modifier = Modifier .fillMaxSize() .background(MaterialTheme.colorScheme.background), color = MaterialTheme.colorScheme.background ) { val programmingLanguages = rememberSaveable( saver = ListSaver ) { mutableStateListOf( "Kotlin", "C++", "C#", "Java", "JavaScript", "Swift", "Python" ) } Scaffold ( topBar = { TopAppBar( title = { Text(text = "Programming Languages") } ) } ){padding -> LazyColumn( modifier = Modifier .fillMaxSize() .padding(padding) ) { items(items = programmingLanguages, key = { it }) { language -> SwipeToDeleteContainer( item = language, onDelete = { programmingLanguages.remove(language) }) { item -> Text( text = item, modifier = Modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.background) .padding(16.dp), color = MaterialTheme.colorScheme.primary ) } } } } } } } }}
@Composablefun <T> SwipeToDeleteContainer( item: T, onDelete: (T) -> Unit, animationDuration: Int = 500, content: @Composable (T) -> Unit ) { var isRemoved by remember { mutableStateOf(false) } val dismissState = rememberSwipeToDismissBoxState(confirmValueChange = { value -> if (value == SwipeToDismissBoxValue.EndToStart) { isRemoved = true true } else { false } })
LaunchedEffect(key1 = isRemoved) { if (isRemoved) { delay(animationDuration.toLong()) onDelete(item) } }
AnimatedVisibility( visible = !isRemoved, exit = shrinkVertically( animationSpec = tween(durationMillis = animationDuration), shrinkTowards = Alignment.Top ) + fadeOut() ) { SwipeToDismissBox( state = dismissState, backgroundContent = { DeleteBackground(swipeDismissState = dismissState) }, content = { content(item) }, enableDismissFromStartToEnd = false, ) }}
@Composablefun DeleteBackground(swipeDismissState: SwipeToDismissBoxState) { val color by animateColorAsState( when (swipeDismissState.targetValue) { SwipeToDismissBoxValue.Settled -> Color.LightGray SwipeToDismissBoxValue.StartToEnd -> Color.Green SwipeToDismissBoxValue.EndToStart -> Color.Red }, label = "" )
Box( modifier = Modifier .fillMaxSize() .background(color) .padding(16.dp), contentAlignment = Alignment.CenterEnd ) { Icon( imageVector = Icons.Default.Delete, contentDescription = null, tint = Color.White ) }}
val ListSaver: Saver<MutableList<String>, Any> = listSaver( save = { stateList -> stateList.toList() }, restore = { list -> mutableStateListOf<String>().apply { addAll(list) } })