Search code examples
kotlincomponentsandroid-jetpack-composelazycolumn

When Can I Use A Composable in the Scope of a LazyList or Column?


I have built a number of projects and components with Jetpack Compose. I've read the docs and other tutorials, sites, and posts on StackOverFlow.

I am unclear about when I am allowed to use composables and composable functions inside of another composable, particularly with a LazyColumn.

I have components where I use a Row or a Column inside of a LazyColumn. Other times I try to create an itemsIndexed list that creates a series of Rows or Cards and I get an error that @Composable invocations can only happen from the context of a @Composable function.

Isn't the LazyColumn considered a @Composable function? The entire LazyColumn is within a @Composable function itself.

I'm confused about where the context changes from a composable environment to a non-composable environment.

Can anyone explain? What am I confusing?


Solution

  • @Composable invocations can only happen from the context of a @Composable function
    

    As this says you need to call a Composable from a function that is annotated with @Composable

    If you check LazyColumn function signature

    @Composable
    fun LazyColumn(
        // rest of the params
        content: LazyListScope.() -> Unit
    ) {
    
    }
    

    You will see that content is not a Composable. On the other hand items function use itemContent as Composable

    inline fun <T> LazyListScope.items(
        items: List<T>,
        noinline key: ((item: T) -> Any)? = null,
        noinline contentType: (item: T) -> Any? = { null },
        crossinline itemContent: @Composable LazyItemScope.(item: T) -> Unit
    ) = items(
        count = items.size,
        key = if (key != null) { index: Int -> key(items[index]) } else null,
        contentType = { index: Int -> contentType(items[index]) }
    ) {
        itemContent(items[it])
    }
    

    Because of this you can call Composable functions like Column inside item function.

    items(myItems) { myItem: MyItem ->
        Column{
    
        }
    }
    

    Functions like Column, Row or Box have their XScope functions marked with @Composable

    @Composable
    inline fun Column(
        modifier: Modifier = Modifier,
        verticalArrangement: Arrangement.Vertical = Arrangement.Top,
        horizontalAlignment: Alignment.Horizontal = Alignment.Start,
        content: @Composable ColumnScope.() -> Unit
    ) {
        val measurePolicy = columnMeasurePolicy(verticalArrangement, horizontalAlignment)
        Layout(
            content = { ColumnScopeInstance.content() },
            measurePolicy = measurePolicy,
            modifier = modifier
        )
    }
    

    And because of this you can call another Composable inside a Column, Row or Box.