# Compose — Animaciones y Performance

## Animaciones

```kotlin
// AnimatedVisibility — mostrar/ocultar con transición
AnimatedVisibility(
    visible = mostrarFiltros,
    enter = slideInVertically() + fadeIn(),
    exit = slideOutVertically() + fadeOut()
) {
    PanelFiltros()
}

// animateContentSize — tamaño cambia suavemente
Card(
    modifier = Modifier
        .animateContentSize(
            animationSpec = spring(dampingRatio = Spring.DampingRatioMediumBouncy)
        )
) {
    Column {
        FacturasHeader(expandido = expandido, onClick = { expandido = !expandido })
        if (expandido) FacturasDetalle()
    }
}

// updateTransition — múltiples propiedades animadas juntas
val transicion = updateTransition(targetState = estado, label = "estado")
val color by transicion.animateColor(label = "color") { s ->
    when (s) {
        Estado.Activo -> MaterialTheme.colorScheme.primary
        Estado.Inactivo -> MaterialTheme.colorScheme.surfaceVariant
    }
}

// Animate*AsState — animación de valor único
val opacidad by animateFloatAsState(
    targetValue = if (visible) 1f else 0f,
    animationSpec = tween(durationMillis = 300),
    label = "opacidad"
)
```

---

## Performance — @Stable y Skippability

### Reglas de estabilidad

Compose puede saltarse la recomposición de un composable solo si todos sus
parámetros son estables. Un tipo es estable cuando:
- Es primitivo (Int, Boolean, String, etc.)
- Es un data class donde todos los campos son estables
- Está anotado con `@Stable` o `@Immutable`

```kotlin
// @Stable: el compilador confía en que equals() detecta cambios
@Stable
class FacturasState(
    val facturas: List<Factura>,
    val cargando: Boolean
) {
    override fun equals(other: Any?) = other is FacturasState &&
        facturas == other.facturas && cargando == other.cargando
}

// ImmutableList de kotlinx.collections.immutable
// La List<T> estándar NO es @Stable — causa recomposición innecesaria
@Composable
fun FacturasList(facturas: ImmutableList<Factura>) {
    LazyColumn {
        items(facturas, key = { it.id }) { FacturaItem(it) }
    }
}

// Lambdas estables en items de lista
// MAL — nueva lambda en cada recomposición del padre
items(facturas) { factura ->
    FacturaItem(onClic = { viewModel.seleccionar(factura.id) })
}

// BIEN — lambda estable con remember
items(facturas) { factura ->
    val onClic = remember(factura.id) { { viewModel.seleccionar(factura.id) } }
    FacturaItem(onClic = onClic)
}
```

### Diagnóstico con Layout Inspector

Activar "Recomposition counts" en Layout Inspector de Android Studio para
identificar composables que recomponen más veces de lo esperado.
