scrollableArea
Configure a component to act as a scrollable area. A scrollable area clips its content to its
scrollableArea
fun Modifier.scrollableArea(
state: ScrollableState,
orientation: Orientation,
enabled: Boolean = true,
reverseScrolling: Boolean = false,
flingBehavior: FlingBehavior? = null,
interactionSource: MutableInteractionSource? = null,
bringIntoViewSpec: BringIntoViewSpec? = null,
): Modifier
Configure a component to act as a scrollable area. A scrollable area clips its content to its bounds, renders overscroll, and handles scroll gestures such that the content, not the viewport, moves with the user's gestures.
This modifier is a building block for creating custom scrollable containers, and serves as a
higher-level abstraction over androidx.compose.foundation.gestures.scrollable. For simpler use
cases, prefer higher-level components that are built with scrollableArea, such as
verticalScroll and androidx.compose.foundation.lazy.LazyColumn. For example, verticalScroll
offsets the content in the viewport out of the box to have scrollable container behavior.
The primary distinction between scrollable and scrollableArea is in how scroll deltas are
handled. scrollableArea inverts the deltas to provide a natural "content-moving" experience.
For instance, dragging a finger up results in a positive scroll delta, which accommodates content
moving upwards within the layout. In contrast, the lower-level scrollable provides raw,
un-inverted deltas, which is useful for custom gesture handling that isn't directly tied to
content scrolling.
The direction of scrolling is automatically adjusted based on the orientation, the current
androidx.compose.ui.platform.LocalLayoutDirection, and the reverseScrolling flag. Setting
reverseScrolling to true is useful for layouts that grow from the end of the container to the
beginning, like a chat feed. In such cases, the content within the container should also be laid
out in reverse. The following table summarizes the resulting scroll delta for a user's drag
gesture:
orientation | LayoutDirection | reverseScrolling | User Gesture | Scroll Delta |
|---|---|---|---|---|
Vertical | Ltr and Rtl | false | Drag Up | Positive |
Vertical | Ltr and Rtl | true | Drag Up | Negative |
Horizontal | Ltr | false | Drag Left | Positive |
Horizontal | Ltr | true | Drag Left | Negative |
Horizontal | Rtl | false | Drag Left | Negative |
Horizontal | Rtl | true | Drag Left | Positive |
This scrollableArea overload uses overscroll provided through LocalOverscrollFactory by
default. See the other overload to manually provide an OverscrollEffect instance, or disable
overscroll.
Parameters
| state | The ScrollableState of the component. |
| orientation | The Orientation of scrolling. |
| enabled | Whether scrolling is enabled. |
| flingBehavior | logic describing fling behavior when drag has finished with velocity. If null, default from ScrollableDefaults.flingBehavior will be used. |
| reverseScrolling | reverses the direction of scrolling. This is useful for experiences where new items appear at the end and the list grows backwards. When reverseScrolling is true, the layout of the content inside the container should also be reversed by the user. For example, in a verticalScroll, setting reverseScrolling true will cause items to be laid out from bottom to top. When using scrollableArea directly in custom list implementations, ensure your layout logic also arranges content in reverse order (e.g. from end to start) to match the scroll behavior. |
| interactionSource | an optional hoisted MutableInteractionSource for observing and emitting Interactions for this scrollable area. Note that if null is provided, interactions will still happen internally. |
| bringIntoViewSpec | The configuration that this scrollable area should use to perform scrolling when scroll requests are received from the focus system. If null is provided, the system will use the behavior provided by androidx.compose.foundation.gestures.LocalBringIntoViewSpec which by default has a platform dependent implementation. |
fun Modifier.scrollableArea(
state: ScrollableState,
orientation: Orientation,
overscrollEffect: OverscrollEffect?,
enabled: Boolean = true,
reverseScrolling: Boolean = false,
flingBehavior: FlingBehavior? = null,
interactionSource: MutableInteractionSource? = null,
bringIntoViewSpec: BringIntoViewSpec? = null,
): Modifier
Configure a component to act as a scrollable area. A scrollable area clips its content to its bounds, renders overscroll, and handles scroll gestures such that the content, not the viewport, moves with the user's gestures.
This modifier is a building block for creating custom scrollable containers, and serves as a
higher-level abstraction over androidx.compose.foundation.gestures.scrollable. For simpler use
cases, prefer higher-level components that are built with scrollableArea, such as
verticalScroll and androidx.compose.foundation.lazy.LazyColumn. For example, verticalScroll
offsets the content in the viewport out of the box to have scrollable container behavior.
The primary distinction between scrollable and scrollableArea is in how scroll deltas are
handled. scrollableArea inverts the deltas to provide a natural "content-moving" experience.
For instance, dragging a finger up results in a positive scroll delta, which accommodates content
moving upwards within the layout. In contrast, the lower-level scrollable provides raw,
un-inverted deltas, which is useful for custom gesture handling that isn't directly tied to
content scrolling.
The direction of scrolling is automatically adjusted based on the orientation, the current
androidx.compose.ui.platform.LocalLayoutDirection, and the reverseScrolling flag. Setting
reverseScrolling to true is useful for layouts that grow from the end of the container to the
beginning, like a chat feed. In such cases, the content within the container should also be laid
out in reverse. The following table summarizes the resulting scroll delta for a user's drag
gesture:
orientation | LayoutDirection | reverseScrolling | User Gesture | Scroll Delta |
|---|---|---|---|---|
Vertical | Ltr and Rtl | false | Drag Up | Positive |
Vertical | Ltr and Rtl | true | Drag Up | Negative |
Horizontal | Ltr | false | Drag Left | Positive |
Horizontal | Ltr | true | Drag Left | Negative |
Horizontal | Rtl | false | Drag Left | Negative |
Horizontal | Rtl | true | Drag Left | Positive |
This overload allows providing OverscrollEffect that will be rendered within the scrollable
area. See the other overload of scrollableArea in order to use a default OverscrollEffect
provided by LocalOverscrollFactory.
Parameters
| state | The ScrollableState of the component. |
| orientation | The Orientation of scrolling. |
| overscrollEffect | the OverscrollEffect that will be used to render overscroll for this scrollable area. Note that the OverscrollEffect.node will be applied internally as well - you do not need to use Modifier.overscroll separately. |
| enabled | Whether scrolling is enabled. |
| flingBehavior | logic describing fling behavior when drag has finished with velocity. If null, default from ScrollableDefaults.flingBehavior will be used. |
| reverseScrolling | reverses the direction of scrolling. This is useful for experiences where new items appear at the end and the list grows backwards. When reverseScrolling is true, the layout of the content inside the container should also be reversed by the user. For example, in a verticalScroll, setting reverseScrolling true will cause items to be laid out from bottom to top. When using scrollableArea directly in custom list implementations, ensure your layout logic also arranges content in reverse order (e.g. from end to start) to match the scroll behavior. |
| interactionSource | an optional hoisted MutableInteractionSource for observing and emitting Interactions for this scrollable area. Note that if null is provided, interactions will still happen internally. |
| bringIntoViewSpec | The configuration that this scrollable area should use to perform scrolling when scroll requests are received from the focus system. If null is provided, the system will use the behavior provided by androidx.compose.foundation.gestures.LocalBringIntoViewSpec which by default has a platform dependent implementation. |
Code Examples
ScrollableAreaSample
@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
}
}
}
}