Reputation: 521
Below is a simple Cart Implementation where the value of qty should increase by 1 if a list item is clicked...
But somehow values in the ViewModel are changing, but not reflected in UI ( not recomposing with new qty value)
I think problem lies in this part :
LazyColumn {items(cartInfo.value!!.cartItems){...}
as cartItems
is not directly a state value that's why not triggering the recomposition of the LazyColumn UI.
But cant solve this problem as mutableStateOf<CartInfo?>
is fixed and I don't want a separate state for MutableList<CartItem>
data class User(var id: String, var name: String)
data class Product(var id: String, var name: String)
data class CartItem(var product: Product, var qty: Int)
data class CartInfo(val userid: String, val cartItems: MutableList<CartItem>)
val user = User("1", "A")
val item1 = CartItem(Product("1", "iPhone"), 1)
val item2 = CartItem(Product("2", "Xbox"), 1)
val cart = CartInfo(user.id, mutableListOf(item1, item2))
class MainViewModel : ViewModel() {
private val _cartInfo = mutableStateOf<CartInfo?>(null)
val cartInfo: State<CartInfo?> = _cartInfo
init {
_cartInfo.value = cart
}
fun increaseQty(product: Product, user: User) {
viewModelScope.launch {
var cartInfo = cartInfo.value
if (user.id == cartInfo?.userid) {
var cartItems = cartInfo.cartItems.map {
if (it.product.id == product.id) {
it.qty += 1
it
} else
it
}
_cartInfo.value = cartInfo.copy(cartItems = cartItems as MutableList<CartItem>)
}
// Log.d("debug", "viewmodel: ${cartInfo?.cartItems.toString()}")
}
}
}
class MainActivity : ComponentActivity() {
private val viewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
val cartInfo = viewModel.cartInfo
Log.d("debug", cartInfo.toString())
LazyColumn {
items(cartInfo.value!!.cartItems) {
Card(modifier = Modifier
.fillMaxWidth()
.padding(5.dp)
.clickable {
viewModel.increaseQty(it.product, user)
}) {
Row() {
Text(
text = "${it.product.name}",
modifier = Modifier.weight(1f).padding(10.dp)
)
Text(
text = "qty: ${it.qty}"
)
}
}
}
}
}
}
}
}
}
Thanks in advance
Upvotes: 2
Views: 1566
Reputation: 521
Creating new CartItem
with CartItem(it.product, qty = it.qty + 1)
solved the problem:
fun increaseQty(product: Product, user: User) {
viewModelScope.launch {
var cartInfo = cartInfo.value
if (user.id == cartInfo?.userid) {
var cartItems = cartInfo.cartItems.map {
if (it.product.id == product.id) {
// old:
// it.qty += 1
// it
// changes made:
CartItem(it.product, qty = it.qty + 1)
} else
it
}
_cartInfo.value = cartInfo.copy(cartItems = cartItems as MutableList<CartItem>)
}
}
}
Upvotes: 0
Reputation: 87794
In this line _cartInfo.value = cartInfo
you set the same object in your _cartInfo
.
Mutable state cannot check if the state of your internal object has changed, it can only check if it is the same object or not.
You can make your CartInfo
a data class which allows you to easily create a new object with copy
. Make sure you do not use any var
or modifiable collections such as MutableList
to reduce the chance of error. Check out Why is immutability important in functional programming?.
data class CartInfo(val id: String, val cartItems: List<Item>)
// usage
_cartInfo.value = cartInfo.copy(cartItems = cartItems)
Upvotes: 2