3

I am calling an increaseCount(id: String) method from a composable to increase the value of a parameter in a LiveData List of objects. The value gets updated quite alright but the UI only shows the update once I navigate out. PS: I am using a BottomNavigation view for navigating.

The Composable screen in question looks something like this

@Composabale
fun MyScreen(navController: NavHostController, viewModel: MyViewModel){
val itemList = viewModel.itemList.observeAsState().value
 LazyColumn(modifier = Modifier.fillMaxWidth()) {
        items(itemList) { item: Item? ->

            ItemView(item, viewModel)

        }
    }
}

@Composable
private fun ItemView(item: Item?, viewModel: MyViewModel) {
Row(
            modifier = Modifier.padding(4.dp, 0.dp),
            verticalAlignment = Alignment.CenterVertically,
            horizontalArrangement = Arrangement.End
        ) {
            //Counter section
            IconButton(onClick = { viewModel.decreaseItemCount(cartItem.itemId) }) {
                Icon(
                    imageVector = Icons.Filled.RemoveCircleOutline,
                    contentDescription = "Reduce order button",
                    tint = Color.Gray
                )
            }
            Text(text = "${item.itemQuantity}")
            IconButton(onClick = { viewModel.increaseItemCount(item.itemId) }) {
                Icon(
                    imageVector = Icons.Filled.AddCircleOutline,
                    contentDescription = "Increase order button",
                    tint = Color(R.color.yellow_700)
                )
            }
        }

The ViewModel side of things looks something like this:

private val _itemList = MutableLiveData<ArrayList<Item>>()
val itemList: LiveData<List<Item>>
  get() = _itemList

 fun increaseItemCount(id: String) {
    val theList = _itemList.value
    theList?.find { item ->
        item.itemId == id
    }?.let { item ->
        item.itemQuantity += 1
        _itemList.value = theList!!
    }
}

The decreaseItemCount function is similar to the increaseItemCount with the only difference being the count is decreasing.

MrBE
  • 103
  • 2
  • 10
  • 1
    check [this](https://stackoverflow.com/a/71596921/3585796) and [this](https://stackoverflow.com/a/70074376/3585796) – Phil Dukhov Mar 25 '22 at 07:40
  • @PylypDukhov I can't figure out what I'm doing wrong. I have modified the code to: `fun increaseItemCount(id: String) { val theList = _itemsList.value _itemsList.value?.find { item -> item.itemId == id }?.let { item -> item.itemQuantity += 1 val index = _itemsList.value!!.indexOf(item) setSelected(index, item.itemQuantity)}}` and `private fun setSelected(index: Int, itemQuantity: Int) { _itemsList.value!![index].itemQuantity = itemQuantity _itemsList.value = _itemsList.value }` – MrBE Mar 25 '22 at 10:17
  • @PylypDukhov also tried the other solution, setting up another method to update the view using the object itself as a parameter such as: `fun updateOne(item: Item) { val index = _convertedItemListLiveData.value?.indexOf(item) if (index != null) { _convertedItemListLiveData.value?.get(index)?.let { _convertedItemListLiveData.value?.set(index, it .copy(itemUid = item.itemUid, itemQuantity = item.itemQuantity + 1) ) } } }` – MrBE Mar 25 '22 at 10:19
  • 1
    `_itemsList.value = _itemsList.value` cannot trigger recomposition, as this is the same object. my answers contains all needed information – Phil Dukhov Mar 25 '22 at 11:48

1 Answers1

4

When you update an object property, LiveData cannot know that this object has changed. And a list still contains the same list of objects.

The solution is to actually create a new object with updated properties. data class is very comfy for this: declare your properties as val and update them using copy.

Check out Why is immutability important in functional programming?.

Live data version:

data class Item(val itemId: String, val itemQuantity: Int)

private val _itemList = MutableLiveData<List<Item>>()
val itemList: LiveData<List<Item>>
    get() = _itemList

fun increaseItemCount(id: String) {
    val theList = _itemList.value?.toMutableList() ?: return
    val index = theList.indexOfFirst { it.itemId == id }
    if (index == -1) return
    theList[index] = theList[index].let {
        it.copy(itemQuantity = it.itemQuantity + 1)
    }
    _itemList.value = theList
}

mutableStateListOf version is a little bit shorted and cleaner:

data class Item(val itemId: String, val itemQuantity: Int)

private val _itemList = mutableStateListOf<Item>()
val itemList: List<Item> = _itemList

fun increaseItemCount(id: String) {
    val index = _itemList.indexOfFirst { it.itemId == id }
    if (index == -1) return
    _itemList[index] = _itemList[index].let {
        it.copy(itemQuantity = it.itemQuantity + 1)
    }
}
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220