Escribir la interfaz con código es genial, pero puede ser que pronto se nos vaya de las manos.
Aunque la forma de escribir el código de Jetpack Compose es muy natural y relativamente directa, sí que es verdad que en pro de la flexibilidad, también hay algunos componentes que ocupan bastante código, y pueden hacer que este crezca indiscriminadamente.
Por ejemplo, solo para escribir una AppBar
con dos acciones, hemos necesitado esto:
TopAppBar( title = { Text(stringResource(id = R.string.app_name)) }, actions = { IconButton(onClick = { /*TODO*/ }) { Icon( imageVector = Icons.Default.Search, contentDescription = null ) } IconButton(onClick = { /*TODO*/ }) { Icon( imageVector = Icons.Default.Share, contentDescription = null ) } } )
Aunque ya hemos extraído algunos componentes, la verdad es que aún queda un poco por hacer.
Así que en este artículo te voy a dar algunos consejos para que el código de Compose no se te vaya de las manos.

¡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.
1. Crea un Composable con la base de tu App
Hay una serie de configuraciones que, si quieres que previews reflejen correctamente, vas a tener que repetir cada vez.
Lo ideal es extraer un Composable
que defina esa configuración, para luego poder reutilizarlo.
En nuestro caso, el código a extraer es:
MyMoviesTheme { // A surface container using the 'background' color from the theme Surface(color = MaterialTheme.colors.background) { ... }
Para ello, nos podemos crear un componente MyMoviesApp
que contenga esa configuración. Lo que nos gustaría es tener algo de este estilo:
MyMoviesApp { ... }
Lo que necesitamos es una función con este nombre, que a su vez acepte un Composable
como parámetro.
Para ello, basta con anotar una lambda que devuelva Unit
(es obligatorio, ya que los Composable
s no devuelven nada, solo emiten UI) con la anotación @Composable
:
@Composable fun MyMoviesApp(content: @Composable () -> Unit) { MyMoviesTheme { // A surface container using the 'background' color from the theme Surface(color = MaterialTheme.colors.background) { content() } } }
Con esta función hacemos un wrapper del tema y la Surface
y emitimos el content
que recibimos como argumento.
Ahora en la Activity
podemos hacer:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MyMoviesApp { ... } } }
Además de usarlo para la App completa, también podemos reaprovecharlo para las previews, de tal forma que la configuración también se aplique a estas:
@Preview @Composable fun MediaListItemPreview() { MyMoviesApp { val mediaItem = MediaItem(1, "Item 1", "", MediaItem.Type.VIDEO) MediaListItem(mediaItem = mediaItem) } }
2. Divide el Composable en otros más pequeños
Haz tantos y tan pequeños como sea necesario para que el código sea más fácil de entender.
Los Composables deberían explicar bien lo que hacen y tener nombres semánticos, en vez de tener un montón de elementos visuales seguidos que nos dificultan la lectura.
Por ejemplo, de aquí:
Scaffold( topBar = { TopAppBar( title = { Text(stringResource(id = R.string.app_name)) }, actions = { IconButton(onClick = { /*TODO*/ }) { Icon( imageVector = Icons.Default.Search, contentDescription = null ) } IconButton(onClick = { /*TODO*/ }) { Icon( imageVector = Icons.Default.Share, contentDescription = null ) } } ) } ) { padding -> MediaList(Modifier.padding(padding)) }
Podríamos extraer perfectamente la TopAppBar
:
Scaffold( topBar = { MainAppBar() } ) { padding -> MediaList(Modifier.padding(padding)) }
Mucho mejor, ¿verdad?
Incluso dentro de la barra, podemos simplificar las acciones:
@Composable fun MainAppBar() { TopAppBar( title = { Text(stringResource(id = R.string.app_name)) }, actions = { AppBarAction( imageVector = Icons.Default.Search, onClick = { /* TODO */ } ) AppBarAction( imageVector = Icons.Default.Share, onClick = { /* TODO */ } ) } ) } @Composable private fun AppBarAction( imageVector: ImageVector, onClick: () -> Unit ) { IconButton(onClick = onClick) { Icon( imageVector = imageVector, contentDescription = null ) } }
Por supuesto, no dejes los Composables todos en el mismo archivo, crea uno por cada componente nuevo.
3. Crea Composables que definan una pantalla
Ahora mismo solo tenemos una pantalla, así que podemos tirarlo todo en el setContent { }
del MainActivity
y olvidarnos.
Pero esto no va a ser así cuando tengamos que pintar más pantallas.
Lo ideal es extraer esto a funciones propias:
@Composable fun MainScreen() { MyMoviesApp { Scaffold( topBar = { MainAppBar() } ) { padding -> MediaList(Modifier.padding(padding)) } } }
Y la Activity
se queda tan sencilla como esto:
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MainScreen() } } }
4. Estructura los paquetes de UI por pantallas
De esa forma el código te quedará mucho más ordenado, y puedes crear tantos ficheros de Composables como te sean necesarios.
Mira cómo he estructurado yo este proyecto:
Dentro del paquete ui
(que ya existe, se creó al crear el proyecto desde cero con Compose) he añadido el paquete screens
, y dentro, un paquete por cada pantalla. Lo he dejado ya preparado para cuando cree la pantalla de detalle.
Además, dentro del paquete main
he creado un archivo para la propia pantalla (MainScreen
), otro para la barra superior (MainAppBar
) y otro para la lista (MediaList
). No es la única forma válida de dividirlo, pero es la que por el momento he creído la adecuada.
Dentro de cada archivo, puedes también tener las previews relacionadas con los componentes de ese archivo.
También he creado un paquete model
, aunque seguramente este evolucionaría en el futuro si la App fuera más grande, pero para este ejemplo sobra.
Finalmente tenemos MyMoviesApp
en el root de ui
, ya que solo va a existir uno de estos por proyecto, y MainActivity
en el root de app
por lo mismo.
Ya hablaremos de navegación en el próximo artículo y te mostraré opciones, pero si sigues lo que te voy a enseñar, solo vas a tener una Activity
.
5. Extrae las dimensiones
Como es tan sencillo, es muy tentador dejar las dimensiones hardcodeadas gracias a la propiedad de extensión dp
.
Pero esto nos va a ser un problema si trabajamos con pantallas de múltiples tamaños (lo que en Android es casi inevitable), ya que lo que en un dispositivo pequeño queda bien, en uno más grande va a ser insuficiente.
Para eso necesitamos un sistema que, en función del tamaño de la pantalla, nos devuelva un valor u otro. Nos podemos crear el nuestro propio basado en CompositionLocal siguiendo la idea de lo que se hace con los temas.
Pero la realidad es que ya tenemos en el propio sistema de Android y que funciona muy bien: los resources.
Además, acceder a ellos desde Compose es muy sencillo, así que vamos a hacerlo. Con estas dimensiones:
<?xml version="1.0" encoding="utf-8"?> <resources> <dimen name="cell_min_width">150dp</dimen> <dimen name="cell_thumb_height">200dp</dimen> <dimen name="cell_play_icon_size">92dp</dimen> <dimen name="padding_xsmall">2dp</dimen> <dimen name="padding_medium">16dp</dimen> </resources>
Podemos eliminar las dimensiones hardcodeadas. Por ejemplo en el LazyVerticalGrid
:
LazyVerticalGrid( cells = GridCells.Adaptive(dimensionResource(R.dimen.cell_min_width)), contentPadding = PaddingValues(dimensionResource(R.dimen.padding_xsmall)), modifier = modifier ) { items(getMedia()) { MediaListItem( mediaItem = it, modifier = Modifier.padding(dimensionResource(R.dimen.padding_xsmall)) ) } }
Un código más ordenado es más reutilizable
Al final, una de las grandes ventajas de poder escribir la UI en código Kotlin es que podemos utilizar toda la potencia del lenguaje para ayudarnos con ello.
Con los XMLs estábamos mucho más limitados.
Es por eso que nos podemos crear componentes genéricos que nos permitan reutilizar al máximo nuestro código.
Ya lo tenemos todo listo para poder empezar a navegar entre pantallas, así que si no quieres perderte nada, apúntate a mi formación Compose Expert, donde tendrás acceso gratuito a un primer módulo donde aprenderás todas las bases del trabajo con Jetpack Compose, y podrás ver el siguiente vídeo sobre navigation.

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