[EDIT] I added my files for the LazyColumn and the Cards
I'm building an Android app with Kotlin, Jetpack Compose and Material2 and I currently have a LazyColumn
of Card
s implementing the SwipeToDismiss
composable .
The problem I encounter is when I start to swipe a card, nothing prevents me (and the user) to swipe other cards at the same time.
I saw Gmail's app handles this nicely to only allow one swipe at a time and I'd like to copy this behaviour.
MyLazyList.kt
@Composable
@ExperimentalMaterialApi
LazyColumn(
modifier = Modifier.fillMaxWidth(),
state = scrollState // is LazyListState
) {
items(
items = myList, key = { item -> item.ID },
itemContent = { item ->
ItemCard(
item = item,
openDialog = { id, titleAction, confirmCallback, dismissCallBack ->
// do something
},
swipedToRight = { id: Int ->
// handle right swipe
},
swipedToLeft = { id: Int ->
// handle left swipe
},
navigateToScreen = navigateToScreen
)
}
)
}
ItemCard.kt
@Composable
@ExperimentalMaterialApi
fun ItemCard(
item: item,
openDialog: (id: Int, titleAction: String, confirmCallback: (id: Int) -> Unit, dismissCallBack: (id: Int) -> Unit) -> Unit,
swipedToRight: (id: Int) -> Unit,
swipeToLeft: (id: Int) -> Unit,
navigateToScreen: (itemId: Int) -> Unit
) {
val linear = Brush.linearGradient(listOf(Color.Blue, Color.Green))
val gradientIcon =
Brush.linearGradient(listOf(BlueIcon, Color.Blue), Offset(0F, 0F), Offset(10F, 10F))
val mContext = LocalContext.current
fun infoToast() {
Toast.makeText(mContext, "Double-click for détails", Toast.LENGTH_SHORT).show()
}
var isDialogClosed by remember { mutableStateOf(true) }
val dismissState = rememberDismissState(
confirmStateChange = {
if (it == DismissValue.DismissedToEnd) {
isDialogClosed = false
openDialog(
item.id,
"Confirm swipe to right ?",
{
swipedToRight(item.id)
ApiHelper.setHandleditem(item.id)
isDialogClosed = true
Toast.makeText(mContext, "Done", Toast.LENGTH_SHORT).show()
},
{
isDialogClosed = true
}
)
} else if (it == DismissValue.DismissedToStart) {
swipeToLeft(item.id)
}
true
}
)
if (isDialogClosed && (dismissState.isDismissed(DismissDirection.StartToEnd) || dismissState.isDismissed(
DismissDirection.EndToStart
))
) {
LaunchedEffect(Unit) {
dismissState.reset()
}
}
SwipeToDismiss(
state = dismissState,
directions = setOf(DismissDirection.EndToStart, DismissDirection.StartToEnd),
dismissThresholds = { direction ->
FractionalThreshold(
when (direction) {
DismissDirection.EndToStart -> 0.5f
DismissDirection.StartToEnd -> 0.5f
else -> 0.05f
}
)
},
background = {
val direction = dismissState.targetValue
val color by animateColorAsState(
targetValue =
when (dismissState.targetValue) {
DismissValue.DismissedToStart -> Red500
DismissValue.DismissedToEnd -> Green700
else -> MaterialTheme.colors.background
}, label = ""
)
val alignment = when (direction) {
DismissValue.DismissedToStart -> Alignment.CenterEnd
DismissValue.DismissedToEnd -> Alignment.CenterStart
else -> Alignment.Center
}
val icon = when (direction) {
DismissValue.DismissedToStart -> Icons.Default.Delete
else -> Icons.Default.Done
}
val scale by animateFloatAsState(
targetValue = if (dismissState.targetValue == DismissValue.Default) 0.75f else 1f,
label = ""
)
Box(
Modifier
.fillMaxSize()
.background(color)
.padding(horizontal = 20.dp),
contentAlignment = alignment
) {
Icon(
icon,
contentDescription = "Swipe Icon ${icon.name}",
modifier = Modifier.scale(scale)
)
}
},
dismissContent = {
Card(
modifier = Modifier
.padding(
start = 8.dp,
end = 8.dp,
top = 4.dp,
bottom = 4.dp
)
.fillMaxWidth()
.clickable {}
.pointerInput(Unit) {
detectTapGestures(
onPress = {},
onDoubleTap = {
navigateToScreen(item.id)
},
onLongPress = { infoToast() },
onTap = { infoToast() }
)
},
border = BorderStroke(2.dp, linear),
elevation = 10.dp,
shape = Shapes.large,
) {
Text(
text = item.ID.toString(),
fontSize = 25.sp,
)
}
}
)
}