Instruction
The simplest way to build a scrolling list in XML is by using a ScrollView
. ScrollView
is an excellent candidate for building a scrolling UI when your list has fixed, finite and predictable items.
For those instances when your Compose UI calls for a simple list, with a fixed small set of items, the scroll modifiers in Compose are your go-to. They provide a convenient and efficient way to build a horizontal or vertical scrolling list.
Making Your Existing Row & Column Layouts Scrollable
By this point, you are already familiar with working with Row
and Column
composables. Depending on their direction, these layouts emit their items in the horizontal or vertical direction.
@Composable
fun Messages(messages: List<Message>) {
Column {
messages.forEach { message ->
MessageCard(message)
}
}
}
The example above shows that the resulting UI will be a vertical list of MessageCard
components.
Row
and Column
are layout wrappers that we use to arrange the UI, but, they are not scrollable by default. If your use case requires your Row
or Column
to be scrollable, you need to specify that explicitly using modifiers.
Scroll Modifiers
The horizontalScroll
and verticalScroll
modifiers are a simple way to allow users the ability to scroll through your UI’s contents.
@Composable
fun Messages(messages: List<Message>) {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
messages.forEach { message ->
MessageCard(message)
}
}
}
In the example above, to make the Column
of message objects scrollable, you used the verticalScroll
modifier and passed in rememberScrollState()
. This creates a scroll state based on the scroll orientation and persists the scroll position so it isn’t lost after recomposition.
Its counterpart, horizontalScroll
applies to Row
layouts and lets users scroll horizontally.
@Composable
fun Photos(photos: List<Photo>) {
Row(modifier = Modifier.horizontalScroll(rememberScrollState())) {
photos.forEach { photo ->
PhotoItem(photo)
}
}
}
When this change is deployed on the device, the Column will render all the items and for items that go outside the screen bounds, you’ll be able to scroll to see them.
Exploring the Scroll Modifier(s)
Before going any further, it’ll be helpful to learn how the scroll modifiers work.
fun Modifier.verticalScroll(
state: ScrollState,
enabled: Boolean = true,
flingBehavior: FlingBehavior? = null,
reverseScrolling: Boolean = false
)
Here’s what the different parameters do
-
scrollState
is the current state of the scroll. Scroll state determines the offset from the top and can also be used to start or stop smooth scrolling and fling animations. -
enabled
enables or disables scrolling. If disabled, you can still programmatically scroll to a specific position using thestate
property scrolling via gestures will not work. -
flingBehavior
is used to perform a fling animation with a given velocity. -
reverseScrolling
allows you to reverse the direction of the scroll. In other words, setting it totrue
lets you scroll up. Note that its default value isfalse
.
Since verticalScroll
is a modifier, you can use it to make your custom composables scrollable.
Scrolling modifiers are a poor choice if you need to render a larger dataset or if dynamic data back your list. This is because scrollable composables compose and render all the elements inside eagerly, which can be heavy when you have many elements to display.
Like in XML, you have RecyclerView
for efficiently rendering a large dynamic list; Jetpack Compose also comes with dedicated composables, which you’ll learn about in the next lesson.
Understanding the ScrollState Object
Both scrolling modifiers require a ScrollState
to be passed as a parameter. To simplify this process, you can use the rememberScrollState
function which creates and stores a ScrollState
object for you. This object tracks the current scroll position and can be utilized with scrollable composables to alter their scroll behavior.
The crucial aspect in this context is the “remember” section, which guarantees that the scroll position remains unchanged across recompositions. This prevents the scrollable element from resetting to its default state, such as scrolling back to the top, whenever the user interface is redrawn.
The ScrollState
object provides several helpful properties to manipulate the scroll behavior.
- You can use the
value
property to know how far the content has scrolled. - You can use the
scrollTo(value: Int)
function to scroll to a specified position instantly. - You can use the
animateScrollTo(value: Int, animationSpec: AnimationSpec<Int> = default)
to scroll to a specified position with an animation. - You can use the
isScrollInProgress
flag to know if there is an ongoing scroll action. This can be useful for showing or hiding UI elements based on the scroll state.
These handy properties and functions let you control and customize the scroll behaviour.