How to do One swipe at a time or Disable swipe on multiple items as in Gmail in Jetpack Compose?

73 Views Asked by At

[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 Cards 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,
                )
            }
        }
    )
}
0

There are 0 best solutions below