2
votes

I built a lazy column that gets it`s data from ViewModel and every time I add an item the lazy column recomposes and the new Item is being viewed in the lazy column, But when I added the functionality of deleting an Item the lazy column does not recompose and the removed item stays on the screen until a configuration change happens (Like rotating the screen).

My question is how to make the lazy column know that the list has been updated after each Delete operation.

here is the ViewModel:

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.example.shoppinglistcompose.entities.Item

class AddNewShoppingListViewModel : ViewModel() {

val items = MutableLiveData<MutableList<Item>>(mutableListOf())

fun addNewItem(newItem: Item) {
    items.value = items.value?.plus(listOf(newItem)) as MutableList<Item>?
}

fun removeItem(item: Item) {
    items.value!!.remove(item)
    items.value = items.value?.plus(listOf()) as MutableList<Item>?
}
}

,The Compsable that shows the Lazy Column:

content = { paddingValues ->
        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
            modifier = Modifier
                .fillMaxSize()
                .padding(8.dp)
        ) {
            OutlinedTextField(
                value = text,
                onValueChange = { textFieldsViewModel.onShoppingListNameChanged(it) },
                label = { Text(text = "Shopping list Name") },
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(start = 30.dp, end = 30.dp, bottom = 8.dp)
            )
            if (!items.value?.isEmpty()!!) {
                LazyColumn(
                    Modifier
                        .animateContentSize()
                        .fillMaxSize()
                        .padding(paddingValues)
                ) {
                    items(items = items.value!!) {
                        SingleItem(item = it) {
                            addNewShoppingListViewModel.removeItem(it)
                        }
                    }
                }

            } else {
                Box(
                    modifier = Modifier
                        .fillMaxSize(),
                    contentAlignment = Alignment.Center
                ) {
                    Text(
                        text = "There are no shopping Items",
                        color = Color.Red,
                        style = MaterialTheme.typography.h5
                    )
                }
            }
        }
        if (showDialog.value) {
            MyDialog(
                showDialog = showDialog,
                textFieldsViewModel = textFieldsViewModel,
            ) {
                val name = textFieldsViewModel.shoppingItemText.value
                val quantity = textFieldsViewModel.itemQuantityText.value
                val item = Item(0, name!!, quantity!!.toInt(), 0)
                addNewShoppingListViewModel.addNewItem(item)
                textFieldsViewModel.shoppingItemText.value = ""
                textFieldsViewModel.itemQuantityText.value = ""
                showDialog.value = !showDialog.value
            }
        }

    },

And here is the Composable fun of the Item it self:

@Composable
fun SingleItem(item: Item, deleteClick: () -> Unit) {
val composableScope = rememberCoroutineScope()
val new: MutableState<Boolean> = remember {
    mutableStateOf(true)
}
val color = remember { Animatable(Color(0xFFB9F6CA)) }
LaunchedEffect(new.value) {
    color.animateTo(if (new.value) Color(0xFFB9F6CA) else Color(0xFFE0F2F1))
}
Card(
    modifier = Modifier
        .padding(4.dp)
        .fillMaxWidth(),
    shape = RoundedCornerShape(8.dp),
    backgroundColor = color.value,
    elevation = 3.dp
) {
    composableScope.launch {
        delay(2000)
        new.value = false
    }
    Row(modifier = Modifier.fillMaxWidth() , horizontalArrangement = Arrangement.SpaceBetween) {
        Column(
            modifier = Modifier,
            horizontalAlignment = Alignment.Start,
            verticalArrangement = Arrangement.SpaceBetween
        ) {
            Text(text = item.name, style = MaterialTheme.typography.h5)
            Text(text = "${item.quantity}", style = MaterialTheme.typography.h6)
        }
        IconButton(onClick = deleteClick) {
            Icon(Icons.Rounded.Delete, "Button for deleting an Item of the items list.")
        }
    }

}

}
1
Contribution tip: for your next question, try to only include enough of the code to reproduce the problem or explain the question. The question above contains a lot of information not relevant to the issue you were having and makes the question more difficult to answer.chuckj

1 Answers

5
votes

Try replacing the line,

items.value!!.remove(item)
items.value = items.value?.plus(listOf()) as MutableList<Item>?

with,

items.value = items.value?.filter { it != item }?.toMutableList()

The problem is that the expression items.value?.plus(listOf()) does not produce a different instance and the ViewModel doesn't republish the value because it is == to the previous value. This is because Kotlin will only allocate a new list if one of the operants of a list expression not equivalent to the result.

The use of filter above ensures that a new list is produce if item is in the list.