2

I want to create the reels feature with the lazy columns in kotlin where user can scroll the single item at a time not the multiple one's.

I tried to use the

   flingBehavior = rememberSnapFlingBehavior(
                snapLayoutInfoProvider = SnapLayoutInfoProvider(state)
            )

this code can snap the item but can't stop the user from scrolling multiple items at a time I tried to use the pager but the pager performance is not as much good as the lazy columns.

2 Answers 2

2

Actually the purpose of the Pager is to do this kind of things and it comes with more functionalities you can use and also efficient and easy to implement. But in case if you still want to do same with LazyColumn then we have to implement our own scrolling behaviour. And, you can done this by following these simple steps:

Step 1: Set userScrollEnabled to false so that user cannot do the default scrolling.

Step 2: Pass the state to the LazyColumn using rememberLazyListState by saving it in different variable as we will use it later.

Step 3: Now set the modifier for drag gestures as shown in the code and then you can try out the feature.

val lazyListState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()
var currentIndex = 0
var job: Job? = null

LazyColumn(
    state = lazyListState,
    userScrollEnabled = false, // to restrict the default scrolling behaviour
    modifier = Modifier
        .pointerInput(Unit) {
            detectDragGestures { change, dragAmount ->
                change.consume()
                val (x,y) = dragAmount
                
                // cancelling the previous job
                job?.cancel()
                job = coroutineScope.launch{
                    // Waiting for next gesture changes
                    delay(100)
                    
                    // Although we don't need right and left swipes
                    when {
                        x > 0 -> { /* swiped right */ }
                        x < 0 -> { /* swiped left */ }
                    }
                    when {
                        y > 0 -> { /* swiped down */
                            try { lazyListState.animateScrollToItem(--currentIndex) }
                            catch (e: Exception) { /* handle the error */}
                        }
                        y < 0 -> { /* swiped up */
                            try { lazyListState.animateScrollToItem(++currentIndex) }
                            catch (e: Exception) { /* handle the error */}
                        }
                    }
                }
            }
        }
) {
    // List content...
}

NOTE: In this logic the currentIndex can you beyond the limits but the app will not crashed so you can also handle these edge cases.

You might came up with the question why we have done job assignment and cancellation. So the reason is that the gestures changes multiple time rapidly and due to this it can do multiple scrolling so to counter this we have used Debounce Technique. You can check more about in one of my solution at: https://stackoverflow.com/a/76968173/14972910

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for the answer, but I need the smooth scroll behaviour like the ViewPager2. I don't want to block the user interaction.
@AnasMirza And that's what I was saying that Pager library comes with great functionalities and is also performance efficient so if you want exact same functionality like pager then you should implement the same as the LazyColumn will work with it's own efficiency.
Thanks, Just one more thing to confirm: Can the pager manage a large amount of data like the viewPager2 or lazyColumn?
@AnasMirza You can optimize synchronisation of data from server and also optimize UI by following some best practices like SideEffect, DisposalEffect, etc. and hence, the performance can be maximised.
0

Here is how I handled the issue, so maybe works for you case also;

First of all, define necessary variables;

val lazyStackState = rememberLazyListState()
val lazyStackScope = rememberCoroutineScope()

val positionInLayout = SnapPositionInLayout { layoutSize, itemSize, itemIndex ->
    // This value tells where to snap on the x axis within the viewport
    // Setting it to 0 results in snapping of the first visible item to the left side (or right side if RTL)
    0
}

val snappingLayout = remember(lazyStackState) { SnapLayoutInfoProvider(lazyStackState, positionInLayout) }
val snapBehavior = rememberSnapFlingBehavior(snappingLayout)

After that, create a LaunchedEffect to trigger your action in case users swipe up. The thing here is prevent more than one swipes at a time by using scrollToItem(1) logic. Also, if you are using a shimmer item at the end and load new data when you reach the shimmer item, you may need to use scrollToItem(0) at the end to make it work also when new data arrives.

LaunchedEffect(isSwipedUp) {
    if (isSwipedUp) {
        lazyStackScope.launch {
            if (listData.size > 1) {
                lazyStackState.scrollToItem(1)
            }
            // Your extra logic if you need
            if (listData.isEmpty()) {
                lazyStackState.scrollToItem(0)
            }
        }
    }
}

Finally, assign isSwipedUp.not() results in userScrollEnabled parameter for LazyColumn

LazyColumn(
    modifier = Modifier
        .heightIn(1500.dp)
        .background(White),
    userScrollEnabled = isSwipedUp.not(),
    state = lazyStackState,
    flingBehavior = snapBehavior,
    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
) {
    items(
        items = listData,
        key = { currentData -> currentData.id!! },
        itemContent = { data ->
            val isActiveItem = listData.firstOrNull()?.id == data.id
            // Use your content here
        }
   )
}

Hope it helps

2 Comments

where do you define the variable isSwipedUp and what type is it? where do you change it?
You can add it above LaunchedEffect, this is how I define it val isSwipedUp by remember { derivedStateOf { lazyStackState.firstVisibleItemIndex > 0 } }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.