Tony Kazanjian

    Tony Kazanjian

    1 year ago
    I am trying to add an auto-scroll effect to the example of a Pager seen in the Jetcaster sample (https://github.com/android/compose-samples/blob/main/Jetcaster/app/src/main/java/com/example/jetcaster/util/Pager.kt), but it seems like adding a
    ScrollableState
    to a modifier that also includes a
    DraggableState
    has no effect (also, seems like whichever modifier comes next pretty much overwrites the previous one). Code in thread
    Let's say I declare my scrollableState and want the whole composable to scroll by it's screen length as a launch effect:
    val scrollable = rememberScrollableState {dy ->
            Log.d(TAG, "Scrolled to : $dy")
            dy
        }
    
        LaunchedEffect(state) {
            delay(2000)
            scrollable.animateScrollBy(1080f)
        }
    Then, I'm adding the scrollable to the whole layout modifier
    Layout(
            content = {
                val minPage = (state.currentPage - offscreenLimit).coerceAtLeast(state.minPage)
                val maxPage = (state.currentPage + offscreenLimit).coerceAtMost(state.maxPage)
    
                for (page in minPage..maxPage) {
                    val pageData = PageData(page)
                    val scope = PagerScope(state, page)
                    key(pageData) {
                        Box(contentAlignment = Alignment.Center, modifier = pageData) {
                            scope.pageContent()
                        }
                    }
                }
            },
            modifier = modifier.draggable(
                orientation = Orientation.Horizontal,
                onDragStarted = {
                    state.selectionState = PagerState.SelectionState.Undecided
                },
                onDragStopped = { velocity ->
                    coroutineScope.launch {
                        // Velocity is in pixels per second, but we deal in percentage offsets, so we
                        // need to scale the velocity to match
                        state.fling(velocity / pageSize)
                    }
                },
                state = rememberDraggableState { dy ->
                    coroutineScope.launch {
                        with(state) {
                            val pos = pageSize * currentPageOffset
                            val max = if (currentPage == minPage) 0 else pageSize * offscreenLimit
                            val min = if (currentPage == maxPage) 0 else -pageSize * offscreenLimit
                            val newPos = (pos + dy).coerceIn(min.toFloat(), max.toFloat())
                            snapToOffset(newPos / pageSize)
                        }
                    }
                },
            ).scrollable(scrollable, Orientation.Horizontal)
        ) { measurables, constraints ->
            layout(constraints.maxWidth, constraints.maxHeight) {
                val currentPage = state.currentPage
                val offset = state.currentPageOffset
                val childConstraints = constraints.copy(minWidth = 0, minHeight = 0)
    
                measurables
                    .map {
                        it.measure(childConstraints) to it.page
                    }
                    .forEach { (placeable, page) ->
                        // TODO: current this centers each page. We should investigate reading
                        //  gravity modifiers on the child, or maybe as a param to Pager.
                        val xCenterOffset = (constraints.maxWidth - placeable.width) / 2
                        val yCenterOffset = (constraints.maxHeight - placeable.height) / 2
    
                        if (currentPage == page) {
                            pageSize = placeable.width
                        }
    
                        val xItemOffset = ((page + offset - currentPage) * placeable.width).roundToInt()
    
                        placeable.place(
                            x = xCenterOffset + xItemOffset,
                            y = yCenterOffset
                        )
                    }
            }
        }
    I'm not sure if I have to do anything in the
    rememberScrollableState
    lambda to ensure the composable gets scrolled (it gets fired when I drag, but nothing happens onscreen). It does not take a suspend function so I can't do the same thing as the draggableState does with the animatable offset, etc. Is there any way I can scroll this thing without the draggable modifier?
    And will the layout actually scroll with measurements like this?