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 the state 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 to true lets you scroll up. Note that its default value is false.

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.

See forum comments
Download course materials from Github
Previous: Introduction Next: Demo