Reputation: 593
I'm new with Kotlin and Jetpack Compose, and I have an issue in my Android app. When I run the code below, I can press the "Increase" button, and the Canvas shows the increased number. However, in the pointerInput
onTap
event, the number is always 1. Only when I force a recomposition by rotating the device, the new number is used.
It seems as if the state is used when it was first composited instead of the current state. How can I use the most recent state in the onTap
event?
Some background: I use the Canvas to draw a grid with a dynamic number of columns and rows. The number in my example determines the number of columns or rows. In the onTap
event I want to calculate which cell was clicked, to raise my own onCellTap
event. I do this by using the formula to calculate the position of the cells used in onDraw
, but in reverse. This all works fine, until a column or row is added dynamically, because I can't calculate with the "old" column count.
Instead of the mutableIntStateOf
in this example, I use a ViewModel
in my app. I removed this from the example for simplicity, I hope this does not matter.
@Composable
fun TopLevelComposable(modifier: Modifier = Modifier) {
var number by rememberSaveable { mutableIntStateOf(0) }
Column {
TestCanvasComposable(number)
Button(onClick = {
number += 1
}) {
Text(
text = "Increase",
modifier = modifier
)
}
}
}
@Composable
fun TestCanvasComposable(
number: Int,
modifier: Modifier = Modifier
) {
val textMeasurer = rememberTextMeasurer()
Canvas(modifier = modifier
.fillMaxWidth()
.height(30.dp)
.pointerInput(Unit) {
detectTapGestures(
onTap = {
// This does not show the correct number:
Log.i("Test", "The number is $number")
}
)
}, onDraw = {
drawRect(Color.Yellow, Offset.Zero, this.size)
drawText(
textMeasurer = textMeasurer,
text = buildAnnotatedString {
withStyle(ParagraphStyle(textAlign = TextAlign.Start)) {
// This works perfectly:
append("The number is: $number")
}
}
)
}
)
}
I tried to remove the TestCanvasComposable
Composable, and just put the Canvas code in the TopLevelComposable
. After this change it works fine. But I'd like to keep them apart for potential re-use.
Upvotes: 2
Views: 353
Reputation: 3232
You are losing the state because when @Composable
recomposes it resets the value of the number.
You can use rememberUpdatedState
to track changes in number
and remember
last updated number.
@Preview
@Composable
fun TopLevelComposable(modifier: Modifier = Modifier) {
var number by rememberSaveable { mutableIntStateOf(0) }
Column (modifier = Modifier.fillMaxSize()){
TestCanvasComposable(number)
Button(onClick = {
number += 1
}) {
Text(
text = "Increase",
modifier = modifier
)
}
}
}
@Composable
fun TestCanvasComposable(
number: Int,
modifier: Modifier = Modifier
) {
val num by rememberUpdatedState(newValue = number)
val textMeasurer = rememberTextMeasurer()
Canvas(modifier = modifier
.fillMaxWidth()
.height(30.dp)
.pointerInput(Unit) {
detectTapGestures(
onTap = {
// This does not show the correct number:
Log.i("Test", "The number is $num")
}
)
}, onDraw = {
drawRect(Color.Yellow, Offset.Zero, this.size)
drawText(
textMeasurer = textMeasurer,
text = buildAnnotatedString {
withStyle(ParagraphStyle(textAlign = TextAlign.Start)) {
// This works perfectly:
append("The number is: $num")
}
}
)
}
)
}
It's working Check this code
Upvotes: 2