4

Using compose LazyColumn I would like a card for the header item, and a single card that contains the remaining items.

LazyColumn() {
  item {
    Card() { // header card}
  }

  // would like all items in single card
  Card() { // cannot do this, outside composable function
      items(myItems) { item ->
         // item here
      }
  }
}

Is something like this possible?

lostintranslation
  • 23,756
  • 50
  • 159
  • 262

3 Answers3

0

There is no point to have a LazyColumn if you just have 2 items. In this case, use a regular Column with verticalScroll if you need to:

Column(
    modifier = Modifier
        .fillMaxWidth()
        .verticalScroll(
            rememberScrollState()
        ),
) {
    Card(
        modifier = Modifier.fillMaxWidth()
    ) {
        header()
    }
    Card(
        modifier = Modifier.fillMaxWidth()
    ) {
        Column {
            item1()
            item2()
        }
    }
}
Francesc
  • 25,014
  • 10
  • 66
  • 84
  • Its not 2 items at all. Its 2 cards, the first card is one item, the second card has, many, many items. MyItems is a list. I want the list of items to be in a card. – lostintranslation Oct 08 '21 at 21:29
  • 2 cards = 2 items - that's all that matters as far as the LazyList is concerned. Whether 1 card has many items is irrelevant to the LazyList. – Francesc Oct 08 '21 at 21:52
  • 1
    Seems like a pretty big limitation on LazyColumn to not be able to add a surrounding view to the items. This is pretty common scenario, I get your solution, wondering if there was a way to use LazyColumn w/ my use case. – lostintranslation Oct 08 '21 at 21:58
  • Without knowing more about the specific UX requirements there isn't much more I can say, but you could move the Card to each item (so 1 Card per item instead of 1 Card for all items), but what you had originally is not possible. – Francesc Oct 08 '21 at 22:05
  • I agree with @Iostintraslation this is a big limitation. If for this scenario we decide to use just a vertical scrollable `Column` instead, I wonder what happen with the performance. Is the same using a scrollable Column than a LazyColumn? – Diego Palomar Oct 29 '21 at 15:15
0

I found sort of a work around for this, but it is not 100%. For instance, it does not have the card attributes. Maybe someone else has some bright ideas. Here's what I have:

@Composable
    fun QuerySearch(
    modifier: Modifier = Modifier,
    query: String,
    label: String,
    onDoneActionClick: () -> Unit = {},
    onClearClick: () -> Unit = {},
    onQueryChanged: (String) -> Unit
) {
    var showClearButton by remember { mutableStateOf(false) }

    TextField(
        modifier = modifier
            .fillMaxWidth()
            .onFocusChanged { focusState ->
            showClearButton = (focusState.isFocused)
        },
        value = query,
        onValueChange = onQueryChanged,
        label = { Text(text = label) },
        textStyle = MaterialTheme.typography.subtitle1,
        singleLine = true,
        trailingIcon = {
        if (showClearButton) {
            IconButton(onClick = { onClearClick() }) {
                Icon(imageVector = Icons.Filled.Close, contentDescription = "Clear")
            }
        }

    },
    keyboardActions = KeyboardActions(onDone = {
        onDoneActionClick()
    }),
    keyboardOptions = KeyboardOptions(
        imeAction = ImeAction.Done,
        keyboardType = KeyboardType.Text
    ),
        colors = TextFieldDefaults.textFieldColors(
            cursorColor = MaterialTheme.colors.secondary,
            textColor = MaterialTheme.colors.onPrimary,
            focusedIndicatorColor = MaterialTheme.colors.secondary,
        )

    )
}

@Composable
fun <T> AutoCompleteTextView(
    modifier: Modifier,
    query: String,
    queryLabel: String,
    onQueryChanged: (String) -> Unit = {},
    predictions: List<T>,
    onDoneActionClick: () -> Unit = {},
    onClearClick: () -> Unit = {},
    onItemClick: (T) -> Unit = {},
    itemContent: @Composable (T) -> Unit = {}
) {

val view = LocalView.current
val lazyListState = rememberLazyListState()
LazyColumn(
    state = lazyListState,
    modifier = modifier.heightIn(max = TextFieldDefaults.MinHeight * 6)
) {
    val lazyListScope = this

    item {
        QuerySearch(
            query = query,
            label = queryLabel,
            onQueryChanged = onQueryChanged,
            onDoneActionClick = {
                view.clearFocus()
                onDoneActionClick()
            },
            onClearClick = {
                onClearClick()
            }
        )
    }

    item {
        if (predictions.count() > 0) {
            Card(
                elevation = 4.dp,
                modifier = Modifier.padding(8.dp),
                backgroundColor = Color.White,
                shape = RoundedCornerShape(8.dp)
            ) {
                lazyListScope.items(predictions) { prediction ->
                    Row(
                        Modifier
                            .fillMaxWidth()
                            .clickable {
                                view.clearFocus()
                                onItemClick(prediction)
                            }
                        .background(MaterialTheme.colors.primaryVariant)
                        ) {
                            itemContent(prediction)
                        }
                    }
                }
            }
        }

    }
}

fun <T> LazyListScope.items(items: List<T>) {
    this.items(items)
}

This results in: address auto complete

Kristy Welsh
  • 7,828
  • 12
  • 64
  • 106
0

You can have something like this:

Card {
    Column {
        Text(text = "Header")
        LazyColumn {
            items(myItems) { 
                // ...
            }
        }
    }
}

But note that this can run into an IllegalStateException if you try to have an outer composable scroll in the same direction as well...but if you specify a specific height for your LazyColumn then it should be fine.

See this video about nesting same-direction columns:

Alvin Dizon
  • 1,855
  • 14
  • 34