2

I am trying to create a LazyColumn with a sticky header and a sticky sub-header. The functionality I would like to achieve what is demonstrated in this GIF.

My source code is as follows:

The data item which will be used

class Item(
val type: String, // Will be used as header
val subType: String, // Will be used as sub-header
val title: String,
val description: String,
val imageUrl: String)

An example of the item usage

Item("Movie", "Action",
        "Shang-Chi and the Legend of the Ten Rings",
        "Shang-Chi, the master of unarmed weaponry-based Kung Fu, is forced to " +
                "confront his past after being drawn into the Ten Rings organization.",
    "https://threepixelslab.gr/wp-content/uploads/2021/04/Shang-Chi-and-the-Legend-of-the-Ten-Rings.jpg")

The list composing

@Composable
fun MainList(data: List<Item>) {
    val mainGroup = data.groupBy { it.type }
    LazyColumn {
        mainGroup.forEach { (type, groupedData) ->
            val subGroup = groupedData.groupBy { it.subType }
            stickyHeader {
                Header(text = type)
            }
            item {
                LazyColumn {
                    subGroup.forEach { (subType, subGroupedData) ->
                        stickyHeader { Header(text = subType) }
                        items(subGroupedData) {
                            SimpleItem(item = it)
                        }
                    }
                }
            }
        }
    }
}

The error

java.lang.IllegalStateException: Nesting scrollable in the same direction layouts like LazyColumn and Column(Modifier.verticalScroll()) is not allowed. If you want to add a header before the list of items please take a look on LazyColumn component which has a DSL api which allows to first add a header via item() function and then the list of items via items().

at androidx.compose.foundation.ScrollKt.assertNotNestingScrollableContainers-K40F9xA(Scroll.kt:370) at androidx.compose.foundation.lazy.LazyListKt$LazyList$1.invoke-0kLqBqw(LazyList.kt:96) at androidx.compose.foundation.lazy.LazyListKt$LazyList$1.invoke(LazyList.kt:95) at androidx.compose.ui.layout.SubcomposeLayoutState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:345) at androidx.compose.ui.node.InnerPlaceable.measure-BRTryo0(InnerPlaceable.kt:43) at androidx.compose.foundation.layout.PaddingValuesModifier.measure-3p2s80s(Padding.kt:417) at androidx.compose.ui.node.ModifiedLayoutNode.measure-BRTryo0(ModifiedLayoutNode.kt:39) at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:219) at androidx.compose.ui.node.ModifiedLayoutNode.measure-BRTryo0(ModifiedLayoutNode.kt:39) at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.measure-BRTryo0(DelegatingLayoutNodeWrapper.kt:116) at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.measure-BRTryo0(DelegatingLayoutNodeWrapper.kt:116) at androidx.compose.ui.node.DelegatingLayoutNodeWrapper.measure-BRTryo0(DelegatingLayoutNodeWrapper.kt:116) at androidx.compose.ui.node.OuterMeasurablePlaceable$remeasure$3.invoke(OuterMeasurablePlaceable.kt:100) at androidx.compose.ui.node.OuterMeasurablePlaceable$remeasure$3.invoke(OuterMeasurablePlaceable.kt:99) at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:128) at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:75) at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:63) at androidx.compose.ui.node.OuterMeasurablePlaceable.remeasure-BRTryo0(OuterMeasurablePlaceable.kt:99) at androidx.compose.ui.node.OuterMeasurablePlaceable.measure-BRTryo0(OuterMeasurablePlaceable.kt:71) at androidx.compose.ui.node.LayoutNode.measure-BRTryo0(LayoutNode.kt:1227) at androidx.compose.foundation.layout.RowColumnImplKt$rowColumnMeasurePolicy$1.measure-3p2s80s(RowColumnImpl.kt:89) at androidx.compose.ui.node.InnerPlaceable.measure-BRTryo0(InnerPlaceable.kt:43) at androidx.compose.ui.node.OuterMeasurablePlaceable$remeasure$3.invoke(OuterMeasurablePlaceable.kt:100) at androidx.compose.ui.node.OuterMeasurablePlaceable$remeasure$3.invoke(OuterMeasurablePlaceable.kt:99) at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:128) at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:75) at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:63) at androidx.compose.ui.node.OuterMeasurablePlaceable.remeasure-BRTryo0(OuterMeasurablePlaceable.kt:99) at androidx.compose.ui.node.OuterMeasurablePlaceable.measure-BRTryo0(OuterMeasurablePlaceable.kt:71) at androidx.compose.ui.node.LayoutNode.measure-BRTryo0(LayoutNode.kt:1227) at androidx.compose.foundation.lazy.LazyMeasuredItemProvider.getAndMeasure-ZjPyQlc(LazyMeasuredItemProvider.kt:50) at androidx.compose.foundation.lazy.LazyListMeasureKt.measureLazyList-9CW8viI(LazyListMeasure.kt:145) at androidx.compose.foundation.lazy.LazyListKt$LazyList$1.invoke-0kLqBqw(LazyList.kt:152) at androidx.compose.foundation.lazy.LazyListKt$LazyList$1.invoke(LazyList.kt:95) at androidx.compose.ui.layout.SubcomposeLayoutState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:345)

Is it possible to implement this kind of list in Compose?

LiTTle
  • 1,811
  • 1
  • 20
  • 37
  • You can't do this with out of the box LazyColumn as you are trying to use 2 different kind of sticky headers at the same time. Your exception comes from having a vertically scrolling composable (LazyList) within another vertically scrolling composable (LazyList). You'll need to write a custom solution. – Francesc Sep 18 '21 at 15:38
  • @Francesc Do you have any example of something custom made for Compose? Just to take an idea at first. – LiTTle Sep 18 '21 at 16:16
  • 1
    No, sorry, I don't have any examples of this nature, you may need to look into the LazyList sticky headers implementation from the Compose libraries and expand on that. – Francesc Sep 18 '21 at 16:19

0 Answers0