Configure a component to act as a scrollable area.
@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
}
}
}
}