There is a list in my app, on which, a user can swipe to delete items. If the list is empty, then the user is shown an EmptyListView
. I have been trying utilising the animateItemPlacement()
modifier on LazyList
so that the swipe to delete animation feels quite fluid. However, since adding the Crossfade
to enable a smooth fade between Loading/Ready/Empty states, the screen now crossfades whenever the list is updated – stopping the LazyList animation.
It would be better for the list to animate the item placement of items, and only have the crossfade between the LOADING, READY and EMPTY states. Is there a way to achieve this?
This is essentially the setup:
sealed class ListUiState {
data object Loading: ListUiState()
data class Ready(val items: List<Item>): ListUiState()
data object Empty: ListUiState()
}
@Composable
fun ListScreen(
uiState: ListUiState,
onDeleteItem: (itemId: Long) -> Unit,
) {
Crossfade(
targetState = uiState,
) { animatedUiState ->
when(animatedUiState) {
is ListUiState.Loading -> CircularProgressIndicator()
is ListUiState.Ready -> ItemList(
listItems = animatedUiState.listItems,
onDeleteItem = onDeleteItem,
)
is ListUiState.Empty -> EmptyListView()
}
}
}
@Composable
private fun ItemList(
items: List<Item>,
onVehicleDelete: (itemId: Long) -> Unit,
) {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(16.dp),
) {
items(
items,
key = { it.id },
) { vehicleSummary ->
SwipableItem(
item = item,
onItemDelete = onItemDelete,
modifier = Modifier.animateItemPlacement()
)
}
}
}
@Composable
private fun SwipableItemCard(
item: Item,
onItemDelete: (itemId: Long) -> Unit,
modifier: Modifier = Modifier,
) {
val swipeState = rememberSwipeToDismissBoxState()
SwipeToDismissBox(
modifier = modifier.animateContentSize(),
state = swipeState,
backgroundContent = {
...
},
content = {
...
},
enableDismissFromStartToEnd = false,
)
when(swipeState.currentValue) {
SwipeToDismissBoxValue.StartToEnd -> {/* no-op */ }
SwipeToDismissBoxValue.EndToStart -> { onItemDelete(item.id) }
SwipeToDismissBoxValue.Settled -> { /* no-op */ }
}
}