Creación de un Arco de Progreso Animado en Compose

Clase 9 de 19Curso de Jetpack Compose en Android

Contenido del curso

Resumen

Darle personalidad a una aplicación significa ir más allá de los componentes predeterminados. Construir una vista personalizada de progreso con Canvas en Jetpack Compose permite visualizar cuántas tareas están completadas frente al total, utilizando arcos animados que se actualizan en tiempo real. A continuación se desglosa paso a paso cómo lograrlo.

¿Cómo preparar las variables para calcular el progreso?

Antes de dibujar cualquier forma, es necesario contar con los datos que alimentan la visualización. Dentro del componente SummaryInfo se declaran dos variables fundamentales [00:08]:

  • completedTask: número entero que representa las tareas completadas.
  • totalTask: número entero con el total de tareas, incluyendo las pendientes.

Con ambas se calcula el porcentaje de avance. La columna existente se envuelve dentro de un Row usando la opción Surround with Widget [00:30], lo que permite mostrar el círculo de progreso junto a la información de resumen sin alterar la estructura previa. Al Row se le asigna un verticalAlignment = Alignment.CenterVertically para que todos los elementos queden alineados horizontalmente en el centro.

Dentro de un Box se configuran tres propiedades clave [00:50]:

  • contentAlignment = Alignment.Center para centrar el contenido.
  • Un padding de dieciséis.
  • Un aspectRatio de uno, que mantiene la proporción del composable si llega a crecer.

El modificador aspect ratio resulta especialmente útil cuando se trabaja con imágenes en proporciones como 16:9 o 9:16, pero aquí garantiza que el círculo no se deforme.

¿Cómo dibujar arcos de progreso con Canvas y drawArc?

Canvas es el lienzo donde se dibujan formas como arcos, rectángulos o círculos directamente en píxeles [01:10]. Para construir el indicador se utilizan dos llamadas a drawArc: una para la base y otra para el progreso.

¿Qué parámetros controlan el arco base?

Se definen dos colores [01:18]:

  • colorBase: MaterialTheme.colorScheme.inversePrimary, la barra inferior que representa el total.
  • progress: MaterialTheme.colorScheme.primary, el color de acento que se superpone mostrando el avance.

El primer drawArc recibe estos parámetros [01:35]:

  • color = colorBase.
  • startAngle = 0f, que corresponde al centro horizontal del lado derecho del círculo.
  • sweepAngle = 360f, una circunferencia completa.
  • useCenter = false, porque no se dibuja desde el centro sino como una barra curva.
  • size heredado del Canvas.
  • style = Stroke(width = strokeWidth.toPx()), donde strokeWidth se declara en dp y se convierte a píxeles con .toPx() dentro del contexto del composable [02:08].
  • cap = StrokeCap.Round, que da un terminado curvo en los extremos de la línea [02:30].

¿Cómo se anima el arco de progreso?

El segundo drawArc solo se pinta si completedTask <= totalTask [02:45]. Su startAngle es noventa grados, comenzando desde el límite inferior del círculo. El sweepAngle se calcula como 360f * angleRatio.value.

La variable angleRatio se crea con remember { Animatable(0f) } [03:15], estableciendo un valor inicial de cero. Esto permite una transición suave en lugar de un salto brusco.

Para disparar la animación se utiliza un LaunchedEffect que observa completedTask y totalTask [04:00]. Cada vez que el usuario marca, desmarca una tarea o inserta una nueva, se recalcula:

kotlin LaunchedEffect(completedTask, totalTask) { if (totalTask == 0) return@LaunchedEffect angleRatio.animateTo( targetValue = completedTask.toFloat() / totalTask, animationSpec = tween(durationMillis = 300) ) }

La validación totalTask == 0 evita una excepción por división por cero que provocaría un crash [04:15]. La especificación de animación Tween genera una transición lineal de trescientos milisegundos, equivalente a 0.3 segundos [04:30].

¿Cómo mostrar el porcentaje en el centro del círculo?

En el medio del Canvas se coloca un Text que muestra el porcentaje calculado [03:40]:

kotlin Text( text = "${(completedTask.toFloat() / totalTask * 100).toInt()}%", style = MaterialTheme.typography.headlineMedium, color = MaterialTheme.colorScheme.surface, fontWeight = FontWeight.Bold )

La operación convierte la fracción a punto flotante, la multiplica por cien y la transforma a entero para obtener un valor limpio como "50%".

Al ejecutar el preview interactivo con completedTask = 5 y totalTask = 10, el arco se anima desde cero hasta el 50%, dibujándose desde el límite inferior central hasta el superior central [05:00]. Detener y reiniciar la interacción permite apreciar la fluidez de la animación.

Explora otros tipos de animationSpec más allá de Tween, como Spring o Keyframes, para darle un toque único a tu indicador de progreso. ¿Qué estilo de animación vas a probar primero? Compártelo en los comentarios.