3

I have a screen where I need to show a header and a list of items wrapped in a card view. The whole screen has to be scrollable (like shown in the image below).

enter image description here

I know how to do this with a scrollable Column but I want to be able to use a LazyColumn (because each list item will have its own ViewModel due to the complexity of the view and I thought LazyColumn will be more resources-efficient). For the header, I can use item and for the list, I can use items. Below is the code I tried:

@Composable
fun Screen(
  items: List<String>
) {
  Column(
    Modifier.fillMaxSize()
  ) {
    TopAppBar(title = { Text(text = "My Activity") })

    LazyColumn {

      // Header
      item {
        Text("Title", Modifier.padding(32.dp))
      }

      // I cannot use Box in this way here
      Box(Modifier.padding(32.dp)) {
        Card {
          items(items.size) {
            Text("Item $it")
          }
        }
      }
    }
  }
}

The problem with that code is that I cannot wrap the list items in a card view because Card is not a LazyListScope. Using LazyColumn, how can I wrap the list items in a Card?

Abhimanyu
  • 11,351
  • 7
  • 51
  • 121
Diego Palomar
  • 6,958
  • 2
  • 31
  • 42

2 Answers2

0

As a workaround, you can emulate Card by drawing a custom Shape.

It would look something like this: screen_record

sealed class Item
class HeaderItem(...): Item()
class ContentItem(...) : Item()

...

val items: SnapshotStateList<Item> = mutableStateListOf()

...

LazyColumn(
        modifier = Modifier.fillMaxSize(),
    ) {
        itemsIndexed(
            items = state.items,
        ) { index, item ->

            val prevItem = state.items.getOrNull(index - 1)
            val nextItem = state.items.getOrNull(index + 1)

            Column {
                when (item) {
                    is HeaderItem -> Header(item)

                    is ContentItem -> {
                        Box(
                            modifier = Modifier
                                .heightIn(min = 48.dp)
                                .fillMaxWidth()
                                .clip(shape = getShape(prevItem, nextItem, 16.dp))
                                .background(Color.Green.copy(0.3F))
                        ) {
                            Item(item)
                        }
                    }
                }
            }
        }
fun getShape(prevItem: Item?, nextItem: Item?, corner: Dp): Shape {
    return if (prevItem is ContentItem && nextItem is ContentItem) {
        //FLAT
        RectangleShape
    } else if (prevItem !is ContentItem && nextItem !is ContentItem) {
        //ROUNDED_ALL
        RoundedCornerShape(corner)
    } else if (prevItem !is ContentItem) {
        //ROUNDED_TOP
        RoundedCornerShape(topStart = corner, topEnd = corner)
    } else {
        //ROUNDED_BOTTOM
        RoundedCornerShape(bottomStart = corner, bottomEnd = corner)
    }
}
Vvv998
  • 1
-1

You had it slightly mixed up, you should put the Card inside of the items call:

    LazyColumn {
      // Header
      item {
        Text("Title", Modifier.padding(32.dp))
      }
      
      items(items.size) {
          // You can use Box in this way here
          Box(Modifier.padding(32.dp)) {
            Card {
              Text("Item $it")
            }
          }
       }
    }
Blundell
  • 75,855
  • 30
  • 208
  • 233