Modifier es un objeto inmutable en Jetpack Compose que define las propiedades de un componente de UI: tamaño, relleno, fondo, manejo de gestos y comportamiento. Los modificadores se combinan en una cadena mediante llamadas secuenciales, y el orden de su aplicación afecta críticamente el resultado. Según Google Android Developers, 2026, el uso correcto de Modifier es la base para construir interfaces flexibles y de alto rendimiento en UI declarativa.
Puntos clave
Modifier es una interfaz del paquete androidx.compose.ui que implementa el patrón Composite. Cada modificador es un elemento de cadena que envuelve al anterior y añade su propio comportamiento. Modifier es inmutable: cualquier cambio crea un nuevo objeto mediante copia con un nuevo elemento añadido a la cadena. Esto permite compartir de forma segura un único Modifier entre múltiples componentes.
Las funciones modificadoras básicas se llaman a través del objeto complementario Modifier (por ejemplo, Modifier.padding(), Modifier.fillMaxWidth()). Cada función devuelve un nuevo Modifier con el elemento añadido. Si hay varios modificadores, se combinan en una cadena: Modifier.padding(16.dp).fillMaxWidth().background(Color.Blue). El orden va de externo a interno en relación con el elemento de UI.
A diferencia de las Vistas tradicionales donde las propiedades se establecían mediante setters (view.setPadding(...), view.setBackground(...)), en Compose Modifier es una descripción declarativa. El componente no "aplica" los modificadores en tiempo de ejecución — LayoutNode recorre la cadena Modifier durante la composición y construye una lista de Modifier.Element que luego se procesan durante las fases de medición y diseño.
El orden de los modificadores es uno de los errores más comunes en Compose. Cada modificador envuelve al anterior y las operaciones se aplican de fuera hacia dentro. Por ejemplo, padding(16.dp).clickable { }: primero se añade relleno alrededor del elemento, luego el área de clic incluye el relleno. clickable { }.padding(16.dp): el área de clic es igual al tamaño del elemento primero, luego se añade relleno alrededor — hacer clic en el relleno no funcionará.
Regla mnemotécnica: lea la cadena de izquierda a derecha y aplíquela de fuera hacia dentro. El primer modificador es el más externo, se aplica al área alrededor del elemento. El último es el más interno, se aplica directamente al contenido. Los modificadores de tamaño (size, fillMaxWidth) deben ir después del relleno si se necesita relleno del padre, o antes del relleno si el contenido debe restringirse primero y luego centrarse.
Ejemplo: size(100.dp).padding(10.dp) — elemento de tamaño fijo 100dp, luego relleno de 10dp por fuera (tamaño final 120dp). padding(10.dp).size(100.dp) — el relleno de 10dp reduce el espacio disponible a (padre - 20dp), luego size(100dp) puede desbordar al padre. Siempre piense deliberadamente el orden, utilizando pruebas visuales para verificar el resultado.
| Orden | Resultado |
|---|---|
| padding → clickable | El clic funciona también en el área de relleno |
| clickable → padding | El clic funciona solo en el contenido, el relleno es zona muerta |
| size → padding | Elemento size(100), relleno por fuera → 100+2*pad |
| padding → size | El relleno reduce el espacio, size puede exceder los límites |
| background → padding | El fondo llena todo el elemento incluyendo el área externa |
| padding → background | El fondo solo dentro del relleno (área externa transparente) |
La biblioteca estándar de Compose incluye ~50+ modificadores divididos en categorías. Tamaño y posicionamiento: Modifier.size(), width(), height(), fillMaxSize(), fillMaxWidth(), fillMaxHeight(), defaultMinSize(), requiredSize(). Relleno y bordes: padding(), offset(), margin (se establece mediante padding del padre o Layout). Decoración: background(), border(), clip(), alpha(), shadow(), blur().
Comportamiento y gestos: clickable(), combinedClickable(), pointerInput(), draggable(), swipeable(). Diseño en contenedor: weight() (para Row/Column), align(), alignBy(), matchParentSize(). Semántica y accesibilidad: semantics(), testTag(), clearAndSetSemantics(). Dibujo: drawBehind(), drawWithContent(), drawModifier() — modificadores que permiten dibujo personalizado en el lienzo.
Modificadores semánticos son una categoría especial. Modifier.semantics {} define cómo se representará el elemento en el árbol de Accesibilidad. Compose rellena automáticamente la semántica a partir del texto, pero los componentes personalizados necesitan roles, estados y acciones establecidos manualmente. Esto es crítico para el cumplimiento de WCAG 2.2 y el funcionamiento correcto de TalkBack (Android) y VoiceOver (iOS).
@Composable
fun ModifierDemo() {
// Cadena de modificadores con orden correcto
Box(
modifier = Modifier
.size(150.dp)
.padding(8.dp)
.border(2.dp, Color.Gray)
.background(Color(0xFFE3F2FD))
.clickable { /* handle click */ }
.semantics {
contentDescription = "Demo card with click action"
role = Role.Button
}
) {
Text("Tócame")
}
}
Modifier.composed es un método de fábrica que permite crear modificadores compuestos que pueden usar otros modificadores, LocalComposition y estado local. A diferencia de una función de extensión regular, composed crea una instancia cada vez que se aplica, lo que permite que el modificador tenga su propio estado.
Cuándo usar composed: combinaciones recurrentes de modificadores (por ejemplo, estilo de tarjeta estándar: padding + background + border + clickable); modificadores con estado (cambio de fondo animado al presionar); acceso a CompositionLocals (esquema de colores de MaterialTheme, densidad de píxeles). Para casos ordinarios, una función de extensión regular sin composed es suficiente.
Rendimiento de composed: cada llamada crea un nuevo objeto modificador, lo que puede provocar asignaciones adicionales durante la recomposición. Para evitarlo, envuelva composed en remember. Google recomienda usar composed solo cuando realmente se necesite estado o CompositionLocal en su interior. Para combinaciones estáticas, use funciones de extensión regulares.
// Modificador personalizado mediante composed con estado
fun Modifier.cardStyle(
elevation: Dp = 4.dp,
isSelected: Boolean = false
): Modifier = this.composed {
val backgroundColor = if (isSelected)
MaterialTheme.colorScheme.primaryContainer
else
MaterialTheme.colorScheme.surface
this
.fillMaxWidth()
.padding(12.dp)
.background(backgroundColor, RoundedCornerShape(8.dp))
.shadow(elevation, RoundedCornerShape(8.dp))
}
// Ejemplo de uso
@Composable
fun CardList() {
Column {
Box(Modifier.cardStyle()) { Text("Elemento 1") }
Box(Modifier.cardStyle(isSelected = true)) { Text("Seleccionado") }
}
}
// Versión estática (sin composed) — más rápida
fun Modifier.simpleCardStyle(): Modifier =
this.fillMaxWidth().padding(8.dp).clip(RoundedCornerShape(4.dp))
Evite recrear Modifier en cada recomposición. Si el modificador no depende de datos mutables — muévalo a una constante o remember. Cada llamada a Modifier.padding().background() crea nuevos objetos Modifier.Element. En un componente aislado esto es insignificante, pero en un LazyColumn con cientos de elementos, las asignaciones adicionales causan un retraso notable en el desplazamiento.
Regla: si la cadena de modificadores no depende de los parámetros de la función Composable — declárela como val fuera de la función (a nivel de archivo o Companion). Si depende — use remember(dependencia) { ... }. Para los modificadores que son siempre iguales, val fuera del Composable es lo más eficiente: dichos objetos se crean una vez durante toda la vida de la aplicación.
Mejores prácticas de orden de Modifier: coloque los modificadores en orden lógico: primero tamaño/relleno (diseño), luego decoración (background, border), luego comportamiento (clickable, pointerInput). Esto no solo mejora la legibilidad sino que también ayuda a Compose Runtime a optimizar la cadena durante la medición. Evite también los elementos Box anidados excesivos con diferentes Modifier — a menudo un solo Modifier en el contenedor padre puede reemplazar 2-3 anidados.
// ✅ Bueno: constante fuera del Composable
private val cardModifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.clip(RoundedCornerShape(8.dp))
@Composable
fun CardContent() {
Box(cardModifier.background(Color.White)) { ... }
}
// ❌ Malo: recreación en cada recomposición
@Composable
fun BadCard() {
Box(Modifier.fillMaxWidth().padding(16.dp)) { ... }
}
// ✅ Bueno: remember para Modifier dinámico
@Composable
fun DynamicCard(color: Color) {
val modifier = remember(color) {
Modifier.fillMaxWidth().background(color)
}
Box(modifier) { ... }
}
Preguntas frecuentes
Sí, Modifier es inmutable, por lo que un objeto puede usarse de forma segura en múltiples lugares. Sin embargo, si usa un modificador composed, cada llamada crea una nueva instancia. Para cadenas estáticas, una constante o val fuera del Composable es la solución óptima.
Use Layout Inspector en Android Studio — muestra visualmente los límites de cada Modifier. Para depuración programática, añada Modifier.border() con diferentes colores en cada paso de la cadena para ver los límites de cada modificador.
Modifier.then(other) adjunta la cadena other a this. Las llamadas secuenciales (Modifier.a().b()) son equivalentes a Modifier.then(a()).then(b()). No hay diferencia — es el mismo mecanismo de cadena. then() es útil cuando necesita adjuntar una cadena ya preparada desde una variable.
Modifier.semantics {} define cómo se describirá el elemento a un lector de pantalla. Modifier.clickable() añade automáticamente el rol de Botón y Action(OnClick). Para gestos personalizados, debe especificar explícitamente semantics. Sin modificadores semánticos, los usuarios de TalkBack no podrán interactuar con componentes personalizados.
Modifier.background(color, shape) funciona con esquinas, pero clip() debe ir ANTES de background para que las esquinas se recorten. Orden correcto: clip(shape).background(color). Si necesita recortar el contenido interior también, use clipToBounds() en el padre.
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.
Lea también