Home > other >  LiveData value not updating UI in Jetpack Compose until I navigate out
LiveData value not updating UI in Jetpack Compose until I navigate out

Time:03-27

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.

CodePudding user response:

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