6

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?

jpm
  • 3,300
  • 1
  • 19
  • 29
  • 1
    how exactly have you tried? Something like `multiplier = 5f` with [my solution](https://stackoverflow.com/a/69495439/3585796) should definitely work – Phil Dukhov May 31 '22 at 08:44
  • 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 option – Phil Dukhov May 31 '22 at 08:46
  • 1
    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. – jpm May 31 '22 at 09:42
  • Ok, it seems like in the current version of the snapper library (https://chrisbanes.github.io/snapper/) there is actually a way to achieve this by implementing the snapIndex (see https://github.com/chrisbanes/snapper/blob/main/lib/src/main/kotlin/dev/chrisbanes/snapper/SnapperFlingBehavior.kt ) parameter in rememberSnapperFlingBehavior(). Too bad this is not yet supported in the HorizontalPager :/. – jpm May 31 '22 at 15:31
  • just copy `flingBehavior` [source code](https://github.com/google/accompanist/blob/80fbb7d6711fdfb4135b175ea03be35550a92d12/pager/src/main/java/com/google/accompanist/pager/Pager.kt#L117-L130) and provide different `snapIndex` – Phil Dukhov May 31 '22 at 15:38
  • 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? (https://github.com/google/accompanist/blob/2248e5efc788f7dc0c0be26239d6020bc888f127/pager/src/main/java/com/google/accompanist/pager/PagerState.kt#L77-L78 ) – jpm May 31 '22 at 15:59
  • 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 lambda – Phil Dukhov May 31 '22 at 16:35
  • 1
    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. – jpm May 31 '22 at 18:47
  • `flingBehavior` internally does not implement the function that takes `snapIndex`. Even copying the implementation and adding `snapIndex` won't help as @jpm mentioned it needs a `lazyListState` so there is a problem. – Astro Jun 22 '22 at 06:05

1 Answers1

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
        }
    )
}
jpm
  • 3,300
  • 1
  • 19
  • 29
  • I got webviews in my HorizontalPager. But it seems to be not working for me.. – Rafael Jul 26 '22 at 18:13
  • Please use `var currentPageIndex by rememberSaveable { mutableStateOf(pagerState.currentPage) }` or there will be strange sliding rebound. Because the state cannot be retained. – gaohomway Nov 14 '22 at 15:31
  • For me the only way it works is putting the current page index like this: val currentPageIndex = pagerState.currentPage – Jachumbelechao Unto Mantekilla Feb 13 '23 at 11:27
  • 1
    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. – Nino van Hooff Aug 09 '23 at 16:09