SwipeToDismiss
Composable Component
A composable that can be dismissed by swiping left or right.
Common
@Composable
@ExperimentalMaterialApi
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,
) =
BoxWithConstraints(modifier) {
val width = constraints.maxWidth.toFloat()
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
val anchors = mutableMapOf(0f to Default)
if (StartToEnd in directions) anchors += width to DismissedToEnd
if (EndToStart in directions) anchors += -width to DismissedToStart
val thresholds = { from: DismissValue, to: DismissValue ->
dismissThresholds(getDismissDirection(from, to)!!)
}
val minFactor =
if (EndToStart in directions) StandardResistanceFactor else StiffResistanceFactor
val maxFactor =
if (StartToEnd in directions) StandardResistanceFactor else StiffResistanceFactor
Box(
Modifier.swipeable(
state = state,
anchors = anchors,
thresholds = thresholds,
orientation = Orientation.Horizontal,
enabled = state.currentValue == Default,
reverseDirection = isRtl,
resistance =
ResistanceConfig(
basis = width,
factorAtMin = minFactor,
factorAtMax = maxFactor,
),
)
) {
Row(content = background, modifier = Modifier.matchParentSize())
Row(
content = dismissContent,
modifier = Modifier.offset { IntOffset(state.offset.value.roundToInt(), 0) },
)
}
}
Parameters
state | The state of this component. |
modifier | Optional Modifier for this component. |
directions | The set of directions in which the component can be dismissed. |
dismissThresholds | The thresholds the item needs to be swiped in order to be dismissed. |
background | A 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. |
dismissContent | The content that can be dismissed. |
Code Examples
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!") },
)
}
},
)
}
}
}