Blog
Android KMP Apr 27, 2026

Compose Multiplatform: migrar un módulo uikit de Android a CMP

Cómo migrar un módulo Compose Android a Compose Multiplatform: recursos, fuentes, Lottie y ConstraintLayout

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

ConceptoAndroidCMP (commonMain)
Acceso a drawablesR.drawable.xxxRes.drawable.xxx
Acceso a strings en ComposestringResource(R.string.xxx)stringResource(Res.string.xxx)
Tipo drawable en modelos@DrawableRes IntDrawableResource
FuentesFont(R.font.xxx) (objeto)Font(Res.font.xxx) (@Composable)
Tipografíaval Typography = Typography(...)@Composable fun rememberTypography()
LottieLottieAnimation(...)CircularProgressIndicator / Icon
ConstraintLayoutConstraintLayout { ... }Row / Column / Box