Shakil Karim

    Shakil Karim

    1 year ago
    How can I achieve snapping like behavior in LazyRow? is there any built-in method to do that?
    Vadim

    Vadim

    1 year ago
    I also need ViewPager-like snapping behavior in my LazyRows but getting there very slowly, so I can only put some thougths here, maybe this would be helpful to someone. I believe
    FlingConfig
    is meant for snapping in Compose, and there's flingConfig in
    LazyListState
    that's used by LazyRow. So we can slightly modify
    rememberLazyListState
    to use custom filngConfig:
    @Composable
    fun rememberFlingLazyListState(
        initialFirstVisibleItemIndex: Int = 0,
        initialFirstVisibleItemScrollOffset: Int = 0,
        interactionState: InteractionState? = null,
        config: FlingConfig = defaultFlingConfig()
    ): LazyListState {
        val clock = AmbientAnimationClock.current.asDisposableClock()
    
        // Avoid creating a new instance every invocation
        val saver = remember(config, clock, interactionState) {
            LazyListState.Saver(config, clock, interactionState)
        }
    
        return rememberSavedInstanceState(config, clock, interactionState, saver = saver) {
            LazyListState(
                initialFirstVisibleItemIndex,
                initialFirstVisibleItemScrollOffset,
                interactionState,
                config,
                clock
            )
        }
    }
    so it can be used in LazyRow like this:
    val badgeListScroll = rememberFlingLazyListState(
        config = FlingConfig(
            anchors = pageOffsetList, // list of X-coordinates which will be snapped at while scrolling
            animationSpec = TweenSpec(),
            decayAnimation = FloatExponentialDecaySpec(frictionMultiplier = 1f)
        )
    )
    
    LazyRow(
        modifier = Modifier.fillMaxWidth(),
        state = badgeListScroll
    ) {
    	itemsIndexed(badgeList) { index, item -> MyBadge(item) }
    }
    The problem here is to choose correct value for
    decayAnimation
    .
    FloatExponentialDecaySpec
    doesn't allow to set the end coordinate of animation so, after user swipes, scrolling just stops at a 'random' place (depending only on velocity of swipe gesture). Couple of alphas ago, there wasn't any
    decay
    in
    FlingConfig
    and snapping worked almost fine with similar config...
    Doris Liu

    Doris Liu

    1 year ago
    The anchors in the
    FlingConfig
    in your snippet should give you the snapping behavior. If not, please file a bug. 🙂
    decayAnimation
    describes what the natural slow-down should look like. It is also used to calculate where the natural target point is, in order to choose a snapping target from the given list of
    anchors
    . If/when you have
    anchors
    defined, the
    animationSpec
    is used for the snapping animation. I would suggest having a spring animation for the snapping (instead of tween) to maintain continuity in velocity. If you need a "snappier" animation, you could consider increasing the spring stiffness.
    Vadim

    Vadim

    1 year ago
    @Doris Liu thank you for your clarifications. Maybe what I want is completely different behaviour and is not snapping: I want LazyRow, after user gesture, to stop (accelerating itself if needed) exactly at one of the anchor points. So it would behave like horizontal app icon lists in Play Store app. With this flingConfig, if I just touch, pan and then release LazyRow, (so start velocity is 0), it just stops - there's no scrolling to the nearest anchor.
    Doris Liu

    Doris Liu

    1 year ago
    So with the
    LazyRow
    you never want it to come to a stop in-between two items after a gesture (be it panning or flinging). That makes sense. Thanks for clarifying.👍 Looks like that work is already tracked here: https://issuetracker.google.com/issues/166590434 Please feel free to cc yourself to get updates from the work, and/or add additional clarification/request in the tracker. 🙂