0

I have the following @composable function:

@Composable
fun MessageCard(msg: Message){
   //Elements
}

And have observed that this piece of code :

@Composable
fun Conversation(messages: List<Message>) {
    LazyColumn {
        for(message in messages){
            MessageCard(message)
        }
    }
}

complains about : @Composable invocations can only happen from the context of a @Composable function

While on the other hand,

@Composable
fun Conversation(messages: List<Message>) {
    LazyColumn {
        items(messages) { message ->
            MessageCard(message)
        }
    }
}

Works just fine (it's taken from the official google docs).

By changing the LazyColumn into Column, both approaches work.

Looking through the docs it seemed to me like both LazyColumn and Column are composable functions, so what exactly is going on here. Thanks in advance.

Ait-Gacem Nabil
  • 165
  • 3
  • 12
  • 1
    I posted a link in my answer that explains difference between Composable scope and non-Composable scope referencing official document. Also you can out that doc here. https://medium.com/androiddevelopers/under-the-hood-of-jetpack-compose-part-2-of-2-37b2c20c6cdd – Thracian Aug 09 '23 at 09:08

2 Answers2

1

LazyRow or LazyColumns have content with LazyListScope receiver that is not annotated with @Composable which doesn't let you to call other Composables.

@Composable
fun LazyColumn(
    // Rest of params
     // Non Composable scope
    content: LazyListScope.() -> Unit
) {
  // Implementation details
}

LazyColumn(){ // LazyListScope
   // You can't add Composable here because this scope is LazyListScope
   // which is not annotated with @Composable, and returns compiler error
   // Row(){}
   
   items(100){
       Row() {
           
       }
   }
}

You can't call Composable functions from LazyListScope but you can call inside items because its content is @Composable function.

fun item(
    key: Any? = null,
    contentType: Any? = null,
    content: @Composable LazyItemScope.() -> Unit
) {
    error("The method is not implemented")
}

You can also check out this answer about composable and non-composble functions.

https://stackoverflow.com/a/73261429/5457853

Thracian
  • 43,021
  • 16
  • 133
  • 222
1

From the official documentation here

The Lazy components are different to most layouts in Compose. Instead of accepting a @Composable content block parameter, allowing apps to directly emit composables, the Lazy components provide a LazyListScope.() block. This LazyListScope block offers a DSL which allows apps to describe the item contents. The Lazy component is then responsible for adding the each item’s content as required by the layout and scroll position.

You can only invoke a composable function from another composable function context. Content of the LazyColumn itself is not a composible function rather it's a LazyListScope. But items() body is a composable function therefore you can call composable function within items.

Hope that clears it up.

Samuel Robert
  • 10,106
  • 7
  • 39
  • 60