Composable Component

SwipeToDismiss

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

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!") },
                        )
                    }
                },
            )
        }
    }
}