12

I am trying to implement a list of items with the ability to delete items with swipe. I've basically used LazyColumn and SwipeToDismiss composables.

With default configuration vertical scrolling of LazyColumn and horizontal swiping of SwipeToDismiss mixes and I mostly trigger SwipeToDismiss while trying to do verticall scrolling.

To be able to change sensitivity of SwipeToDismiss I've looked how touch system works. As far as I understand children composables should ask to parents before consuming dragging events and parents can consume before children by utilizing nestedScroll. But there is a problem with SwipeToDismiss, it does not dispatch drag events to parents if the dragging angle on the x&y axis is between 0 & 45 degrees. So parents are not able to consume dragging events.

The problem is reproducable by changing the example on the nestedScroll documentation page. Just replace the items with SwipeToDismiss. You can also find the changed example source code below.

I am trying to understand if this is this a bug, or am I missing something and how can I make SwipeToDismiss and vertical scrolling work together better.

// here we use LazyColumn that has build-in nested scroll, but we want to act like a
// parent for this LazyColumn and participate in its nested scroll.
// Let's make a collapsing toolbar for LazyColumn
    val toolbarHeight = 48.dp
    val toolbarHeightPx = with(LocalDensity.current) { toolbarHeight.roundToPx().toFloat() }
// our offset to collapse toolbar
    val toolbarOffsetHeightPx =

        remember { mutableStateOf(0f) }
// now, let's create connection to the nested scroll system and listen to the scroll
// happening inside child LazyColumn
    val nestedScrollConnection = remember {
        object : NestedScrollConnection {
            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
                // try to consume before LazyColumn to collapse toolbar if needed, hence pre-scroll
                val delta = available.y
                val newOffset = toolbarOffsetHeightPx.value + delta
                toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
                // here's the catch: let's pretend we consumed 0 in any case, since we want
                // LazyColumn to scroll anyway for good UX
                // We're basically watching scroll without taking it
                return Offset.Zero
            }
        }
    }
    Box(
        Modifier
            .fillMaxSize()
            // attach as a parent to the nested scroll system
            .nestedScroll(nestedScrollConnection)
    ) {
        // our list with build in nested scroll support that will notify us about its scroll
        LazyColumn(contentPadding = PaddingValues(top = toolbarHeight)) {
            items(100) { index ->
                val state = remember { DismissState(DismissValue.Default) { true } }
                SwipeToDismiss(state = state, background = {
                    Text(text = "Background", modifier = Modifier.fillMaxWidth().background(color = androidx.compose.ui.graphics.Color.Red))
                }) {
                    Text("I'm item $index", modifier = Modifier
                        .fillMaxWidth()
                        .background(color = Color.Black)
                        .padding(16.dp))
                }

            }
        }
        TopAppBar(
            modifier = Modifier
                .height(toolbarHeight)
                .offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) },
            title = { Text("toolbar offset is ${toolbarOffsetHeightPx.value}") }
        )
    }
mehmet6parmak
  • 4,777
  • 16
  • 50
  • 71

0 Answers0