Swipe sensitivity for HorizontalPager in Compose
M

2

8

I implemented a simple HorizontalPager which works mostly as expected. The app I develop is for one specific device, a 8" Tablet in landscape mode. At the moment it is required to swipe more than 50% of the screen-width to get to the next/prev page. This is a very long swipe and I would like to reduce that to make changing pages easier...

I played around with fling behavior and tried to manually change pages when the offset changed or to intercept the touchevents...Nothing really lead to the desired behavior.

Since the "problem" seems so simple, I really hope that I have just overseen something. Do you have an idea what I could try?

Marijn answered 31/5, 2022 at 8:32 Comment(9)
how exactly have you tried? Something like multiplier = 5f with my solution should definitely workCavetto
Alternatively you can copy PagerDefaults.flingBehavior source code and redefine it to your needs. There's no simple parameter like "screen scrolled percent", flingBehavior is the only optionCavetto
I don't really think this is exactly what I expect. The fling is faster with the multipler = 5f, but what I want to change is, that the fling behavior is executed with less of a swipe. I think I don't want to change the fling behevior but the detection of a fling... If that makes sense.Marijn
Ok, it seems like in the current version of the snapper library (chrisbanes.github.io/snapper) there is actually a way to achieve this by implementing the snapIndex (see github.com/chrisbanes/snapper/blob/main/lib/src/main/kotlin/dev/… ) parameter in rememberSnapperFlingBehavior(). Too bad this is not yet supported in the HorizontalPager :/.Marijn
just copy flingBehavior source code and provide different snapIndexCavetto
That was my plan, but lazyListState is internal and I don't see a way to build a flingBehavior with custom snapIndex, without the lazyListState... Or am I missing something? (github.com/google/accompanist/blob/… )Marijn
I don't see where you need lazyListState? for flingBehavior you only need PagerState, and for custom snapIndex it seems like you don't need anything at all - it's just a lambdaCavetto
Ahh my bad! I had an older version where this method didn't exist and I was a little confused...Thank you! That looks like it's exactly what I was looking for.Marijn
flingBehavior internally does not implement the function that takes snapIndex. Even copying the implementation and adding snapIndex won't help as @Marijn mentioned it needs a lazyListState so there is a problem.Upmost
M
5

This solved my problem, you can edit minFlingDistanceDp to change the sensitivity:

HorizontalPager(
            modifier = Modifier.fillMaxSize(),
            state = pagerState,
            count = noOfPages,
            flingBehavior = flingBehavior(pagerState = pagerState, noOfPages = noOfPages)
        ) { page ->
            //Content
        }


val minFlingDistanceDp = 150.dp

@OptIn(ExperimentalPagerApi::class, dev.chrisbanes.snapper.ExperimentalSnapperApi::class)
@Composable
fun flingBehavior(pagerState: PagerState, noOfPages: Int): FlingBehavior {
    var currentPageIndex = remember { pagerState.currentPage }
    return PagerDefaults.flingBehavior(
        state = pagerState,
        snapIndex = { layoutInfo, _, _ ->
            val distanceToStartSnap = layoutInfo.distanceToIndexSnap(currentPageIndex)
            currentPageIndex = when {
                distanceToStartSnap < -(minFlingDistanceDp.value) -> {
                    (currentPageIndex + 1).coerceAtMost(noOfPages - 1)
                }
                distanceToStartSnap > minFlingDistanceDp.value -> {
                    (currentPageIndex - 1).coerceAtLeast(0)
                }
                else -> {
                    currentPageIndex
                }
            }
            currentPageIndex
        }
    )
}
Marijn answered 6/7, 2022 at 11:5 Comment(5)
I got webviews in my HorizontalPager. But it seems to be not working for me..Erechtheum
Please use var currentPageIndex by rememberSaveable { mutableStateOf(pagerState.currentPage) } or there will be strange sliding rebound. Because the state cannot be retained.Lists
For me the only way it works is putting the current page index like this: val currentPageIndex = pagerState.currentPageDobsonfly
Note that this solution (like the question) is for the Accompanist Pager, which is now deprecated. Unfortunately the compose foundation PagerDefaults.flingBehavior does not provide a snapIndex parameter.Gabon
@NinovanHooff rememberSnapperFlingBehavior() has the snapIndex parameter. Maybe give it a try?Klopstock
K
1

HorizontalPager in foundation library version 1.5.1 now has a parameter in flingBehavior called snapVelocityThreshold to control the swipe velocity.

You can create your own flingBehavior with more sensitivity like this

val pagerState = androidx.compose.foundation.pager.rememberPagerState(
    initialPage = 0
) {
    imageList.size // A list of image urls to load in the carousel
}

androidx.compose.foundation.pager.HorizontalPager(
    state = pagerState,
    pageSpacing = itemSpacing,
    flingBehavior = PagerDefaults.flingBehavior(
        state = pagerState,
        snapVelocityThreshold = 5.dp
    ),
) { pageScope@ { page ->
    // Your pages go here
}

Klopstock answered 15/9, 2023 at 7:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.