Muchas veces no nos es suficiente con la configuración básica que nos provee una vista, y por tanto vamos a necesitar modificarla para adaptarla a nuestras necesidades. Esto es exactamente para lo que sirven los Modifiers.
Es un cajón de sastre que nos da la opción de añadir apariencia o capacidades extra a los Composable
s.
En el artículo anterior sobre layouts básicos ya vimos cómo usar algunas propiedades del Modifier
, pero en este artículo vamos a ver más a fondo qué tipos de modificadores tenemos, cómo se usan, por qué hay modificadores que aplican a unas vistas y otras no, y cómo preparar nuestros Composables
para que acepten un Modifier
.
Por compararlo con algo, sería el equivalente a las propiedades que usábamos en los XMLs para configurar las vistas.

¡Atención! Si quieres acceder más rápido a todo el contenido en vídeo, organizado, con contenido extra, soporte y muchas más sorpresas, puedes apuntarte gratis a mi formación Compose Expert y ver gratis el primer módulo de más de 3 horas con todo lo necesario para empezar.
El objeto Modifier
La forma de representar este elemento a nivel de código es usando simplemente un object
. El objeto Modifier
en realidad es un companion object de una interfaz con el mismo nombre.
En su forma más básica, este objeto solo implementa una serie de operaciones para poder combinar operaciones entre sí. A partir de aquí, se crean un montón de funciones de extensión sobre el mismo para ofrecer muchas funcionalidades distintas.
La forma de usarlo es muy sencilla. El objeto Modifier
ya nos da los valores por defecto para esa vista. Si no especificamos nada, es el que se usará.
Si queremos aplicar configuraciones extra, llamamos a las funciones que nos provee.
Su API es tipo builder, así que podemos concatenar varias operaciones seguidas hasta obtener el resultado que queremos. Ya vimos este ejemplo en el artículo anterior:
Row( modifier = Modifier .fillMaxWidth() .height(200.dp) ) { ... }
Aplicamos el fillMaxWidth()
, y luego una altura de 200dp.
Tipos de modifiers
La cantidad de modificaciones que podemos hacer sobre cada vista es muy grande, y luego además veremos que varía en función de diversos factores.
¿Qué tipos de modificadores nos podemos encontrar?
- De posicionamiento y tamaño: Indican a la vista cómo se va a posicionar en función del resto de vistas con las que interactúa, así como el espacio que ocupará en pantalla. Algunos ejemplos son
fillMaxWidth()
,wrapContentHeight()
, los propioswidth()
yheight
, oaspectRatio()
entre muchos otros. - De funcionalidad: hay algunos modifiers que amplían funcionalidad, como por ejemplo
clickable()
,toggeable()
, o incluso para añadir scroll a los layouts si su contenido ocupa más que el espacio en pantalla, conhorizontalScroll()
,verticalScroll()
y algunas variantes. - De apariencia: aquí hay muchos tenemos algunos que nos permiten añadir un espacio alrededor como
padding()
, otros la escala comoscale()
, un borde conborder()
, el fondo como ya vimos conbackground()
, transparencia conalpha()
- Listeners: también podemos escuchar ciertos eventos relacionados con la vista, como
onFocusChanged()
,onKeyEvent()
oonSizeChanged()
entre otros.
Aprendiendo a usar algunos modifiers
Obviamente el número de modifiers es tan alto que no podemos verlos todos en un solo artículo, así que vamos a ver aquí algunos de los más importantes, e iremos viendo algunos extra en los siguientes artículos.
¿Cómo vamos a probarlos? Vamos a convertir un Text
en un botón. Aunque el Text
merece su propio vídeo y lo veremos más en profundidad, y existe un Button
que hace este ejercicio innecesario en un proyecto propio, nos va a ayudar a practicar con muchos modifiers.
Lo primero es crearnos un Texto dentro de un Box, y lo vamos a centrar. Esto ya sabemos cómo hacerlo del artículo anterior:
Box(contentAlignment = Alignment.Center) { Text(text = "Hello Button") }
Lo primero que vamos a hacer es ponerle un fondo. Este también te lo sabes ya:
Text( text = "Hello Button", modifier = Modifier .background(Color.Cyan) )
Pero ese texto está ahí un poco aplastado, necesitamos darle un poco de aire, para ello, podemos usar un padding.
Se puede definir tanto un padding completo, como horizontal y vertical, así como individualmente para cada uno de los lados.
En este caso, y solo por probarlo, vamos a especificar uno horizontal y otro vertical de forma independiente:
Text( text = "Hello Button", modifier = Modifier .background(Color.Cyan) .padding(horizontal = 16.dp, vertical = 8.dp) )
¡Esto va cogiendo forma! ¿Te parece si le añadimos un borde? Para los bordes podríamos hacer un vídeo entero, pero vamos a lo simple: con un ancho y un color:
Text( text = "Hello Button", modifier = Modifier .background(Color.Cyan) .padding(horizontal = 16.dp, vertical = 8.dp) .border(width = 2.dp, color = Color.Blue) )
¿Pero qué está pasando aquí? 😱
Aquí llega una lección importante sobre los Modifiers:
El orden de los modifiers es MUY importante.
Se aplican de forma secuencial, y nos dan mucha flexibilidad, pero a la vez tenemos que tener cuidado con el orden que los ponemos. Si queremos tener el borde por fuera, tenemos que aplicarlo antes de aplicar el padding:
modifier = Modifier .background(Color.Cyan) .border(width = 2.dp, color = Color.Blue) .padding(horizontal = 16.dp, vertical = 8.dp)
Gracias a que los modifiers se aplican de forma consecutiva, podemos hacer cosas tan locas como esta:
modifier = Modifier .background(Color.Cyan) .border(width = 2.dp, color = Color.Blue) .padding(horizontal = 16.dp, vertical = 8.dp) .background(Color.LightGray) .border(width = 2.dp, color = Color.Red) .padding(8.dp) )
Es realmente muy potente. Voy a volver atrás y dejarlo solo con el primer borde, porque ahora vamos a aplicarle un evento de click. Es importante ponerlo al principio, para que aplique sobre el resto de modifiers que hemos añadido:
modifier = Modifier .clickable { /* TODO */ } .background(Color.Cyan) .border(width = 2.dp, color = Color.Blue) .padding(horizontal = 16.dp, vertical = 8.dp) )
El efecto de ripple se aplicará automáticamente:
Cómo preparar mis Composables para que acepten un modifier
En artículo anterior te comentaba que al Composable Greeting()
le había añadido un modifier para poder ajustar sus modificadores desde fuera y personalizarlo según se necesitara.
Es una práctica recomendada que, si queremos crear vistas genéricas, permitamos aplicarle modifiers. El proceso es realmente muy sencillo.
Tan solo tenemos que pasarle como argumente un modifier
a la función, y asignarle el objeto Modifier
por defecto. De esta forma, si no queremos cambiar ningún ajuste, se aplicarán los valores por defecto:
@Composable fun Greeting(name: String, modifier: Modifier = Modifier) { ... }cc
Luego ya solo queda asignarle ese modifier al Composable principal de la función:
@Composable fun Greeting(name: String, modifier: Modifier = Modifier) { Text( modifier = modifier, text = "Hello $name!" ) }
Los modifiers se adaptan al Scope en el que se usan
Esto es algo muy loco que se consigue gracias a la buen aprovechamiento que han hecho en Compose de la potencia de Kotlin.
Por ejemplo, si volviéramos al Column
del artículo anterior, veríamos que podemos hacer Alignment.CenterHorizontally
, pero no Alignment.CenterVertically
. Sin embargo, con Rows pasa justo al revés.
La magia se esconde en un pequeño detalle:
¿Ves ese ColumnScope
? Cada layout provee un scope propio que le aporta funciones de extensión extra al objeto Modifier
.
Por ejemplo, ColumnScope
añade un align()
, que usa un Alignment.Horizontal
, por lo que solo puede usar CenterHorizontally
:
¿Y cómo se genera ese scope?
Es relativamente sencillo, gracias al receiver que tiene la lambda que representa el contenido de ese layout.
De esa forma no hay que aprenderse de memoria qué modifiers funcionan con cada layout, el compilador y el autocompletado te lo dicen 😱
Este scope no sirve solo para los modifiers, sino que algunos scopes proveen sus propias funciones extra para hacer otras cosas.
Por ejemplo, para las LazyColumn
s (el equivalente a RecyclerView
, que lo veremos en un próximo artículo), tenemos unas funciones que nos permiten añadir los items o los sticky headers:
Si te fijas, hasta los items de la lista tienen sus propio LazyItemScope
, con sus modifiers extra:
Aplicando lo aprendido a nuestro ejemplo
En este paso no vamos a poder avanzar mucho. Teníamos esto:
Vamos a apañar un poco el título de la celda, centrándolo, añadiendo un padding y un color de fondo. Para ello lo rodearemos con un Box
:
Box( contentAlignment = Alignment.Center, modifier = Modifier .fillMaxWidth() .background(MaterialTheme.colors.secondary) .padding(16.dp) ) { Text("Title 1") }
La forma de acceder a las propiedades del tema es usando el objeto MaterialTheme
. Aquí le estamos poniendo el color secondary
a la celda.
El texto aún se ve un poco pequeño, pero para hablar de textos y tipografías nos vamos a esperar un poco, ya que lo veremos en el siguiente artículo.
Mientras tanto, puedes ver el código haciendo checkout en su commit correspondiente.
Recuerda que puedes acceder a un curso gratuito de Jetpack Compose aquí, donde además tendrás un canal de soporte y muchas sopresas extra. ¡Te veo dentro!

Apúntate ahora a Compose Expert y accede a más de 3h de contenido gratuito, soporte, extras y mucho más totalmente gratis.
0 comentarios