0

I have been scratching my head over this problem. I have a data class representing items that can be selected(or unselected).

data class FilterItem(     
    val name: String,     
    val isSelected: Boolean 
)

I have created a mutableStateList of items in my ViewModel which creates a list and that can be accessed by composable.

val filterList = mutableStateListOf<FilterItem>()

 filterList.addAll(listOf(
            FilterItem("ABC", isSelected = false),
            FilterItem("DEF", isSelected = false)
))
       

My composable accesses the list -

FilterLayout(
                filterList = viewModel.filterList
                )
@Composable
fun FilterLayout(
    filterList: SnapshotStateList<FilterItem>
){
.....
 itemsIndexed(filterList)) { index, it ->
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 15.dp)
                        .clickable {
                            setSelected(it.name)
                        },
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Text(
                        text = it.name
                    )
                    Log.d("debug", "selected " + it.isSelected + "   " + it.name)
                    Checkbox(
                        isSelected = it.isSelected,
                        onCheckedChange = {},
                        modifier = Modifier.size(25.dp)
                    )
                }
            }
}

The problem here is, when clicked on an item, setSelected gets called, which updates the list (till here it's good). The change is reflected in logcat, when I print my message. That means list is getting updated, and recomposed. I can see the item getting selected in logs, but Ui doesn't reflect that. Sometimes it does reflect instantly and sometimes I have to click on other rows to trigger recomposition and then it reflects.

Below is my update function -

fun setSelected(name: String) {
        viewModelScope.launch {
            val index = filterList.indexOfFirst { name == it.name }
            filterList[index] = filterList[index].copy(isSelected = !filterList[index].isSelected)
        }
    }

I tried replacing the checkbox with if-else block, if(it.isSelected) this icon else other icon still facing the same issue -

  • This is due to your place of setSelected() function call. You called it when clicked on the Row. But when you click on checkbox, the click action is consumed by Checkbox and hence no reflection. Try to also put the setSelected() function call in your on onCheckedChange{} lambda too. – Ahsan Ullah Rasel Jul 24 '23 at 05:00

2 Answers2

0

mutableStateListOf only notify the changes when a new item is added removed or replaced. Try the answers in this post.

Byte Code
  • 96
  • 1
  • 3
  • But as you can see in my setSelected function - it is replacing the current item. I tried logging, And I can see different hashcode before and after the setSelected for that object. – Ayush Patel Jul 21 '23 at 13:52
  • `fun setSelected(name: String) { filterList = filterList.map { if (it.name == name) it.copy(isSelected = !it.isSelected) else it }.toMutableStateList() }` If your list is not very long, try this. – Byte Code Jul 21 '23 at 20:17
0

When clicking the check box, nothing happens because its onCheckedChange does nothing. But when clicking the row, it updates successfully.

@Composable
fun FilterLayout(
    filterList: SnapshotStateList<FilterItem>,
    setSelected: (String) -> Unit,
) {
    LazyColumn(modifier = Modifier.fillMaxSize()) {
        itemsIndexed(filterList) { index, filterItem ->
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 15.dp)
                    .clickable {
                        setSelected(filterItem.name)
                    },
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically
            ) {
                Text(
                    text = filterItem.name
                )
                Log.d("debug", "selected " + filterItem.isSelected + "   " + filterItem.name)
                Checkbox(
                    checked = filterItem.isSelected,
                    // set on checked change to update selected state
                    onCheckedChange = { setSelected(filterItem.name) },
                    modifier = Modifier.size(25.dp)
                )
            }
        }
    }
}
ObscureCookie
  • 756
  • 2
  • 10