Al migrar :core:uikit de Compose Android puro a Compose Multiplatform, la mayoría de los componentes no requieren cambios en su lógica. El grueso del trabajo son sustituciones sistemáticas: cómo se cargan los recursos, cómo se define la tipografía y qué hacer con las dependencias que son exclusivamente Android.
Recursos: de res/ a composeResources/
En un módulo Android normal los recursos viven en src/main/res/ y se accede a ellos con la clase R generada por AAPT. En CMP, los recursos se mueven a src/commonMain/composeResources/ y el acceso cambia completamente.
El compilador de Compose Resources genera una clase Res (con el paquete que se configura en build.gradle.kts) con propiedades tipadas para cada recurso:
// Antes (Android)
R.drawable.ic_fuel_station
R.string.filter_title
// Después (CMP)
Res.drawable.ic_fuel_station
Res.string.filter_title
Para strings también cambia la función de carga. androidx.compose.ui.res.stringResource no existe en commonMain. La alternativa directa es org.jetbrains.compose.resources.stringResource:
// Antes
import androidx.compose.ui.res.stringResource
Text(text = stringResource(R.string.filter_title))
// Después
import org.jetbrains.compose.resources.stringResource
Text(text = stringResource(Res.string.filter_title))
El tipo de los campos en los modelos de componentes también cambia. Los @DrawableRes Int pasan a ser DrawableResource, y los @StringRes Int pasan a ser directamente String (resuelto antes de construir el modelo, desde el composable que lo crea).
// Antes
data class FuelStationItemModel(
@DrawableRes val icon: Int,
@StringRes val label: Int,
...
)
// Después
data class FuelStationItemModel(
val icon: DrawableResource,
val label: String,
...
)
Tipografía: Font() es @Composable en CMP
En Android se puede llamar a Font(R.font.inter_bold) fuera de un contexto composable porque la carga es síncrona y local. En CMP, Font(Res.font.inter_bold) es una función @Composable — la carga de fuentes puede ser asíncrona dependiendo de la plataforma.
Esto obliga a convertir cualquier val interFamily = FontFamily(...) que esté en el nivel de objeto o módulo en una función @Composable:
// Antes (Android)
private val InterFamily = FontFamily(
Font(R.font.inter_bold, FontWeight.Bold),
Font(R.font.inter_medium, FontWeight.Medium),
// ...
)
val Typography = Typography(bodyLarge = TextStyle(fontFamily = InterFamily, ...))
// Después (CMP)
@Composable
fun rememberInterFamily() = FontFamily(
Font(Res.font.inter_bold, FontWeight.Bold),
Font(Res.font.inter_medium, FontWeight.Medium),
// ...
)
@Composable
fun rememberTypography(): Typography {
val interFamily = rememberInterFamily()
return Typography(bodyLarge = TextStyle(fontFamily = interFamily, ...))
}
Lo mismo aplica a cualquier sistema de tipografía propio. Si GasGuruTypography se construía con las fuentes en su valor por defecto, ahora hay que moverlo a una función rememberGasGuruTypography() que obtenga la familia desde el contexto composable.
Lottie y ConstraintLayout: dependencias solo-Android
com.airbnb.android:lottie-compose y androidx.constraintlayout:constraintlayout-compose no tienen equivalente multiplataforma. Al mover el código a commonMain, cualquier uso de estas librerías rompe la compilación.
Lottie: se usaba para animaciones de carga. La alternativa en CMP es Icon con un ImageVector o, si se necesita animación, AnimatedVisibility o animaciones de Compose puras. En este caso la animación de carga se reemplazó por un CircularProgressIndicator estándar, lo que simplificó el componente sin perder funcionalidad.
ConstraintLayout: se usaba para posicionar elementos con referencias entre ellos. En la mayoría de los casos es reemplazable por combinaciones de Row, Column, Box y Arrangement. No hay pérdida de expresividad para layouts normales.
Resumen de sustituciones
| Concepto | Android | CMP (commonMain) |
|---|---|---|
| Acceso a drawables | R.drawable.xxx | Res.drawable.xxx |
| Acceso a strings en Compose | stringResource(R.string.xxx) | stringResource(Res.string.xxx) |
| Tipo drawable en modelos | @DrawableRes Int | DrawableResource |
| Fuentes | Font(R.font.xxx) (objeto) | Font(Res.font.xxx) (@Composable) |
| Tipografía | val Typography = Typography(...) | @Composable fun rememberTypography() |
| Lottie | LottieAnimation(...) | CircularProgressIndicator / Icon |
| ConstraintLayout | ConstraintLayout { ... } | Row / Column / Box |