I have created a PieChart using Android Compose. I have added an animation to the chart that should trigger when a slice is clicked. However, the animation is only working on the first click.
Here is the relevant code:
@Composable
internal fun PieChart(
modifier: Modifier = Modifier,
colors: List<Color>,
inputValues: List<Float>,
textColor: Color = MaterialTheme.colorScheme.primary,
) {
val chartDegrees = 360f
var startAngle = 270f
val proportions = remember(inputValues) {
inputValues.map {
it * 100 / inputValues.sum()
}
}
val angleProgress = remember(proportions) {
mutableStateOf(
proportions.map { prop ->
chartDegrees * prop / 100
}
)
}
val clickedItemIndex = remember {
mutableStateOf(-1)
}
val progressSize = mutableListOf<Float>()
LaunchedEffect(angleProgress.value) {
progressSize.add(angleProgress.value.first())
for (x in 1 until angleProgress.value.size) {
progressSize.add(angleProgress.value[x] + progressSize[x - 1])
}
}
val density = LocalDensity.current
val textFontSize = with(density) { 30.dp.toPx() }
val textPaint = remember {
Paint().apply {
color = textColor.toArgb()
textSize = textFontSize
textAlign = Paint.Align.CENTER
}
}
Box(Modifier.padding(24.dp)) {
BoxWithConstraints(
modifier = modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
val canvasSize = min(constraints.maxWidth, constraints.maxHeight)
val size = Size(canvasSize.toFloat(), canvasSize.toFloat())
val canvasSizeDp = with(density) { canvasSize.toDp() }
val sliceWidth = with(LocalDensity.current) { 20.dp.toPx() }
val animatedWidth = remember { Animatable(initialValue = sliceWidth) }
LaunchedEffect(clickedItemIndex.value) {
if (clickedItemIndex.value != -1) {
animatedWidth.animateTo(
targetValue = sliceWidth * 1.5f,
animationSpec = tween(durationMillis = 600),
)
}
}
Canvas(
modifier = Modifier
.size(canvasSizeDp)
.pointerInput(inputValues) {
detectTapGestures { offset ->
val clickedAngle = touchPointToAngle(
width = canvasSize.toFloat(),
height = canvasSize.toFloat(),
touchX = offset.x,
touchY = offset.y,
chartDegrees = chartDegrees
)
progressSize.forEachIndexed { index, item ->
if (clickedAngle <= item) {
clickedItemIndex.value = index
return@detectTapGestures
}
}
}
}
) {
angleProgress.value.forEachIndexed { index, angle ->
val strokeWidth =
if (index == clickedItemIndex.value)
animatedWidth.value
else sliceWidth
drawArc(
color = colors[index],
startAngle = startAngle,
sweepAngle = angle,
useCenter = false,
size = size,
style = Stroke(width = strokeWidth)
)
startAngle += angle
}
if (clickedItemIndex.value != -1) {
drawIntoCanvas { canvas ->
canvas.nativeCanvas.drawText(
"${proportions[clickedItemIndex.value].roundToInt()}%",
((canvasSize / 2).toFloat()),
((canvasSize / 2).toFloat()),
textPaint
)
}
}
}
}
}
}
Does anyone know how to fix this issue so that the animation works on subsequent clicks?