Ayush Patel
Ayush Patel

Reputation: 51

Updating item in a mutableStateList is not triggering recomposition in UI (LazyColumn)

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 -

Upvotes: 1

Views: 2134

Answers (2)

ObscureCookie
ObscureCookie

Reputation: 1128

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)
                )
            }
        }
    }
}

Upvotes: 0

Byte Code
Byte Code

Reputation: 161

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

Upvotes: 1

Related Questions