Animaciones y Transiciones Personalizadas en Jetpack Compose
Crear animaciones fluidas y significativas es crucial para ofrecer una experiencia de usuario pulida. Este artículo explora cómo crear animaciones y transiciones personalizadas en Jetpack Compose, desde animaciones básicas hasta implementaciones personalizadas complejas.
Creando Animaciones Personalizadas
Las animaciones personalizadas permiten efectos visuales más complejos y únicos:
Especificaciones de Animación Personalizadas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| @Composable
fun CustomAnimatedButton(
onClick: () -> Unit,
content: @Composable () -> Unit
) {
var isPressed by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
val scale by animateFloatAsState(
targetValue = if (isPressed) 0.95f else 1f,
animationSpec = spring(
dampingRatio = 0.4f,
stiffness = Spring.StiffnessLow
)
)
Box(
modifier = Modifier
.scale(scale)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null
) {
isPressed = true
onClick()
// Restablecer después de la animación usando un scope consciente del ciclo de vida
scope.launch {
delay(100)
isPressed = false
}
}
) {
content()
}
}
|

Animaciones Infinitas
Para animaciones continuas como indicadores de carga:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| @Composable
fun PulsatingDot(
color: Color,
size: Dp = 20.dp
) {
val infiniteTransition = rememberInfiniteTransition(label = "pulsating")
val scale by infiniteTransition.animateFloat(
initialValue = 0.6f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
animation = tween(1000),
repeatMode = RepeatMode.Reverse
),
label = "scale"
)
Box(
modifier = Modifier
.size(size)
.scale(scale)
.background(color, CircleShape)
)
}
|

Implementando Transiciones Personalizadas
Las transiciones ayudan a crear cambios suaves entre diferentes estados de la UI:
Transiciones de Contenido
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| @Composable
fun AnimatedContent(
content: @Composable () -> Unit,
modifier: Modifier = Modifier
) {
AnimatedVisibility(
visible = true,
enter = fadeIn() + expandVertically(),
exit = fadeOut() + shrinkVertically()
) {
Box(modifier = modifier) {
content()
}
}
}
|

Especificaciones de Transición Personalizadas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| @Composable
fun CustomTransitionCard(
expanded: Boolean,
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
val transition = updateTransition(
targetState = expanded,
label = "card_transition"
)
val cardElevation by transition.animateDp(
label = "elevation",
targetValueByState = { isExpanded: Boolean ->
if (isExpanded) 8.dp else 2.dp
}
)
val cardRoundedCorners by transition.animateDp(
label = "corner_radius",
targetValueByState = { isExpanded: Boolean ->
if (isExpanded) 0.dp else 16.dp
}
)
Card(
modifier = modifier,
elevation = CardDefaults.cardElevation(defaultElevation = cardElevation),
shape = RoundedCornerShape(cardRoundedCorners)
) {
content()
}
}
|

Mejores Prácticas y Optimización de Rendimiento
Al implementar animaciones personalizadas, ten en cuenta estas mejores prácticas:
1. Gestión del Estado de Animación
Mantén el estado de la animación cerca de donde se usa:
1
2
3
4
5
6
7
8
9
10
11
| @Composable
fun OptimizedAnimation() {
// ❌ No almacenes valores de animación a nivel de composable
// var scale by remember { mutableStateOf(1f) }
// ✅ Usa AnimationState o APIs animate*
val scale by animateFloatAsState(
targetValue = 1f,
label = "scale"
)
}
|
2. Consideraciones de Rendimiento
- Usa
remember
para cálculos costosos - Evita animar parámetros de layout cuando sea posible
- Considera usar
LaunchedEffect
para animaciones complejas
Conclusión
Las animaciones y transiciones personalizadas en Jetpack Compose proporcionan herramientas poderosas para crear experiencias de usuario atractivas. Al comprender los conceptos fundamentales y seguir las mejores prácticas, puedes crear animaciones fluidas y eficientes que mejoren la interfaz de usuario de tu aplicación.
Recuerda:
- Comienza con animaciones simples y aumenta gradualmente la complejidad
- Prueba las animaciones en diferentes dispositivos y tamaños de pantalla
- Considera las implicaciones de accesibilidad
- Monitoriza el impacto en el rendimiento
- Usa principios de animación para crear transiciones significativas
Para temas más avanzados, consulta nuestros otros artículos sobre optimización de rendimiento y gestión de estado en Compose.
Puedes encontrar todos los ejemplos de este post en el siguiente repositorio de GitHub: ComposeAnimations