I'm new in Android development. Currently I'm trying to do a true or false quizz app. Its main screen presents a sentence and two buttons, True and False, which are white at first. Just one button can be clicked: if it is the right answer, it must immediately change its color to green, if it's the wrong one, it will change to red. Under these True/False buttons there are two more, the navigation buttons, Previous and Next, which allow the user to navigate through different sentences, each with its own answers.
I have already coded the logic part, using printl() to check the if/else behavior. However, the color of the buttons isn't changing properly. Sometimes I have to go to the next sentence and back for the button to get to the right color from white, and some other times colors don't work at all.
I suspect that if recomposition of the composable is forced everything will be solved. Is there a way to force recomposition?
@Composable
fun Options(modifier: Modifier, i: Int) {
ConstraintLayout(modifier) {
val (trueButton, falseButton) = createRefs()
val chain =
createHorizontalChain(trueButton, falseButton, chainStyle = ChainStyle.SpreadInside)
OptionButton(modifier = Modifier.constrainAs(trueButton) {
start.linkTo(parent.start)
top.linkTo(parent.top)
end.linkTo(falseButton.start)
bottom.linkTo(parent.bottom)
}, text = "TRUE", i, 0)
OptionButton(modifier = Modifier.constrainAs(falseButton) {
start.linkTo(trueButton.end)
top.linkTo(parent.top)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}, text = "FALSE", i, 1)
}
}
@Composable // THIS IS THE FUNCTION WHERE I HAVE THE PROBLEM
fun OptionButton(modifier: Modifier, text: String, i: Int, order: Int) {
var color by remember { mutableStateOf(questions[i].colors[order]) }
/*if (color == Color.Green) println("color is GREEN")
if (color == Color.Red) println("color is RED")*/
Button(modifier = modifier
.height(80.dp)
.width(160.dp), colors = ButtonDefaults.buttonColors(
contentColor = Color.Black, containerColor = questions[i].colors[order]
), border = BorderStroke(width = 4.dp, color = Color.Black), onClick = {
if (questions[i].isChecked == false) { // SENTENCE HAS NOT BEEN ANSWERED
if (questions[i].options[order] == questions[i].answer) {
questions[i].isChecked = true
println("RIGHT ANSWER")
questions[i].colors[order] = Color.Green
color = questions[i].colors[order]
} else { // SENTENCE HAS BEEN ANSWERED
questions[i].isChecked = true
println("WRONG ANSWER")
questions[i].colors[order] = Color.Red
color = questions[i].colors[order]
}
} else {
}
}) {
Text(text = text, fontSize = 30.sp)
}
}
data class Question(
val sentence: String,
val options: Array<String> = arrayOf("True", "False"),
var isChecked: Boolean = false,
var colors: Array<Color> = arrayOf(Color.White, Color.White),
var answer: String
) {}
val questions = listOf<Question>(
Question("Elephant is the biggest living land animal", answer = "True"),
Question("Lions fly", answer = "False"),
Question("Whale is the biggest living animal", answer = "True"),
Question("Trees walk", answer = "False"),
Question("Rhinos are mammals", answer = "True"),
Question("Snakes are vegetarians", answer = "False")
)
I solved the problem. From official doc "Using mutable objects such as ArrayList or mutableListOf() as state in Compose causes your users to see incorrect or stale data in your app. Mutable objects that are not observable, such as ArrayList or a mutable data class, are not observable by Compose and don't trigger a recomposition when they change. Instead of using non-observable mutable objects, the recommendation is to use an observable data holder such as State<List> and the immutable listOf()."
I was using mutableListOf() instead of mutableStateListOf()
Here the code: