State hoisting for each item in LazyColumn

Viewed 323

I've gone through this codelab. In step number 7, when clicking on single row's text it's changing its color, but function will not keep track of it, meaning it will disappear after re-composition.

I want list to remember color of single item thus I've move state hoisting to the NameList function level. Unfortunately it's not working.

Where's the bug?

    @Composable
fun NameList(names: List<String>, modifier: Modifier = Modifier) {

    LazyColumn(modifier = modifier) {
        items(items = names) { name, ->
            val isSelected  = remember { mutableStateOf(false)}
            Greeting(name = name,isSelected.value){ newSelected -> isSelected.value = newSelected}
            Divider(color = Color.Black)
        }
    }
}

@Composable
fun Greeting(name: String,isSelected : Boolean, updateSelected : (Boolean) -> Unit) {

    val backgroundColor by animateColorAsState(if (isSelected) Color.Red else Color.Transparent)
    Text(
        modifier = Modifier
            .padding(24.dp)
            .background(color = backgroundColor)
            .clickable(onClick = { updateSelected(!isSelected)}),
        text = "Hello $name",

        )
}
1 Answers

You should hoist your selection state to the caller of NameList function.

@Composable
fun MyScreen() {
    // Fake list of names
    val namesList = (1..100).map { "Item $it" }
    // Here, we're keeping the selected positions. 
    // At the beginning, all names are not selected.
    val selection = remember {
        mutableStateListOf(*namesList.map { false }.toTypedArray())
    }
    
    NameList(
        // list of names
        names = namesList, 
        // list of selected items
        selectedItems = selection,
        // this function will update the list above
        onSelected = { index, selected -> selection[index] = selected },
        // just to occupy the whole screen 
        modifier = Modifier.fillMaxSize()
    )
}

Then, your NameList will look like this:

@Composable
fun NameList(
    names: List<String>,
    selectedItems: List<Boolean>,
    onSelected: (index: Int, selected: Boolean) -> Unit,
    modifier: Modifier = Modifier
) {
    LazyColumn(modifier = modifier) {
        itemsIndexed(items = names) { index, name ->
            Greeting(
                name = name, 
                isSelected = selectedItems[index],
                updateSelected = { onSelected(index, it) }
            )
            Divider(color = Color.Black)
        }
    }
}

Nothing changes on Greeting function.

Here is the result:

enter image description here

Related