I was gonna make a bug ticket against the Compose ...
# compose-android
l
I was gonna make a bug ticket against the Compose pager but I figured I should post here first to get some opinions. 👋 My goal: Get the target page index as soon as the user cancels their drag. 🎯 Problem: Not really a good way to do it. The
targetPage
should be the way to do it, but it takes a non-deterministic amount of time to update after being dropped. 😬 Additionally, it updates before the user stops dragging. I only want the updates when the user let’s go of the page. Code and demo in the thread. 🧵
Copy code
@Composable
    private fun PagerTestScreen(
        modifier: Modifier = Modifier,
    ) {
        val pages = remember { List(10) { it } }
        val pagerState = rememberPagerState { pages.size }
        var dragCancellationPage by remember { mutableIntStateOf(pagerState.currentPage) }
        var dragCancellationPageWithDelay by remember { mutableIntStateOf(pagerState.currentPage) }

        LaunchedEffect(pagerState) {
            pagerState
                .interactionSource
                .collectLatestDragCancellation {
                    dragCancellationPage = pagerState.targetPage
                }
        }

        LaunchedEffect(pagerState) {
            pagerState
                .interactionSource
                .collectLatestDragCancellation {
                    delay(50)
                    dragCancellationPageWithDelay = pagerState.targetPage
                }
        }

        val textModifier = Modifier
            .padding(16.dp)
            .background(Color.Gray)
            .padding(16.dp)

        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center,
            modifier = modifier,
        ) {
            Text(
                text = "Target Page: ${pagerState.targetPage}",
                modifier = textModifier,
            )

            Text(
                text = "Dragged cancellation page: $dragCancellationPage",
                modifier = textModifier,
            )

            Text(
                text = "Dragged cancellation page with delay: $dragCancellationPageWithDelay",
                modifier = textModifier,
            )

            HorizontalPager(
                contentPadding = PaddingValues(24.dp),
                pageSpacing = 16.dp,
                pageSize = PageSize.Fill,
                state = pagerState,
            ) { index ->
                Page(
                    pageNumber = pages[index],
                    modifier = Modifier.fillMaxWidth(),
                )
            }
        }
    }

    @Composable
    private fun Page(
        pageNumber: Int,
        modifier: Modifier = Modifier,
    ) {
        Box(
            contentAlignment = Alignment.Center,
            modifier = modifier
                .background(
                    color = Color.White,
                    shape = RoundedCornerShape(8.dp),
                )
                .height(64.dp),
        ) {
            Text(
                text = pageNumber.toString(),
            )
        }
    }
}

/**
 * If there is a drag action on this [InteractionSource], when it stops or is canceled, this function will invoke
 * [block]. When a new drag action comes in, the flow returned by [block] will be cancelled.
 *
 * This is useful for performing work when a draggable component is done being dragged.
 */
private suspend fun InteractionSource.collectLatestDragCancellation(
    block: suspend () -> Unit,
) {
    this
        .interactions
        .collect {
            when (it) {
                is DragInteraction.Stop,
                is DragInteraction.Cancel,
                -> block()
            }
        }
}
pagerDemonstration.mp4
Notice the “Dragged cancellation page with delay” works correctly but it is a kludge to add a semi-arbitrary delay.