Gestures in Jetpack Compose: Getting Started

Learn how to implement different gestures in Jetpack Compose and provide your app an intuitive user experience. By Max Buster.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 2 of this article. Click here to view the first page.

Swipe to Dismiss

You still need a way to remove to-do items without adding additional buttons and keeping your UI tidy and beautiful!

A frequently used gesture for this use case is “swipe to dismiss.” It works by dragging an element either to the left or right and once the item passes a certain threshold, it slides off the screen and triggers an action.

This is such a common use that it’s now part of the androidx.compose.material library as its own composable. The first step is to create a state holder within the list item’s composable. You can add the following code at the TODO: Add swipe to dismiss state in TodoListComposable.kt.

val dismissState = rememberDismissState(confirmStateChange = {
  if (it == DismissValue.DismissedToEnd) {
    todoListViewModel.removeTodo(item)
  }
  true
})

This creates the action associated with the SwipeToDismiss component. It will trigger when the element is swiped, calling the view model method to remove the row item.

Next, add the SwipeToDismiss component. In TodoListComposable.kt, replace TODO: Wrap with swipe to dismiss and the TodoListRowContent function call with:

SwipeToDismiss(
  state = dismissState,
  dismissThresholds = { FractionalThreshold(0.5f) },
  directions = setOf(DismissDirection.StartToEnd),
  // TODO: Add top layer UI
  // TODO: Add bottom layer UI
)
  • The state argument passes the SwipeToDismiss state holder, which triggers state change actions.
  • The threshold prevents triggering the state until the element has been dragged by a certain proportion of the screen. In this case, the row must be over 50% of the screen before it is dismissed.
  • Finally, the directions tells the component to only allow drag from left to right. If the user tries to drag the other way, it will nudge in that direction before returning to its regular position. It is useful because you might want context-specific actions such as archiving if a user drags to the left and deleting if a user drags to the right. If you add additional directions here, you must also update the state holder to handle those state changes.

Now you can add the UI portion of the composable. Add the following snippet as an argument to SwipeToDismiss where the TODO: Add top layer UI is.

dismissContent = {
  TodoListRowContent(item, todoListViewModel, navController)
},

The UI for SwipeToDismiss is composed of two layers: the top layer row content and the background content that is exposed when the top layer is swiped away. The dismissContent is the top level content while the background is the layer below it, which is visible on swipe.

In this case, you can add a trash icon for the background to indicate that the dismiss action will remove the element from the list. Add the following beneath the dismissContent argument.

background = {
  Icon(
    painterResource(id = R.drawable.ic_baseline_delete_outline_24),
    modifier = Modifier
      .size(30.dp)
      .align(Alignment.CenterVertically),
    contentDescription = null,
    tint = Color.Red
  )
}

This adds a trash icon behind the original row content so when the user swipes the row, the intent of the action will be clear.

You can run the app now and see your new swipe-to-dismiss gesture. However, you might notice one final gotcha.

When you swipe to delete an item, it doesn’t swipe off screen completely. That’s because the composable items are being recycled in the LazyColumn, but the underlying data set changes aren’t able to convey the recomposition. To tell the LazyColumn the underlying data should recompose the element, update the LazyColumn item creation with:

items(items, key = { it.id }) {
 ...
}

The key associated with data ID tells the LazyColumn that each data element should correspond to its own composable and should refresh the composable when the data changes. Build and run the app. You should see the swipe-to-dismiss working like a charm!

Gif demonstrating swipe to dismiss

Where to Go From Here?

You can download the final project by using the Download Materials button at the top or bottom of this tutorial.

The gestures covered in this tutorial should get you through most scenarios, but if you need to implement others, check out the Official Documentation.

You also can continue learning about Jetpack Compose from the Jetpack Compose by Tutorials book.

Continue your Jetpack Compose journey. Much remains to explore. :]

If you have any questions or comments, please join the forum discussion below!