androidkotlinandroid-jetpack-composecomposable

LazyColumn and error @Composable invocations can only happen from the context of a @Composable function


I have a Composable that shows for each category a line with a dropdown etc and when i try to do a Foreach category do a line in the app so people can click and select a level it shows the Composable error @Composable invocations can only happen from the context of a @Composable function

levels Contains the List Of Categories

    LazyColumn(
                 modifier = Modifier.fillMaxWidth(),
                 verticalArrangement = Arrangement.spacedBy(8.dp)
              ) {
                 levels?.forEach { category ->
                     CategoryItem(category) //Error happens here
                }
            }
*emphasized text*

This is what CategoryItem Does:

@Composable
    fun CategoryItem(category: Category) {
        var expanded by remember { mutableStateOf(false) }

        Column(
            modifier = Modifier
                .fillMaxWidth()
                .padding(horizontal = 16.dp)
        ) {
            ClickableSquare(
                onClick = { expanded =!expanded },
                text = category.name
            )

            if (expanded) {
                DropdownMenu(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(horizontal = 16.dp),
                    expanded = expanded,
                    onDismissRequest = { expanded = false }
                ) {
                    category.levels.forEach { level ->
                        DropdownMenuItem(
                            text = { Text(text = level.name) },
                            onClick = {
                                // Navigate to GameScreen with level data
                                navController.navigate(route = AppScreens.GameScreen.route + "/${level.code}")
                            }
                        )
                    }
                }
            }
        }
    }

Solution

  • With LazyColumn, you can't use an ordinary loop to emit UI elements. Instead, you need to use the LazyListScope DSL like the item() and items() functions. Here's an example:

    LazyColumn(
        modifier = Modifier.fillMaxWidth(),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        items(levels) {
            CategoryItem(it)
        }
    }
    

    Additionally, it's important to use the key parameter within the items() function, especially when your list items can be reordered.

    LazyColumn(
        modifier = Modifier.fillMaxWidth(),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        items(
            items = levels,
            key = { it.id }
        ) { level ->
            CategoryItem(level)
        }
    }