CheckBox State is not changing in jetpack compose LazyColumn items()

168 Views Asked by At
  • I have a LazyColumn in which items() exist that holds 5 checkboxes, and the state of these can be controlled by a single parent variable as well as with a specific variable. The code is below:
 val areAllChecked = rememberSaveable {
                mutableStateOf(false)
            }
            MyApplicationTheme {
                LazyColumn(modifier = Modifier.fillMaxSize()) {
                    item {
                        IconButton(onClick = { areAllChecked.value = false }) {
                            Icon(imageVector = Icons.Default.Clear, contentDescription = null)
                        }
                    }
                    item {
                        Row(verticalAlignment = Alignment.CenterVertically){
                            Text(text = "Parent")
                            Checkbox(checked = areAllChecked.value, onCheckedChange = {
                                areAllChecked.value = it
                            })
                        }
                    }
                    items(5) {
                        val isChecked = rememberSaveable(areAllChecked.value) {
                            mutableStateOf(areAllChecked.value)
                        }
                        Row(verticalAlignment = Alignment.CenterVertically){
                            Text(text = it.toString())
                            Checkbox(checked = isChecked.value, onCheckedChange = {
                                isChecked.value = it
                            })
                        }
                    }
                }
            }
  • Here, areAllChecked is a parent variable for controlling the state, and isChecked is a local variable for checkboxes that changes when areAllChecked gets changed as expected.

  • But when I press the icon button, all of the checkboxes' states will be false only if areAllChecked is true. If it's not true, the local variable, which is isChecked, remain unchanged when they should also be set to false, as the IconButton onClick triggers the areAllChecked to false.

  • I've added the GIF here to show what I'm trying to solve.

How can i fix this?

Thank you.

1

There are 1 best solutions below

2
Chirag Thummar On BEST ANSWER

You need to wrap your item into the Data Class to manage the state of each item easily.

Here is the full example

Why use LaunchedEffect because we don't want to load it every time. We want to change only when the CheckBox of Parent changes.

Whenever changes are made in the single item we loop through and change its value and assign a new list to current.

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material3.Checkbox
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier


data class CheckedItem(
    val text: String, val isSelected: Boolean = false
)

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Stack021(modifier: Modifier = Modifier) {

    val list = listOf(
        CheckedItem("A", false),
        CheckedItem("B", false),
        CheckedItem("C", false),
        CheckedItem("D", false),
        CheckedItem("E", false),
        CheckedItem("F", false),
        CheckedItem("G", false)
    )

    var useList by remember {
        mutableStateOf(list)
    }


    val isAllAreCheck = rememberSaveable {
        mutableStateOf(false)
    }
    
    LaunchedEffect(isAllAreCheck.value) {
        useList = if (isAllAreCheck.value) {
            useList.mapIndexed { j, item ->
                item.copy(isSelected = true)
            }
        } else {
            useList.mapIndexed { j, item ->
                item.copy(isSelected = false)
            }
        }
    }

    Scaffold(topBar = {
        TopAppBar(title = {
            Text(text = "Multi Select Demo")
        })
    }) { padding ->


        Column(
            modifier = Modifier
                .padding(padding)
                .fillMaxSize()
        ) {

            IconButton(onClick = {
                println("All Are Unchecked")
                useList = useList.mapIndexed { j, item ->
                    item.copy(isSelected = false)
                }
            }) {
                Icon(imageVector = Icons.Default.Clear, contentDescription = null)
            }

            Row(verticalAlignment = Alignment.CenterVertically) {
                Text(text = "Parent")
                Checkbox(
                    checked = isAllAreCheck.value,
                    onCheckedChange = {
                        isAllAreCheck.value = it
                    }
                )
            }


            LazyColumn(
                modifier = Modifier
                    .fillMaxSize()
                    .weight(1f)
            ) {
                items(useList.size) { i ->

                    Row(verticalAlignment = Alignment.CenterVertically) {
                        Text(text = useList[i].text)
                        Checkbox(checked = useList[i].isSelected, onCheckedChange = {
                            useList = useList.mapIndexed { j, item ->
                                if (i == j) {
                                    item.copy(isSelected = it)
                                } else item
                            }
                        })
                    }

                }
            }
        }

    }
}