O componente SwipeToDismissBox
permite que o usuário dispense ou atualize um
item deslizando-o para a esquerda ou direita.
Superfície da API
Use o elemento combinável SwipeToDismissBox
para implementar ações acionadas
por gestos de deslizar. Os principais parâmetros incluem:
state
: o estadoSwipeToDismissBoxState
criado para armazenar o valor produzido por cálculos no item de deslizar, que aciona eventos quando produzido.backgroundContent
: um elemento combinável personalizável exibido atrás do conteúdo do item, que é revelado quando o conteúdo é deslizado.
Exemplo básico: atualizar ou dispensar ao deslizar
Os snippets neste exemplo mostram uma implementação de deslize que atualiza o item quando deslizado do início para o fim ou dispensa o item quando deslizado do fim para o início.
data class TodoItem( val itemDescription: String, var isItemDone: Boolean = false )
@Composable fun TodoListItem( todoItem: TodoItem, onToggleDone: (TodoItem) -> Unit, onRemove: (TodoItem) -> Unit, modifier: Modifier = Modifier, ) { val swipeToDismissBoxState = rememberSwipeToDismissBoxState( confirmValueChange = { if (it == StartToEnd) onToggleDone(todoItem) else if (it == EndToStart) onRemove(todoItem) // Reset item when toggling done status it != StartToEnd } ) SwipeToDismissBox( state = swipeToDismissBoxState, modifier = modifier.fillMaxSize(), backgroundContent = { when (swipeToDismissBoxState.dismissDirection) { StartToEnd -> { Icon( if (todoItem.isItemDone) Icons.Default.CheckBox else Icons.Default.CheckBoxOutlineBlank, contentDescription = if (todoItem.isItemDone) "Done" else "Not done", modifier = Modifier .fillMaxSize() .background(Color.Blue) .wrapContentSize(Alignment.CenterStart) .padding(12.dp), tint = Color.White ) } EndToStart -> { Icon( imageVector = Icons.Default.Delete, contentDescription = "Remove item", modifier = Modifier .fillMaxSize() .background(Color.Red) .wrapContentSize(Alignment.CenterEnd) .padding(12.dp), tint = Color.White ) } Settled -> {} } } ) { ListItem( headlineContent = { Text(todoItem.itemDescription) }, supportingContent = { Text("swipe me to update or remove.") } ) } }
Pontos principais sobre o código
swipeToDismissBoxState
gerencia o estado do componente. Ele aciona o callbackconfirmValueChange
quando a interação com o item é concluída. O corpo do callback processa as diferentes ações possíveis. O callback retorna um booleano que informa ao componente se ele precisa mostrar uma animação de encerramento. Nesse caso:- Se o item for deslizado do início ao fim, ele vai chamar o lambda
onToggleDone
, transmitindo otodoItem
atual. Isso corresponde à atualização do item de tarefas. - Se o item for deslizado do fim para o início, ele vai chamar o lambda
onRemove
, transmitindo otodoItem
atual. Isso corresponde à exclusão do item de tarefas. it != StartToEnd
: essa linha retornatrue
se a direção do deslizar não forStartToEnd
efalse
caso contrário. O retorno defalse
impede que oSwipeToDismissBox
desapareça imediatamente após um deslize de "alternar concluído", permitindo uma confirmação visual ou animação.
- Se o item for deslizado do início ao fim, ele vai chamar o lambda
SwipeToDismissBox
ativa interações de deslizar horizontal em cada item. Em repouso, ele mostra o conteúdo interno do componente, mas quando um usuário começa a deslizar, o conteúdo é removido e obackgroundContent
aparece. Tanto o conteúdo normal quanto obackgroundContent
recebem as restrições completas do contêiner pai para renderização. Ocontent
é desenhado sobre obackgroundContent
. Nesse caso:backgroundContent
é implementado como umaIcon
com uma cor de plano de fundo baseada emSwipeToDismissBoxValue
:Blue
ao deslizarStartToEnd
: alternar um item de tarefas.Red
ao deslizarEndToStart
: excluir uma tarefa.- Nada é exibido no segundo plano para
Settled
. Quando o item não está sendo deslizado, nada é mostrado no segundo plano. - Da mesma forma, o
Icon
exibido se adapta à direção do movimento: StartToEnd
mostra um íconeCheckBox
quando a tarefa é concluída e um íconeCheckBoxOutlineBlank
quando ela não é concluída.EndToStart
exibe um íconeDelete
.
@Composable private fun SwipeItemExample() { val todoItems = remember { mutableStateListOf( TodoItem("Pay bills"), TodoItem("Buy groceries"), TodoItem("Go to gym"), TodoItem("Get dinner") ) } LazyColumn { items( items = todoItems, key = { it.itemDescription } ) { todoItem -> TodoListItem( todoItem = todoItem, onToggleDone = { todoItem -> todoItem.isItemDone = !todoItem.isItemDone }, onRemove = { todoItem -> todoItems -= todoItem }, modifier = Modifier.animateItem() ) } } }
Pontos principais sobre o código
mutableStateListOf(...)
cria uma lista observável que pode conter objetosTodoItem
. Quando um item é adicionado ou removido dessa lista, o Compose recompone as partes da IU que dependem dele.- Dentro de
mutableStateListOf()
, quatro objetosTodoItem
são inicializados com as respectivas descrições: "Pay bills", "Buy groceries", "Go to gym" e "Get dinner".
- Dentro de
LazyColumn
mostra uma lista detodoItems
com rolagem vertical.onToggleDone = { todoItem -> ... }
é uma função de callback invocada emTodoListItem
quando o usuário marca um objeto como concluído. Ele atualiza a propriedadeisItemDone
dotodoItem
. ComotodoItems
é ummutableStateListOf
, essa mudança aciona uma recomposição, atualizando a interface.onRemove = { todoItem -> ... }
é uma função de callback acionada quando o usuário remove o item. Ele remove otodoItem
específico da listatodoItems
. Isso também causa uma recomposição, e o item será removido da lista exibida.- Um modificador
animateItem
é aplicado a cadaTodoListItem
para que oplacementSpec
do modificador seja chamado quando o item for dispensado. Isso anima a remoção do item, bem como a reordenação de outros itens na lista.
Resultado
O vídeo a seguir demonstra a funcionalidade básica de deslizar para descartar dos snipets anteriores:
Consulte o arquivo de origem do GitHub para conferir o código de exemplo completo.
Exemplo avançado: animar a cor do plano de fundo ao deslizar
Os snippets a seguir mostram como incorporar um limite posicional para animar a cor de fundo de um item ao deslizar.
data class TodoItem( val itemDescription: String, var isItemDone: Boolean = false )
@Composable fun TodoListItemWithAnimation( todoItem: TodoItem, onToggleDone: (TodoItem) -> Unit, onRemove: (TodoItem) -> Unit, modifier: Modifier = Modifier, ) { val swipeToDismissBoxState = rememberSwipeToDismissBoxState( confirmValueChange = { if (it == StartToEnd) onToggleDone(todoItem) else if (it == EndToStart) onRemove(todoItem) // Reset item when toggling done status it != StartToEnd } ) SwipeToDismissBox( state = swipeToDismissBoxState, modifier = modifier.fillMaxSize(), backgroundContent = { when (swipeToDismissBoxState.dismissDirection) { StartToEnd -> { Icon( if (todoItem.isItemDone) Icons.Default.CheckBox else Icons.Default.CheckBoxOutlineBlank, contentDescription = if (todoItem.isItemDone) "Done" else "Not done", modifier = Modifier .fillMaxSize() .drawBehind { drawRect(lerp(Color.LightGray, Color.Blue, swipeToDismissBoxState.progress)) } .wrapContentSize(Alignment.CenterStart) .padding(12.dp), tint = Color.White ) } EndToStart -> { Icon( imageVector = Icons.Default.Delete, contentDescription = "Remove item", modifier = Modifier .fillMaxSize() .background(lerp(Color.LightGray, Color.Red, swipeToDismissBoxState.progress)) .wrapContentSize(Alignment.CenterEnd) .padding(12.dp), tint = Color.White ) } Settled -> {} } } ) { OutlinedCard(shape = RectangleShape) { ListItem( headlineContent = { Text(todoItem.itemDescription) }, supportingContent = { Text("swipe me to update or remove.") } ) } } }
Pontos principais sobre o código
- O
drawBehind
é exibido diretamente na tela por trás do conteúdo do elemento combinávelIcon
.drawRect()
desenha um retângulo na tela e preenche todos os limites do escopo de exibição com oColor
especificado.
- Ao deslizar, a cor de plano de fundo do item muda suavemente usando
lerp
.- Em um deslize de
StartToEnd
, a cor de fundo muda gradualmente de cinza claro para azul. - Para um deslize de
EndToStart
, a cor de fundo muda gradualmente de cinza claro para vermelho. - A quantidade de transição de uma cor para a próxima é determinada por
swipeToDismissBoxState.progress
.
- Em um deslize de
- O
OutlinedCard
adiciona uma separação visual sutil entre os itens da lista.
@Composable private fun SwipeItemWithAnimationExample() { val todoItems = remember { mutableStateListOf( TodoItem("Pay bills"), TodoItem("Buy groceries"), TodoItem("Go to gym"), TodoItem("Get dinner") ) } LazyColumn { items( items = todoItems, key = { it.itemDescription } ) { todoItem -> TodoListItemWithAnimation( todoItem = todoItem, onToggleDone = { todoItem -> todoItem.isItemDone = !todoItem.isItemDone }, onRemove = { todoItem -> todoItems -= todoItem }, modifier = Modifier.animateItem() ) } } }
Pontos principais sobre o código
- Para conferir os principais pontos sobre esse código, consulte Principais pontos em uma seção anterior, que descreve um snippet de código idêntico.
Resultado
O vídeo a seguir mostra a funcionalidade avançada com a cor de plano de fundo animada:
Consulte o arquivo de origem do GitHub para conferir o código de exemplo completo.