Jetpack Compose es un toolkit declarativo moderno para construir interfaces de Android en Kotlin. El desarrollador describe la UI mediante funciones composable, y el toolkit redibuja automáticamente solo las partes que cambiaron. Según Android Developers (2026), Jetpack Compose funciona en Android 5.0 (API 21) y superior, es compatible con Material Design 3 y alcanza 120 FPS en dispositivos de gama media gracias a su propio sistema de Recomposition — un inteligente algoritmo diff que actualiza solo los widgets modificados.
Puntos clave
Jetpack Compose es un framework declarativo de Google para construir interfaces de usuario de Android, anunciado en 2019 y que alcanzó su versión estable en 2021. A diferencia del antiguo View System (maquetación XML + Activity/Fragment), Compose utiliza funciones Kotlin anotadas — @Composable. La interfaz se describe completamente en Kotlin: no hay separación entre XML y código. Esto eliminó la clase de errores relacionados con la discrepancia de IDs en XML y Kotlin (type-safe synthetic no ayudaba en la refactorización).
Compose está construido sobre su propio sistema de renderizado — Canvas, no vinculado a la jerarquía de View. Cada Composable se dibuja directamente en Canvas, sin pasar por onMeasure/onDraw de View System. Esto proporciona un aumento de rendimiento en pantallas complejas: en las pruebas de Google (2023), una pantalla en Compose con 200 elementos se renderizó un 40% más rápido que una similar en RecyclerView + ViewHolder.
Compose requiere minSdk 21 (Android 5.0) y Kotlin 1.9+. El BOM (Bill of Materials) de Compose sincroniza las versiones de todas las librerías de Compose. El framework es compatible con código existente de View System: Compose se integra mediante ComposeView en maquetaciones XML, y las View antiguas mediante AndroidView en la jerarquía de Compose. Según Google Play Console (2025), Android 5.0+ cubre el 97% de los dispositivos activos, por lo que la compatibilidad no es una limitación para la mayoría de los proyectos.
@Composable es una anotación que convierte una función Kotlin normal en un bloque de construcción de UI. Una función Composable describe cómo debería verse un fragmento de la interfaz — texto, botón, lista. En lugar de devolver un valor, la función emite componentes UI en la composición. Es similar a un generador: cada función añade elementos a la pantalla al ser llamada.
@Composable
fun ProfileCard(name: String, avatarUrl: String) {
Card(
modifier = Modifier.fillMaxWidth().padding(16.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
AsyncImage(
model = avatarUrl,
contentDescription = "Avatar",
modifier = Modifier.size(48.dp).clip(CircleShape)
)
Spacer(Modifier.width(12.dp))
Text(
text = name,
style = MaterialTheme.typography.titleMedium
)
}
}
}
La función ProfileCard recibe parámetros (name, avatarUrl) y emite Card → Row → AsyncImage + Text. Composición es el árbol de componentes emitidos en un solo pase. Si los parámetros no han cambiado, Compose omite la llamada a la función (recomposition skip). Si solo cambió name, solo se llamará a Text, el resto de elementos no se redibujarán. Esta recomposición inteligente es la ventaja clave de rendimiento de Compose frente a la optimización manual de View System.
Las funciones Composable utilizan activamente slots — trailing lambda, content: @Composable (() -> Unit). Esto permite crear contenedores: Card, Column, Row aceptan una lambda content, y el contenido se inserta en el slot. La Slot API reemplazó atributos XML como android:layout_gravity — ahora la posición de los elementos hijos se define mediante código Kotlin dentro del bloque de contenido.
State en Compose es cualquier valor que puede cambiar con el tiempo. Cuando el estado cambia, Compose programa la recomposición para todos los componentes que leen ese estado. El mecanismo se asemeja a React hooks: mutableStateOf devuelve MutableState<T>, la lectura de .value suscribe automáticamente la composición actual a los cambios.
@Composable
fun CounterExample() {
var count by remember { mutableStateOf(0) }
Column(modifier = Modifier.padding(16.dp)) {
Text("Pulsado: $count")
Button(onClick = { count++ }) {
Text("Incrementar")
}
}
}
@Composable
fun UserScreen(viewModel: UserViewModel) {
val userName by viewModel.userName.collectAsState()
Text("Usuario: $userName")
}
remember conserva el valor entre recomposiciones — de lo contrario mutableStateOf se crearía de nuevo en cada actualización de UI. collectAsState() convierte StateFlow de ViewModel en un estado compatible con Compose. Recomendación — usar ViewModel con StateFlow para el estado a nivel de pantalla, y mutableStateOf para el estado local (por ejemplo, tarjeta expandida). Esta separación sigue el principio de componentes inteligentes/mudos.
State Hoisting es un patrón de elevación del estado desde un componente hijo al padre. El padre pasa el valor y un callback mediante parámetros, el hijo llama al callback al cambiar. El padre mantiene el mutableStateOf, el hijo solo los parámetros. Esto hace que el componente sea reutilizable y testeable: el mismo TextField se puede usar con cualquier fuente de datos.
Modifier es un objeto que describe transformaciones del Composable: tamaño, rellenos, fondo, manejo de clics, animación, desplazamiento. Los modificadores se aplican mediante una cadena de llamadas: Modifier.fillMaxWidth().padding(16.dp).background(Color.Blue).clickable { }. Cada llamada devuelve un nuevo Modifier con la propiedad añadida — sin mutación del objeto original.
El orden de los modificadores es importante. Modifier.padding(16.dp).background(Color.Blue) colorea el área con relleno. Modifier.background(Color.Blue).padding(16.dp) colorea el rectángulo interior, y el relleno permanece transparente. La mecánica se asemeja al modelo de caja CSS: padding primero → background funciona como margin + background; background primero → padding funciona como background + relleno interior. El desarrollador solo necesita recordar: padding primero = margen exterior, padding después = relleno interior.
Si los modificadores incorporados no son suficientes, se crea uno personalizado mediante Modifier.composed { ... } o Modifier.then(). Dentro de un modificador personalizado se pueden usar mediciones de layout (Modifier.layout { measurable, constraints -> ... }), dibujo (Modifier.drawWithContent { ... }), gestos (Modifier.pointerInput { ... }). Ejemplo: un modificador para animación pulsante al hacer clic — mide el tamaño, al hacer clic inicia una animación de escala mediante animateFloatAsState.
Para animaciones, Compose proporciona animate*AsState (animateFloatAsState, animateColorAsState, animateDpAsState) — los valores se animan entre el estado antiguo y el nuevo al cambiar. Para animaciones de entrada/salida — AnimatedVisibility y AnimatedContent con transiciones integradas (fade, slide, expand). Todas las animaciones funcionan en la capa gráfica sin desencadenar composición innecesaria.
Las funciones Composable no deben realizar efectos secundarios directamente (solicitudes de red, temporizadores, suscripciones) — se invocan en cada recomposición, lo que provocaría solicitudes duplicadas. Para efectos secundarios, Compose proporciona una familia de funciones Effect: LaunchedEffect inicia una corrutina al entrar en la composición y la cancela al salir, DisposableEffect — para recursos que requieren limpieza explícita (sensores, BroadcastReceiver).
@Composable
fun SensorReader() {
val context = LocalContext.current
var sensorValue by remember { mutableStateOf(0f) }
DisposableEffect(Unit) {
val sensor = registerSensorListener(context) { value ->
sensorValue = value
}
onDispose {
unregisterSensorListener(sensor)
}
}
Text("Valor: $sensorValue")
}
@Composable
fun UserGreeting(userId: String) {
LaunchedEffect(userId) {
val profile = api.fetchProfile(userId)
// actualización del estado
}
}
LaunchedEffect(userId) se reinicia si userId cambia — la corrutina anterior se cancela y se inicia una nueva con el nuevo userId. Esto elimina la gestión manual de cancelación de solicitudes. DisposableEffect(Unit) — un efecto con clave fija Unit, se activa al entrar en la composición y llama a onDispose al salir. SensorReader registra un listener y se da de baja al salir de la pantalla — sin riesgo de fugas.
Si es necesario lanzar una corrutina no al entrar en la composición sino ante un evento (clic en un botón), se usa rememberCoroutineScope(). Devuelve un CoroutineScope vinculado al ciclo de vida del Composable, sin necesidad de DisposableEffect. Ejemplo: lanzar una solicitud de red al hacer clic en un botón — scope.launch { viewModel.loadData() }.
Elegir entre Compose y View System es la principal cuestión arquitectónica para los desarrolladores Android en 2026. Ambas tecnologías son compatibles con Google, pero Compose es la dirección principal en la que Google invierte recursos. View System recibe solo correcciones críticas y no evoluciona. La diferencia se manifiesta en la sintaxis, la gestión del estado, el rendimiento y el tiempo de desarrollo.
| Aspecto | Jetpack Compose | View System |
|---|---|---|
| Descripción de UI | Funciones Kotlin @Composable | Maquetación XML + Activity/Fragment |
| Estado | mutableStateOf, StateFlow, redibujado automático | findViewById, manual: setText, notifyDataSetChanged |
| Rendimiento | Recomposición inteligente, renderizado Canvas | Jerarquía de View, measure/layout/draw |
| Animaciones | animate*AsState, AnimatedVisibility, integradas | ValueAnimator, ObjectAnimator, Transition |
| Compatibilidad | minSdk 21, puentes ComposeView/AndroidView | Todas las versiones, cualquier |
| Tamaño APK | +3–5 MB para Compose | Sin sobrecarga |
Para proyectos nuevos, Google recomienda Jetpack Compose como estándar de desarrollo de UI. View System se mantiene para mantener código escrito antes de 2021, y para casos donde el tamaño mínimo del APK es crítico (por ejemplo, para mercados emergentes con dispositivos de gama baja). Compose reduce el volumen de código UI en un 30–50% en comparación con View System gracias a su sintaxis declarativa y animaciones integradas.
Preguntas frecuentes
Sí, mediante ComposeView en la maquetación XML. Añade la dependencia de Compose y envuelve la pantalla o parte de ella en ComposeView { MyComposable() }. La migración es pantalla por pantalla.
La razón es que el estado se eleva demasiado alto o se utilizan objetos mutables. Solución: derivedStateOf para datos derivados y remember para referencias estables.
Usa LazyColumn (análogo a RecyclerView). Los elementos se crean y reutilizan a medida que se desplaza. Para listas complejas con diferentes tipos de celdas — LazyColumn { items(items, key = { it.id }) { ... } }.
No, se puede empezar directamente con Compose. El conocimiento de View System ayuda al mantener código heredado, pero Compose es un ecosistema independiente con su propia documentación y patrones.
Sí, Material 3 es el tema estándar de Compose desde 2023. Se añade mediante implementation("androidx.compose.material3:material3"). Material 2 se considera obsoleto.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.