SwipeToReveal
Deprecated The SwipeToReveal component from the latest material library should be used instead. This will be removed in a future release of this library.
@ExperimentalWearFoundationApi
@Composable
public fun SwipeToReveal(
primaryAction: @Composable () -> Unit,
modifier: Modifier = Modifier,
onFullSwipe: () -> Unit = {},
state: RevealState = rememberRevealState(),
secondaryAction: (@Composable () -> Unit)? = null,
undoAction: (@Composable () -> Unit)? = null,
gestureInclusion: GestureInclusion = SwipeToRevealDefaults.gestureInclusion(state = state),
content: @Composable () -> Unit,
)
A composable that can be used to add extra actions to a composable (up to two) which will be revealed when the original composable is swiped to the left. This composable requires a primary swipe/click action, a secondary optional click action can also be provided.
When the composable reaches the state where all the actions are revealed and the swipe continues
beyond the positional threshold defined in RevealState
, the primary action is automatically
triggered.
An optional undo action can also be added. This undo action will be visible to users once the
RevealValue
becomes RevealValue.RightRevealed
.
It is strongly recommended to have icons represent the actions and maybe a text and icon for the undo action.
Example of SwipeToReveal with primary action and undo action
Example of SwipeToReveal using RevealState
to delay the appearance of primary action text
Example of SwipeToReveal used with Expandables
Parameters
primaryAction | The primary action that will be triggered in the event of a completed swipe. We also strongly recommend to trigger the action when it is clicked. |
modifier | Optional Modifier for this component. |
onFullSwipe | An optional lambda which will be triggered when a full swipe from either of the anchors is performed. |
state | The RevealState of this component. It can be used to customise the anchors and threshold config of the swipeable modifier which is applied. |
secondaryAction | An optional action that can be added to the component. We strongly recommend triggering the action when it is clicked. |
undoAction | The optional undo action that will be applied to the component once the the RevealState.currentValue becomes RevealValue.RightRevealed . |
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 except a zone on the left edge, which is used for swipe-to-dismiss (see SwipeToRevealDefaults.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
SwipeToRevealSample
@Composable
fun SwipeToRevealSample() {
val state = rememberRevealState()
val coroutineScope = rememberCoroutineScope()
SwipeToReveal(
state = state,
primaryAction = {
Box(
modifier =
Modifier.fillMaxSize().clickable {
/* Add the primary action */
coroutineScope.launch { state.animateTo(RevealValue.RightRevealed) }
},
contentAlignment = Alignment.Center,
) {
Icon(imageVector = Icons.Outlined.Delete, contentDescription = "Delete")
}
},
undoAction = {
Chip(
modifier = Modifier.fillMaxWidth(),
onClick = {
/* Add the undo action */
coroutineScope.launch { state.animateTo(RevealValue.Covered) }
},
colors = ChipDefaults.secondaryChipColors(),
label = { Text(text = "Undo") },
)
},
) {
Chip(
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 */
true
}
)
},
onClick = { /* the click action associated with chip */ },
colors = ChipDefaults.secondaryChipColors(),
label = { Text(text = "Swipe Me") },
)
}
}
SwipeToRevealWithDelayedText
@Composable
fun SwipeToRevealWithDelayedText() {
val state = rememberRevealState()
val coroutineScope = rememberCoroutineScope()
SwipeToReveal(
state = state,
primaryAction = {
Row(
modifier =
Modifier.fillMaxSize().clickable {
/* Add the primary action */
coroutineScope.launch { state.animateTo(RevealValue.RightRevealed) }
},
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
Icon(imageVector = Icons.Outlined.Delete, contentDescription = "Delete")
if (abs(state.offset) > state.revealThreshold) {
// Delay the text appearance so that it has enough space to be displayed
val textAlpha =
animateFloatAsState(
targetValue = 1f,
animationSpec = tween(durationMillis = 250, delayMillis = 250),
label = "PrimaryActionTextAlpha",
)
Box(modifier = Modifier.graphicsLayer { alpha = textAlpha.value }) {
Spacer(Modifier.size(5.dp))
Text("Clear")
}
}
}
},
undoAction = {
Chip(
modifier = Modifier.fillMaxWidth(),
onClick = {
/* Add the undo action */
coroutineScope.launch { state.animateTo(RevealValue.Covered) }
},
colors = ChipDefaults.secondaryChipColors(),
label = { Text(text = "Undo") },
)
},
) {
Chip(
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 */
true
}
)
},
onClick = { /* the click action associated with chip */ },
colors = ChipDefaults.secondaryChipColors(),
label = { Text(text = "Swipe Me") },
)
}
}
SwipeToRevealWithExpandables
/**
* A sample on how to use Swipe To Reveal within a list of items, preferably [ScalingLazyColumn].
*/
@Composable
fun SwipeToRevealWithExpandables() {
// Shape of actions should match with the overlay content. For example, Chips
// should use RoundedCornerShape(CornerSize(percent = 50)), Cards should use
// RoundedCornerShape with appropriate radius, based on the theme.
val actionShape = RoundedCornerShape(corner = CornerSize(percent = 50))
val itemCount = 10
val coroutineScope = rememberCoroutineScope()
val expandableStates = List(itemCount) { rememberExpandableState(initiallyExpanded = true) }
ScalingLazyColumn(modifier = Modifier.fillMaxSize()) {
item { ListHeader { Text("Scaling Lazy Column") } }
repeat(itemCount) { current ->
expandableItem(state = expandableStates[current]) { isExpanded ->
val revealState = rememberRevealState()
if (isExpanded) {
SwipeToReveal(
state = revealState,
primaryAction = {
Box(
modifier =
Modifier.fillMaxSize()
.background(Color.Red, actionShape)
.clickable {
coroutineScope.launch {
revealState.animateTo(RevealValue.RightRevealed)
}
},
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = Icons.Outlined.Delete,
contentDescription = "Delete",
)
}
},
secondaryAction = {
Box(
modifier =
Modifier.fillMaxSize()
.background(Color.Gray, actionShape)
.clickable { /* trigger the optional action */ },
contentAlignment = Alignment.Center,
) {
Icon(
imageVector = Icons.Outlined.MoreVert,
contentDescription = "More Options",
)
}
},
undoAction = {
Chip(
modifier = Modifier.fillMaxWidth(),
onClick = {
coroutineScope.launch {
revealState.animateTo(RevealValue.Covered)
}
},
colors = ChipDefaults.secondaryChipColors(),
label = { Text(text = "Undo") },
)
},
onFullSwipe = {
coroutineScope.launch {
delay(1000)
expandableStates[current].expanded = false
}
},
) {
Chip(
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 */
coroutineScope.launch {
revealState.animateTo(RevealValue.RightRevealed)
}
true
},
CustomAccessibilityAction("More Options") {
/* Add the secondary action click handler */
true
},
)
},
onClick = { /* the click action associated with chip */ },
colors = ChipDefaults.secondaryChipColors(),
label = { Text(text = "Swipe Me") },
)
}
}
}
}
}
}