Nested Scroll with Jetpack Compose

Syntia
Blibli.com Tech Blog
3 min readJan 15, 2023

Jetpack Compose increases developer productivity in views, especially when it comes to RecyclerView and Adapter. We only need to

  • define a composable (LazyRow/LazyColumn),
  • set the items and components for each item,
  • … yeah, that’s all!

There are other composable such as Row and Column too. In this article, we'll see some use cases of Row/ Column, LazyRow/ LazyColumn, and how to handle the scroll action inside them.

Disclaimer: All source code exposed here has been developed with artifact December 2022 release of Compose Bom. Please note that some source code may change with new versions.

Row/Column

Row / Column can be used to arrange some views with a specific orientation (horizontal or vertical). In other words, we can treat Row/Column like LinearLayout in XML. The difference is scroll can be set directly to Row/Column modifier using verticalScroll or horizontalScroll.

LazyRow/LazyColumn

Compose provides LazyRow/ LazyColumn to replace RecyclerView in XML. Use these components when we need to:

  1. Display items with unknown/large sizes, with items() API inside LazyRow/LazyColumn (LazyListScope).
  2. Only compose and lay out items that are visible in the component’s viewport (same principle as RecyclerView).

Nested Scroll with Nested Items

Let’s take an example: Instagram home screen. The screen is scrollable with infinite posts. There’s also a horizontal scroll in the story section.

Typically, first, we will add a Column to wrap the story and post section as one vertical scroll. Then, use LazyRow and LazyColumn for each section as below.

If we try to preview the results, an error from Android Studio will be shown, which prevents us from creating nested scrolls with the same orientation without fixed height/width. By default view system (XML), it’s similar to adding RecyclerView inside a NestedScrollView. It may hurt app performance since the recycler's ability is omitted when it's inside a nested scroll.

java.lang.IllegalStateException: Vertically scrollable component was measured with an infinity maximum height constraints, which is disallowed. One of the common reasons is nesting layouts like LazyColumn and Column(Modifier.verticalScroll()). If you want to add a header before the list of items please add a header as a separate item() before the main items() inside the LazyColumn scope. There are could be other reasons for this to happen: your ComposeView was added into a LinearLayout with some weight, you applied Modifier.wrapContentSize(unbounded = true) or wrote a custom layout. Please try to remove the source of infinite constraints in the hierarchy above the scrolling container. at androidx.compose.foundation.CheckScrollableContainerConstraintsKt.checkScrollableContainerConstraints-K40F9xA(CheckScrollableContainerConstraints.kt:35) at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke-0kLqBqw(LazyList.kt:192) at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke(LazyList.kt:191) at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$1$2$1.invoke-0kLqBqw(LazyLayout.kt:71) .....

To achieve nested scroll, instead of creating a new LazyColumn inside a Column, we should directly wrap all the composable inside parent LazyColumn as below. The concept is similar to ConcatAdapter.

And the result is… Tadaaa! The nested scrolls work both horizontally and vertically.

Last, since the use case is common, let’s create a general composable component to make it reusable. The complete source code of the results can be shown below.

Happy coding.

Originally published at https://proandroiddev.com on January 15, 2023.

--

--