We just launched Compose Examples featuring over 150+ components! Check it out →

SwipeToDismiss

Common

Component in Material Compose

A composable that can be dismissed by swiping left or right.

Last updated:

Installation

dependencies {
   implementation("androidx.compose.material:material:1.8.0-alpha04")
}

Overloads

@Composable
@ExperimentalMaterialApi
@Suppress("ReferencesDeprecated")
fun SwipeToDismiss(
    state: DismissState,
    modifier: Modifier = Modifier,
    directions: Set<DismissDirection> = setOf(EndToStart, StartToEnd),
    dismissThresholds: (DismissDirection) -> ThresholdConfig = {
        FixedThreshold(DISMISS_THRESHOLD)
    },
    background: @Composable RowScope.() -> Unit,
    dismissContent: @Composable RowScope.() -> Unit
)

Parameters

namedescription
stateThe state of this component.
modifierOptional [Modifier] for this component.
directionsThe set of directions in which the component can be dismissed.
dismissThresholdsThe thresholds the item needs to be swiped in order to be dismissed.
backgroundA composable that is stacked behind the content and is exposed when the content is swiped. You can/should use the [state] to have different backgrounds on each side.
dismissContentThe content that can be dismissed.

Code Example

SwipeToDismissListItems

@Composable
@OptIn(ExperimentalMaterialApi::class)
fun SwipeToDismissListItems() {
    // This is an example of a list of dismissible items, similar to what you would see in an
    // email app. Swiping left reveals a 'delete' icon and swiping right reveals a 'done' icon.
    // The background will start as grey, but once the dismiss threshold is reached, the colour
    // will animate to red if you're swiping left or green if you're swiping right. When you let
    // go, the item will animate out of the way if you're swiping left (like deleting an email) or
    // back to its default position if you're swiping right (like marking an email as read/unread).
    LazyColumn {
        items(items) { item ->
            var unread by remember { mutableStateOf(false) }
            val dismissState =
                rememberDismissState(
                    confirmStateChange = {
                        if (it == DismissedToEnd) unread = !unread
                        it != DismissedToEnd
                    }
                )
            SwipeToDismiss(
                state = dismissState,
                modifier = Modifier.padding(vertical = 4.dp),
                directions = setOf(StartToEnd, EndToStart),
                background = {
                    val direction = dismissState.dismissDirection ?: return@SwipeToDismiss
                    val color by
                        animateColorAsState(
                            when (dismissState.targetValue) {
                                Default -> Color.LightGray
                                DismissedToEnd -> Color.Green
                                DismissedToStart -> Color.Red
                            }
                        )
                    val alignment =
                        when (direction) {
                            StartToEnd -> Alignment.CenterStart
                            EndToStart -> Alignment.CenterEnd
                        }
                    val icon =
                        when (direction) {
                            StartToEnd -> Icons.Default.Done
                            EndToStart -> Icons.Default.Delete
                        }
                    val scale by
                        animateFloatAsState(if (dismissState.targetValue == Default) 0.75f else 1f)

                    Box(
                        Modifier.fillMaxSize().background(color).padding(horizontal = 20.dp),
                        contentAlignment = alignment
                    ) {
                        Icon(
                            icon,
                            contentDescription = "Localized description",
                            modifier = Modifier.scale(scale)
                        )
                    }
                },
                dismissContent = {
                    Card(
                        elevation =
                            animateDpAsState(
                                    if (dismissState.dismissDirection != null) 4.dp else 0.dp
                                )
                                .value
                    ) {
                        ListItem(
                            text = {
                                Text(item, fontWeight = if (unread) FontWeight.Bold else null)
                            },
                            secondaryText = { Text("Swipe me left or right!") }
                        )
                    }
                }
            )
        }
    }
}
by @alexstyl