SwipeToReveal
Component in Wear Material 3 Compose
[SwipeToReveal] Material composable. This adds the option to configure up to two additional actions on a Composable: a mandatory [SwipeToRevealScope.primaryAction] and an optional [SwipeToRevealScope.secondaryAction]. These actions are initially hidden and revealed only when the [content] is swiped. These additional actions can be triggered by clicking on them after they are revealed. [SwipeToRevealScope.primaryAction] will be triggered on full swipe of the [content].
For actions like "Delete", consider adding [SwipeToRevealScope.undoPrimaryAction] (displayed when the [SwipeToRevealScope.primaryAction] is activated). Adding undo composables allow users to undo the action that they just performed.
[SwipeToReveal] composable adds the [CustomAccessibilityAction]s using the labels from primary and secondary actions.
Example of [SwipeToReveal] with primary and secondary actions
Last updated:
Installation
dependencies {
implementation("androidx.wear.compose:compose-material3:1.0.0-alpha34")
}
Overloads
@Composable
fun SwipeToReveal(
actions: SwipeToRevealScope.() -> Unit,
modifier: Modifier = Modifier,
revealState: RevealState = rememberRevealState(anchors = SwipeToRevealDefaults.anchors()),
actionButtonHeight: Dp = SwipeToRevealDefaults.SmallActionButtonHeight,
gestureInclusion: GestureInclusion =
if (revealState.hasBidirectionalAnchors()) {
bidirectionalGestureInclusion()
} else {
gestureInclusion()
},
content: @Composable () -> Unit,
)
Parameters
name | description |
---|---|
actions | Actions of the [SwipeToReveal] composable, such as [SwipeToRevealScope.primaryAction]. [actions] should always include exactly one [SwipeToRevealScope.primaryAction]. [SwipeToRevealScope.secondaryAction], [SwipeToRevealScope.undoPrimaryAction] and [SwipeToRevealScope.undoSecondaryAction] are optional. |
modifier | [Modifier] to be applied on the composable |
revealState | [RevealState] of the [SwipeToReveal] |
actionButtonHeight | Desired height of the revealed action buttons. In case the content is a Button composable, it's suggested to use [SwipeToRevealDefaults.SmallActionButtonHeight], and for a Card composable, it's suggested to use [SwipeToRevealDefaults.LargeActionButtonHeight]. |
gestureInclusion | Provides fine-grained control so that touch gestures can be excluded when they start in a certain region. An instance of [GestureInclusion] can be passed in here which will determine via [GestureInclusion.ignoreGestureStart] whether the gesture should proceed or not. By default, [gestureInclusion] allows gestures everywhere for when [revealState] contains anchors for both directions (see [bidirectionalGestureInclusion]). If it doesn't, then it allows gestures everywhere, except a zone on the left edge, which is used for swipe-to-dismiss (see [gestureInclusion]). |
content | The content that will be initially displayed over the other actions provided. |
Code Examples
SwipeToRevealSample
@Composable
fun SwipeToRevealSample() {
SwipeToReveal(
// Use the double action anchor width when revealing two actions
revealState =
rememberRevealState(
anchors =
SwipeToRevealDefaults.anchors(
anchorWidth = SwipeToRevealDefaults.DoubleActionAnchorWidth,
)
),
actions = {
primaryAction(
onClick = { /* This block is called when the primary action is executed. */ },
icon = { Icon(Icons.Outlined.Delete, contentDescription = "Delete") },
text = { Text("Delete") }
)
secondaryAction(
onClick = { /* This block is called when the secondary action is executed. */ },
icon = { Icon(Icons.Outlined.MoreVert, contentDescription = "Options") }
)
undoPrimaryAction(
onClick = { /* This block is called when the undo primary action is executed. */ },
text = { Text("Undo Delete") },
)
}
) {
Button(
modifier =
Modifier.fillMaxWidth().semantics {
// Use custom actions to make the primary and secondary actions accessible
customActions =
listOf(
CustomAccessibilityAction("Delete") {
/* Add the primary action click handler here */
true
},
CustomAccessibilityAction("Options") {
/* Add the secondary click handler here */
true
}
)
},
onClick = {}
) {
Text("This Button has two actions", modifier = Modifier.fillMaxSize())
}
}
}
SwipeToRevealSingleActionCardSample
@Composable
fun SwipeToRevealSingleActionCardSample() {
SwipeToReveal(
actionButtonHeight = SwipeToRevealDefaults.LargeActionButtonHeight,
actions = {
primaryAction(
onClick = { /* This block is called when the primary action is executed. */ },
icon = { Icon(Icons.Outlined.Delete, contentDescription = "Delete") },
text = { Text("Delete") }
)
undoPrimaryAction(
onClick = { /* This block is called when the undo primary action is executed. */ },
text = { Text("Undo Delete") },
)
}
) {
Card(
modifier =
Modifier.fillMaxWidth().semantics {
// Use custom actions to make the primary action accessible
customActions =
listOf(
CustomAccessibilityAction("Delete") {
/* Add the primary action click handler here */
true
},
)
},
onClick = {}
) {
Text(
"This Card has one action, and the revealed button is taller",
modifier = Modifier.fillMaxSize()
)
}
}
}
SwipeToRevealNonAnchoredSample
@Composable
fun SwipeToRevealNonAnchoredSample() {
SwipeToReveal(
revealState =
rememberRevealState(
anchors = SwipeToRevealDefaults.anchors(useAnchoredActions = false)
),
actions = {
primaryAction(
onClick = { /* This block is called when the primary action is executed. */ },
icon = { Icon(Icons.Outlined.Delete, contentDescription = "Delete") },
text = { Text("Delete") }
)
undoPrimaryAction(
onClick = { /* This block is called when the undo primary action is executed. */ },
icon = { Icon(Icons.Outlined.Refresh, contentDescription = "Undo") },
text = { Text("Undo") },
)
}
) {
Button(
modifier =
Modifier.fillMaxWidth().semantics {
// Use custom actions to make the primary action accessible
customActions =
listOf(
CustomAccessibilityAction("Delete") {
/* Add the primary action click handler here */
true
},
)
},
onClick = {}
) {
Text("Swipe to execute the primary action.", modifier = Modifier.fillMaxSize())
}
}
}
SwipeToRevealWithTransformingLazyColumnSample
@Preview
@Composable
fun SwipeToRevealWithTransformingLazyColumnSample() {
val transformationSpec = rememberResponsiveTransformationSpec()
val tlcState = rememberTransformingLazyColumnState()
TransformingLazyColumn(
state = tlcState,
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 20.dp),
modifier = Modifier.background(Color.Black)
) {
items(count = 100) { index ->
val revealState =
rememberRevealState(
anchors =
SwipeToRevealDefaults.anchors(
anchorWidth = SwipeToRevealDefaults.DoubleActionAnchorWidth,
)
)
// SwipeToReveal is covered on scroll.
LaunchedEffect(tlcState.isScrollInProgress) {
if (
tlcState.isScrollInProgress && revealState.currentValue != RevealValue.Covered
) {
revealState.animateTo(targetValue = RevealValue.Covered)
}
}
SwipeToReveal(
revealState = revealState,
modifier =
Modifier.transformedHeight(transformationSpec::getTransformedHeight)
.graphicsLayer {
with(transformationSpec) {
applyContainerTransformation(scrollProgress)
}
// Is needed to disable clipping.
compositingStrategy = CompositingStrategy.ModulateAlpha
clip = false
},
actions = {
primaryAction(
onClick = { /* Called when the primary action is executed. */ },
icon = { Icon(Icons.Outlined.Delete, contentDescription = "Delete") },
text = { Text("Delete") }
)
}
) {
TransformExclusion {
TitleCard(
onClick = {},
title = { Text("Message #$index") },
subtitle = { Text("Body of the message") },
modifier =
Modifier.semantics {
// Use custom actions to make the primary action accessible
customActions =
listOf(
CustomAccessibilityAction("Delete") {
/* Add the primary action click handler here */
true
},
)
}
)
}
}
}
}
}