Compose Modifier

scrollableArea

Configure a component to act as a scrollable area.

ScrollableAreaSample

@Sampled
@Composable
fun ScrollableAreaSample() {
    // This sample demonstrates how to create custom scrollable containers using the scrollableArea
    // modifier.

    // This ScrollableAreaSampleScrollState holds the scroll position and other relevant
    // information. It implements the ScrollableState interface, making it compatible with the
    // scrollableArea modifier, and is similar in function to the ScrollState used with
    // Modifier.verticalScroll.
    val scrollState = rememberScrollableAreaSampleScrollState()

    // For lists with many items, consider using a LazyLayout instead
    Layout(
        modifier =
            Modifier.size(150.dp)
                .scrollableArea(scrollState, Orientation.Vertical)
                .background(Color.LightGray),
        content = {
            repeat(40) {
                Text(
                    modifier = Modifier.padding(vertical = 2.dp),
                    text = "Item $it",
                    fontSize = 24.sp,
                    textAlign = TextAlign.Center,
                )
            }
        },
    ) { measurables, constraints ->
        var totalHeight = 0

        val childConstraints = constraints.copy(minWidth = 0, minHeight = 0)
        val placeables = measurables.map { measurable ->
            val placeable = measurable.measure(childConstraints)
            totalHeight += placeable.height
            placeable
        }

        val viewportHeight = constraints.maxHeight
        // Update the maximum scroll value to not scroll beyond limits and stop when scroll
        // reaches the end.
        scrollState.maxValue = (totalHeight - viewportHeight).coerceAtLeast(0)

        // Position the children within the layout.
        layout(constraints.maxWidth, viewportHeight) {
            // The current vertical scroll position, in pixels.
            val scrollY = scrollState.value
            val viewportCenterY = scrollY + viewportHeight / 2

            var placeableLayoutPositionY = 0
            placeables.forEach { placeable ->
                // This sample applies a scaling effect to items based on their distance
                // from the center, creating a wheel-like effect.
                val itemCenterY = placeableLayoutPositionY + placeable.height / 2
                val distanceFromCenter = abs(itemCenterY - viewportCenterY)
                val normalizedDistance =
                    (distanceFromCenter / (viewportHeight / 2f)).fastCoerceIn(0f, 1f)

                // Items scale between 0.4 at the edges of the viewport and 1 at the center.
                val scaleFactor = 1f - (normalizedDistance * 0.6f)

                // Place the item horizontally centered with a layer transformation for
                // scaling to achieve wheel-like effect.
                placeable.placeRelativeWithLayer(
                    x = constraints.maxWidth / 2 - placeable.width / 2,
                    // Offset y by the scroll position to make placeable visible in the viewport.
                    y = placeableLayoutPositionY - scrollY,
                ) {
                    scaleX = scaleFactor
                    scaleY = scaleFactor
                }
                // Move to the next item's vertical position.
                placeableLayoutPositionY += placeable.height
            }
        }
    }
}

/*
 * A custom implementation of ScrollableState that manages a scroll position and its maximum allowed
 * value.
 *
 * This is a simplified version of the `ScrollState` used by `Modifier.verticalScroll` and
 * `Modifier.horizontalScroll`, demonstrating how to implement a custom state for custom scrollable
 * containers.
 */
private class ScrollableAreaSampleScrollState(initial: Int) : ScrollableState {

    // The current integer scroll position in pixels.
    // This is backed by a mutableStateOf to trigger recomposition when it changes.
Jetpack Compose beyond MaterialFree components, screens and templates for apps that don’t look like Google’s. Copy them into your app, or let your coding agent add them.Browse for free

Content updated: