Компонент «Закрытие с помощью свайпа» позволяет пользователям закрывать или обновлять элемент, проводя пальцем влево или вправо. Compose Material 3 предлагает простой в использовании компонент, который делает все за нас.
Зачем еще одно руководство по компоненту
Недавно мне пришлось реализовать swipe-to-dismiss функцию в одном из моих тестовых проектов. Я обнаружил, что все онлайн-ресурсы, включая официальную документацию Android, ссылаются на устаревшую версию компонента.
В версии 1.4.0-alpha16 Compose Material 3 произошло изменение, связанное с SwipeToDismissBox:
Исходя из этого, я решил опубликовать это руководство, чтобы помочь другим, кто хочет добавить функциональность закрытия с помощью свайпа в свои приложения.
Руководство по реализации
Мы собираемся добавить базовую реализацию swipe-to-dismiss в приложение, которое отображает список из 30 элементов. Это позволит с помощью свайпов влево и вправо либо пометить элемент как прочитанный, либо удалить его.
Шаг 0: предварительные условия
Убедитесь, что в ваш проект добавлена зависимость Compose Material 3.
Если вы используете Compose BOM и хотите получить стабильную версию Compose Material 3 в составе пакета:
// libs.versions.toml
[versions]
composeBom = "2025.12.01"
[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
// app/build.gradle.kts
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.material3)
Если вы предпочитаете последнюю альфа-версию библиотеки, укажите версию явно:
// libs.versions.toml
[versions]
composeBom = "2025.12.01"
composeMaterial3 = "1.5.0-alpha11"
[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "composeMaterial3" }
// app/build.gradle.kts
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.material3)
Шаг 1: создание состояния
Сначала создайте состояние закрытия, используя rememberSwipeToDismissBoxState. Это позволит нам контролировать поведение и определять, когда происходят свайпы.
Убедитесь, что используете версию без параметра confirmValueChange, так как она устарела!
val dismissState = rememberSwipeToDismissBoxState( positionalThreshold = SwipeToDismissBoxDefaults.positionalThreshold)
Свойство positionalTreshold для меня до сих пор остается загадкой. Судя по названию и описанию, можно предположить, что оно контролирует, насколько далеко пользователь должен перетащить/провести пальцем по элементу, прежде чем будет запущено действие. Однако я пробовал использовать значение по умолчанию SwipeToDismissBoxDefaults.positionalTreshold, которое устанавливает его равным 56.dp, а также it, it * 0.1f и аналогичные значения, и не заметил никакой разницы. Если у вас есть какие-либо идеи, напишите в комментариях.
Шаг 2: настройка SwipeToDismissBox
Оберните содержимое вашего списка компонентом SwipeToDismissBox. Это компонент, который обеспечивает жесты смахивания для наших композабл элементов. Он требует четыре основных параметра:
state: состояние закрытияonDismiss: колбек, срабатывающий после завершения смахивания, здесь вы можете проверить направление и выполнить свое действиеbackgroundContent: то, что отображается за элементом во время смахиванияcontent: композабл элемент содержимого, который будет доступен для смахивания
val coroutineScope = rememberCoroutineScope()
SwipeToDismissBox(
state = dismissState,
onDismiss = { dismissValue ->
when (dismissValue) {
SwipeToDismissBoxValue.StartToEnd -> {
coroutineScope.launch {
dismissState.reset()
onMarkAsRead()
}
}
SwipeToDismissBoxValue.EndToStart -> {
coroutineScope.launch {
dismissState.reset()
onDelete()
}
}
SwipeToDismissBoxValue.Settled -> {
// no action
}
}
},
backgroundContent = {
// Background revealed during swipe
BackgroundSwipeContent(dismissState)
},
content = {
// Your list item content
ListItemContent(item = item)
}
)
Мы используем coroutineScope, чтобы иметь возможность вызывать dismissState.reset(), который вернет элемент в исходное положение после срабатывания действия. Затем вызывается onMarkAsRead после того, как движение свайпа завершится. Вы можете опустить этот вызов, если не хотите анимировать возвращение элемента в исходное положение.
Шаг 3: создание фонового контента
Фон — это то, что видят пользователи при свайпе. Мы можем изменять цвет и значок в зависимости от направления свайпа или прогресса, которые мы получаем из dismissState.
@Composable
private fun BackgroundSwipeContent(swipeDirection: SwipeToDismissBoxValue) {
val color by animateColorAsState(
when (swipeDirection) {
SwipeToDismissBoxValue.StartToEnd -> Color(0xFF4CAF50)
SwipeToDismissBoxValue.EndToStart -> Color(0xFFF44336)
SwipeToDismissBoxValue.Settled -> Color.LightGray
},
label = "background color"
)
val alignment = when (swipeDirection) {
SwipeToDismissBoxValue.StartToEnd -> Alignment.CenterStart
SwipeToDismissBoxValue.EndToStart -> Alignment.CenterEnd
SwipeToDismissBoxValue.Settled -> Alignment.Center
}
val iconResId = when (swipeDirection) {
SwipeToDismissBoxValue.StartToEnd -> R.drawable.ic_mark_read
SwipeToDismissBoxValue.EndToStart -> R.drawable.ic_delete
SwipeToDismissBoxValue.Settled -> R.drawable.ic_mark_read
}
Box(
modifier = Modifier
.fillMaxSize()
.background(color)
.padding(horizontal = 20.dp),
contentAlignment = alignment
) {
Icon(
painter = painterResource(iconResId),
contentDescription = null,
tint = Color.White
)
}
}
Шаг 4: использование в LazyColumn
При использовании с LazyColumn убедитесь, что указан стабильный ключ для корректного отслеживания элементов:
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(items = items, key = { it.id }) { item ->
SwipeableListItem(
item = item,
onMarkAsRead = {
// do something
},
onDelete = {
// do something
}
)
}
}
Шаг 5: конечный результат
После того, как мы обернули элемент в SwipeToDismissBox, мы получили следующее поведение, которое в точности соответствует нашим ожиданиям.
Полный пример можно найти на GitHub.
Заключение
Функция SwipeToDismissBox в Compose Material 3 предоставляет простой способ добавления жестов смахивания к элементам списка. Важные моменты, которые следует учитывать:
- Используйте правильное значение
rememberSwipeToDismissBoxState: убедитесь, что используете версию без параметраconfirmValueChange, поскольку она устарела - Сброс после действия: вызовите
dismissState.reset(), если хотите, чтобы элемент вернулся в исходное состояние после выполнения действия. Пропустите сброс, если элемент будет удален. - Направления смахивания:
StartToEnd— смахивание слева направо,EndToStart— справа налево.
На этом всё. Поделитесь в комментариях своим опытом использования swipe-to-dismiss в Compose Material 3.

