androidlistkotlinandroid-jetpack-composecomposable

Adding lists of Composables?


I have found that

val list: List<@Composable ()-> Unit> = listOf({Text("Cat")}, {Text("Dog")})

works, but

val list: List<@Composable ()-> Unit> = listOf({Text("Cat")}) + listOf({Text("Dog")})

produces the error "@Composable invocations can only happen from the context of a @Composable function" on the first Text().

Why is it that adding lists works fine with things like

val list: List<String> = listOf("Cat") + listOf("Dog")

but doesn't seem to work with Composables? Is there a way how I can add lists of Composables to each other?


Solution

  • The difference in behavior between the two scenarios is related to how Jetpack Compose handles the @Composable functions and the context in which they are invoked.

    In the first scenario:

    val list: List<@Composable ()-> Unit> = listOf({Text("Cat")}, {Text("Dog")})
    

    Both Text("Cat") and Text("Dog") are wrapped in @Composable lambda expressions within the list. When you use the list directly, the lambda expressions are invoked within the context of a @Composable function.

    In the second scenario:

    val list: List<@Composable ()-> Unit> = listOf({Text("Cat")}) + listOf({Text("Dog")})
    

    Here, you are using the + operator to concatenate two lists. The resulting list contains two @Composable lambda expressions, but they are not automatically invoked within a @Composable context.

    To Better understand, try with this example

    @Composable
    fun MainView() {
        val list: List<@Composable () -> Unit> = combineComposableLists()
    
        // Important: Render each composable in the combined list
        list.forEach { composable -> composable() }
    }
    
    @Composable
    fun combineComposableLists(): List<@Composable () -> Unit> {
        val list1: List<@Composable () -> Unit> = getList1()
        val list2: List<@Composable () -> Unit> = getList2()
    
        return list1 + list2
    }
    
    @Composable
    fun getList1(): List<@Composable () -> Unit> {
        return listOf(
            { Text("Cat") },
            { Text("Dog") }
        )
    }
    
    @Composable
    fun getList2(): List<@Composable () -> Unit> {
        return listOf(
            { Text("Fish") },
            { Text("Bird") }
        )
    }