'Itemdecoration in Jetpack compose

I am currently in the process of evaluating whether or not we can migrate our rather complex UI to jetpack compose at this stage and I am struggling with the following problem.

I am having an infinite scrolling vertical List of various different conceptual components. Some of them are headers, then there can be some text, some horizontally scrolling (infinite) lists and then there are some grouped components that are also stacked vertically but conceptionally belong to a group.

The conceptual design

@Compose
fun MyComplexList() {
 LazyColumn {
  item {
   // some header
  }
  item {
   // some horizontal content
   LazyRow {
    item {}
   }
  }
  item {
   // some other header
  }
  items(x) {
   // Some text for each item
  }
 }
}

As one can see this thing is rather trivial to do using compose and a lot less code than writing this complex RecyclerView + Adapter... with one exception: that background gradient, spanning (grouping) the Some infinite list of things component. (the tilted gradient in the image)

In the past (:D) I would use an ItemDecoration on the RecyclerView to draw something across multiple items, but I can't find anything similar to that in Compose.

Does anyone have any idea on how one would achieve this with compose?



Solution 1:[1]

After your answer, this is what I understood...

@Composable
fun ListWithBg() {
    val lazyListState = rememberLazyListState()
    val itemsCount = 50
    BoxWithConstraints(Modifier.fillMaxSize()) {
        ListBg(lazyListState, itemsCount, maxHeight) // see below
        LazyColumn(state = lazyListState, modifier = Modifier.fillMaxSize()) {
            item {
                Column(
                    Modifier
                        .fillMaxWidth()
                        .background(Color.White)
                ) {
                    Text(
                        text = "Some header",
                        style = MaterialTheme.typography.h5,
                        modifier = Modifier.padding(16.dp)
                    )
                }
            }

            item {
                Text(
                    text = "Some infinite list of things",
                    style = MaterialTheme.typography.h5,
                    modifier = Modifier.padding(16.dp)
                )
            }

            items(itemsCount) {
                Text(
                    text = "Item $it",
                    Modifier
                        .fillMaxWidth()
                        .padding(horizontal = 16.dp, vertical = 6.dp)
                        .background(Color.LightGray)
                        .padding(8.dp)
                )
            }
        }
    }
}

and to change the background in according to the background, you can define something like the following:

@Composable
fun ListBg(lazyListState: LazyListState, itemsCount: Int, maxHeight: Dp) {
    val firstVisibleIndex = lazyListState.firstVisibleItemIndex
    val totalVisibleItems = lazyListState.layoutInfo.visibleItemsInfo.size
    val hasNoScroll = itemsCount <= totalVisibleItems
    val totalHeight = if (hasNoScroll) maxHeight else maxHeight * 3
    val scrollableBgHeight = if (hasNoScroll) maxHeight else totalHeight - maxHeight
    val scrollStep = scrollableBgHeight / (itemsCount + 2 - totalVisibleItems)
    val yOffset = if (hasNoScroll) 0.dp else -(scrollStep * firstVisibleIndex)
    Box(
        Modifier
            .wrapContentHeight(unbounded = true, align = Alignment.Top)
            .background(Color.Yellow)
            .offset(y = yOffset)
    ) {
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(totalHeight)
                .drawBehind {
                    drawRoundRect(
                        Brush.linearGradient(
                            0f to Color.Red,
                            0.6f to Color.DarkGray,
                            1.0f to Color.Green,
                        ),
                    )
                }
        )
    }
}

Here is the result:

enter image description here

Solution 2:[2]

One of the options:

Replace:

items(x) {
   // Some text for each item
}

with:

item {
   Column(modifier = Modifier.border(...).background(...)) { //Shape, color etc...
      x.forEach {
          // Some text for each item
      }
   }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 bylazy