SwipeToReveal
SwipeToReveal
Material composable. This adds the option to configure up to two additional
actions on a Composable: a mandatory primaryAction
and an optional secondaryAction
. These
actions are initially hidden (unless RevealState
is created with an initial value other than
RevealValue.Covered
) and revealed only when the content
is swiped - the action buttons can
then be clicked. A full swipe of the content
triggers the onSwipePrimaryAction
callback,
which is expected to match the primaryAction
's onClick callback. Custom accessibility actions
should always be added to the content using Modifier.semantics
- examples are shown in the code
samples.
@Composable
public fun SwipeToReveal(
primaryAction: @Composable SwipeToRevealScope.() -> Unit,
onSwipePrimaryAction: () -> Unit,
modifier: Modifier = Modifier,
secondaryAction: (@Composable SwipeToRevealScope.() -> Unit)? = null,
undoPrimaryAction: (@Composable SwipeToRevealScope.() -> Unit)? = null,
undoSecondaryAction: (@Composable SwipeToRevealScope.() -> Unit)? = null,
revealState: RevealState = rememberRevealState(),
revealDirection: RevealDirection = RevealDirection.RightToLeft,
hasPartiallyRevealedState: Boolean = true,
gestureInclusion: GestureInclusion =
if (revealDirection == Bidirectional) {
bidirectionalGestureInclusion
} else {
gestureInclusion(revealState)
},
content: @Composable () -> Unit,
)
Parameters
primaryAction | The primary action of this component. SwipeToRevealScope.PrimaryActionButton should be used to create a button for this slot. If undoPrimaryAction is provided, the undo button will be displayed after SwipeToReveal has animated to the revealed state and the primary action button has been hidden. |
onSwipePrimaryAction | A callback which will be triggered when a full swipe is performed. It is expected that the same callback is given to SwipeToRevealScope.PrimaryActionButton s onClick action. If undoPrimaryAction is provided, that will be displayed after the swipe gesture is completed. |
modifier | Modifier to be applied on the composable. |
secondaryAction | Optional secondary action of this component. SwipeToRevealScope.SecondaryActionButton should be used to create a button for this slot. If undoSecondaryAction is provided, the undo button will be displayed after SwipeToReveal has animated to the revealed state and the secondary action button has been hidden. |
undoPrimaryAction | Optional undo action for the primary action of this component. SwipeToRevealScope.UndoActionButton should be used to create a button for this slot. Displayed after SwipeToReveal has animated to the revealed state and the primary action button has been hidden. |
undoSecondaryAction | Optional undo action for the secondary action of this component, displayed after SwipeToReveal has animated to the revealed state and the secondary action button has been hidden. undoSecondaryAction is ignored if the secondary action has not been specified. SwipeToRevealScope.UndoActionButton should be used to create a button for this slot. |
revealState | RevealState of the SwipeToReveal . |
revealDirection | The direction from which SwipeToReveal can reveal the actions. It is strongly recommended to respect the default value of RightToLeft to avoid conflicting with the system-side swipe-to-dismiss gesture. |
hasPartiallyRevealedState | Determines whether the intermediate states RightRevealing and LeftRevealing are used. These indicate a settled state, where the primary action is partially revealed. By default, partially revealed state is allowed for single actions - set to false to make actions complete when swiped instead. This flag has no effect if a secondary action is provided (when there are two actions, the component always allows the partially revealed states). |
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. Custom accessibility actions should always be added to the content using Modifier.semantics - examples are shown in the code samples. |
Code Examples
SwipeToRevealNoPartialRevealWithScalingLazyColumnSample
@Preview
@Composable
fun SwipeToRevealNoPartialRevealWithScalingLazyColumnSample() {
val slcState = rememberScalingLazyListState()
ScalingLazyColumn(
state = slcState,
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 20.dp),
modifier = Modifier.background(Color.Black),
) {
items(count = 100) { index ->
SwipeToReveal(
hasPartiallyRevealedState = false,
primaryAction = {
PrimaryActionButton(
onClick = { /* This block is called when the primary action is executed. */
},
icon = { Icon(Icons.Outlined.Delete, contentDescription = "Delete") },
text = { Text("Delete") },
)
},
onSwipePrimaryAction = { /* This block is called when the full swipe gesture is performed. */
},
undoPrimaryAction = {
UndoActionButton(
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())
}
}
}
}
}
SwipeToRevealSample
@Composable
fun SwipeToRevealSample() {
SwipeToReveal(
primaryAction = {
PrimaryActionButton(
onClick = { /* This block is called when the primary action is executed. */ },
icon = { Icon(Icons.Outlined.Delete, contentDescription = "Delete") },
text = { Text("Delete") },
)
},
onSwipePrimaryAction = { /* This block is called when the full swipe gesture is performed. */
},
secondaryAction = {
SecondaryActionButton(
onClick = { /* This block is called when the secondary action is executed. */ },
icon = { Icon(Icons.Outlined.MoreVert, contentDescription = "Options") },
)
},
undoPrimaryAction = {
UndoActionButton(
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(
primaryAction = {
PrimaryActionButton(
onClick = { /* This block is called when the primary action is executed. */ },
icon = { Icon(Icons.Outlined.Delete, contentDescription = "Delete") },
text = { Text("Delete") },
modifier = Modifier.height(SwipeToRevealDefaults.LargeActionButtonHeight),
)
},
onSwipePrimaryAction = { /* This block is called when the full swipe gesture is performed. */
},
undoPrimaryAction = {
UndoActionButton(
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(),
)
}
}
}
SwipeToRevealWithScalingLazyColumnSample
@Preview
@Composable
fun SwipeToRevealWithScalingLazyColumnSample() {
val slcState = rememberScalingLazyListState()
val coroutineScope = rememberCoroutineScope()
ScalingLazyColumn(
state = slcState,
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 20.dp),
modifier = Modifier.background(Color.Black),
) {
items(count = 100) { index ->
val revealState = rememberRevealState()
// SwipeToReveal should be reset to covered when scrolling occurs.
LaunchedEffect(slcState.isScrollInProgress) {
if (
slcState.isScrollInProgress && revealState.currentValue != RevealValue.Covered
) {
coroutineScope.launch {
revealState.animateTo(targetValue = RevealValue.Covered)
}
}
}
SwipeToReveal(
revealState = revealState,
primaryAction = {
PrimaryActionButton(
onClick = { /* This block is called when the primary action is executed. */
},
icon = { Icon(Icons.Outlined.Delete, contentDescription = "Delete") },
text = { Text("Delete") },
)
},
onSwipePrimaryAction = { /* This block is called when the full swipe gesture is performed. */
},
secondaryAction = {
SecondaryActionButton(
onClick = { /* This block is called when the secondary action is executed. */
},
icon = { Icon(Icons.Outlined.MoreVert, contentDescription = "Options") },
)
},
undoPrimaryAction = {
UndoActionButton(
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())
}
}
}
}
}
SwipeToRevealWithTransformingLazyColumnSample
@Preview
@Composable
fun SwipeToRevealWithTransformingLazyColumnSample() {
val transformationSpec = rememberTransformationSpec()
val tlcState = rememberTransformingLazyColumnState()
val coroutineScope = rememberCoroutineScope()
TransformingLazyColumn(
state = tlcState,
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 20.dp),
modifier = Modifier.background(Color.Black),
) {
items(count = 100) { index ->
val revealState = rememberRevealState()
// SwipeToReveal should be reset to covered when scrolling occurs.
LaunchedEffect(tlcState.isScrollInProgress) {
if (
tlcState.isScrollInProgress && revealState.currentValue != RevealValue.Covered
) {
coroutineScope.launch {
revealState.animateTo(targetValue = RevealValue.Covered)
}
}
}
SwipeToReveal(
primaryAction = {
PrimaryActionButton(
onClick = { /* Called when the primary action is executed. */ },
icon = { Icon(Icons.Outlined.Delete, contentDescription = "Delete") },
text = { Text("Delete") },
modifier = Modifier.height(SwipeToRevealDefaults.LargeActionButtonHeight),
)
},
revealState = revealState,
onSwipePrimaryAction = { /* This block is called when the full swipe gesture is performed. */
},
modifier =
Modifier.transformedHeight(this@items, transformationSpec).graphicsLayer {
with(transformationSpec) { applyContainerTransformation(scrollProgress) }
// Is needed to disable clipping.
compositingStrategy = CompositingStrategy.ModulateAlpha
clip = false
},
) {
TitleCard(
onClick = {},
title = { Text("Message #$index") },
subtitle = { Text("Subtitle") },
modifier =
Modifier.semantics {
// Use custom actions to make the primary action accessible
customActions =
listOf(
CustomAccessibilityAction("Delete") {
/* Add the primary action click handler here */
true
}
)
},
) {
Text("Message body which extends over multiple lines to extend the card")
}
}
}
}
}
Create your own Component Library
Material Components are meant to be used as is and they do not allow customizations. To build your own Jetpack Compose component library use Compose Unstyled