I'm trying to create simple Dice Roller JetPack Compose App. I create an Image compose and button compose. Whenever I click on the button, log statement shows button is clicked but I'm unable to update the dice image according to that data. I'll attach my code for future references. What am I doing wrong?
package com.example.mydiceroller
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.lifecycle.ViewModelProvider
class MainActivity : ComponentActivity() {
lateinit var mainViewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
Column(
modifier = Modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
DiceImage(count = mainViewModel.count)
Button(onClick = {
mainViewModel.rollDice()
}
)
{
Text(text = "Let's Roll")
}
}
}
}
}
@Composable
fun DiceImage(count: Int) {
Log.d("countValue", "${count}")
var countId=when (count) {
1 -> R.drawable.dice_1
2 -> R.drawable.dice_2
3 -> R.drawable.dice_3
4 -> R.drawable.dice_4
5 -> R.drawable.dice_5
else -> R.drawable.dice_6
}
Image(
painter = painterResource(id = countId), contentDescription = ""
)
}
MainViewModel.kt
package com.example.mydiceroller
import android.util.Log
import androidx.lifecycle.ViewModel
import java.util.Random
class MainViewModel : ViewModel() {
var count: Int = 1
fun rollDice(): Int {
count = (Random().nextInt(6) + 1)
Log.d("ViewModel", "ButtonClicked")
return count
}
}
I also tried mutableStateOf but failed
The problem is that your Compose UI cannot detect that count changed in the view model.
You need to store count in a State object. In a composable you would use something like this (by unwraps the State so it is an Int and can be more easily used):
var count: Int by remember { mutableStateOf(1) }
Whenever this variable is updated a recomposition is triggered and the UI is updated accodingly. Since you use a view model to store count you need to employ a Flow:
class MainViewModel : ViewModel() {
private val _count = MutableStateFlow(1)
val count = _count.asStateFlow()
fun rollDice() {
Log.d("ViewModel", "ButtonClicked")
_count.value = (1..6).random()
}
}
Whenever rollDice is called the count flow changes. You can convert this flow in your composables into a Compose State object with collectAsStateWithLifecycle (You need the gradle dependency androidx.lifecycle:lifecycle-runtime-compose):
setContent {
val mainViewModel: MainViewModel = viewModel()
val count: Int by mainViewModel.count.collectAsStateWithLifecycle()
Column(
modifier = Modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
DiceImage(count = count)
Button(onClick = {
mainViewModel.rollDice()
}) {
Text(text = "Let's Roll")
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With