Agregar Jetpack Compose A Proyecto Android Studio

En este tutorial aprenderás a configurar el ambiente de desarrollo en Android Studio para agregar Jetpack Compose en proyectos nuevos o existentes.

Como requisito necesitarás la versión Artic Fox de Android Studio con el fin de usar nuevas herramientas y plantillas de actividades asociadas a Compose al crear nuevos proyectos.

Nota: Esta es la primera parte de la guía de Compose (todo) de Develou. En ella encontrarás la definición y propósito de esta API y los demás tutoriales en forma secuencial para aprender a usarla.

Ejemplo Al Agregar Jetpack Compose

Aunque el objetivo de este contenido es familiarizarte con la manera de añadir la dependencia de Compose a tus proyectos, también crearemos un ejemplo simple para el diseño de una pantalla de Login:

Ejemplo de pantalla de Login con Jetpack Compose

La idea es abordar de forma breve el uso de funciones componibles (composable functions), el diseño de layouts y el uso de modificadores para ordenar los elementos visuales. No obstante, en los siguientes tutoriales veremos en detalle estos y otros temas más.

Puedes descargar el ejemplo desde el siguiente enlace:

Con esto en mente, configuremos un nuevo proyecto con Compose.


1. Crear Nuevo Proyecto En Android Studio

Abre Android Studio y selecciona la opción New Project. Si te encuentras en un proyecto actualmente, entonces ve a File>New Project:

Android Studio Artic Fox New Project

Esta acción te desplegará una ventana con la lista de plantillas para dispositivos móviles que la versión del IDE provee. En nuestro caso seleccionaremos Empty Activity. Más adelante veremos que al usar a Compose Empty Activity se simplificará la incorporación de Jetpack Compose al proyecto:

Plantilla Empty Activity en Android Studio Artic Fox

Esta plantilla creará una actividad llamada MainActivity, la cual renombraremos a LoginActivity:

class LoginActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

2. Configurar Android Gradle Plugin

En primer lugar, abre el archivo de build.gradle del proyecto y especifica una versión igual o mayor a 7.0 para el plugin de Android:

buildscript {
    //...
    dependencies {
        classpath "com.android.tools.build:gradle:7.0.0"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21"
    }
}

Android Studio Artic Fox usará este valor al crear nuevos proyectos, pero si ya tenías un proyecto existente, entonces actualízalo.


3. Establecer Versión De Compose

Es posible extraer la versión del paquete de Compose con un bloque ext() desde build.gradle del módulo, con el fin de reutilizar este valor en todos los módulos que usen la API:

buildscript {
    ext {
        compose_version = '1.0.1'
    }
    //...
}

4. Habilitar Jetpack Compose

Abre el archivo build.gradle del módulo que haga uso de Jetpack Compose para establecer su versión mínima del SDK un nivel igual o mayor a 21.

Luego haz explícita la habilitación de la API desde el bloque buildFeatures. Y luego asigna la versión del compilador del plugin Kotlin en composeOptions:

android {

    defaultConfig {        
        minSdk 21
        //...
    }

    //...
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    
    kotlinOptions {
        jvmTarget = '1.8'
    }

    buildFeatures {
        compose true
    }

    composeOptions {
        kotlinCompilerExtensionVersion compose_version
    }
}

5. Añadir Dependencias De Compose

Finalmente, para comenzar a definir tu UI programáticamente añade las dependencias hacia los paquetes que requieras usar en tu proyecto:

dependencies {
    
    // Jetpack Compose
    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.material:material:$compose_version"
    implementation "androidx.compose.ui:ui-tooling:$compose_version"
    implementation 'androidx.activity:activity-compose:1.3.0'

    //...
}

Los anteriores grupos proveen las siguientes funcionalidades:

  • androidx.compose.ui:ui: Contiene a todos los componentes básicos para construir elementos de la interfaz gráfica
  • androidx.compose.material:material: Te permite construir UI con los componentes del material design
  • androidx.compose.ui:ui-tooling: Provee la capacidad para previsualizar las interfaces construidas con Compose
  • androidx.activity:activity-compose: Provee una actividad con la capacidad de acceder a las funcionalidades de Compose

En los próximos tutoriales veremos la inclusión de otros artefactos para el uso de características complementarias de Compose.


6. Definir ComponentActivity

Para que una actividad pueda crear implementaciones basados en los elementos de Compose, extiendela de la clase ComponentActivity. En nuestro ejemplo LoginActivity.kt tendría esta definición base:

class LoginActivity : ComponentActivity() {
    
}


7. Declarar Layout De Actividad

Funciones Componibles (Composables)

A diferencia del sistema de views que usámos con XML, Compose crea jerarquías de elementos visuales a partir de bloques definidos por funciones componibles.

Estas están marcadas por la anotación @Composable y son convertidas por el compilador de Compose en jerarquías de nodos visuales que son proyectados en la interfaz:

@Composable
fun FuncionComponible(){
    // Jerarquía con otras funciones Composables
}

Existen funciones componibles prefabricadas para representar elementos como texto, gráficos, listas, etc. Pero también es posible definir funciones personalizadas para componer tu jerarquía como veremos a continuación.

La Función setContent()

Para definir el layout de una actividad cambia el método setContentView() por la llamada de la función setContent() desde onCreate(). Por ejemplo:

class LoginActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Text("Login")
        }
    }
}

La función de extensión ComponentActivity.setContent() recibe como parámetro una función componible que declara el contenido de la UI.

En este caso dicha función será Text(). Ella es la encargada de representar texto en pantalla. Al pasarla como el contenido de setContent(), se mostrará en pantalla el String «Login».

Si ejecutas la App con este código verás el siguiente resultado:


8. Ordenar Elementos En Layout

Para el diseño de la pantalla de inicio de sesión además del texto que ya tenemos, necesitaremos 2 campos de texto para usuario y contraseña; y el botón que inicia la autenticación.

En consecuencia, añade al bloque de contenido de setContent() las funciones TextField y Button:

setContent {
    Text("Login", fontSize = 30.sp)
    
    TextField(
        value = "",
        onValueChange = {},
        label = { Text("Usuario") })

    TextField(
        value = "",
        onValueChange = {},
        label = { Text("Constraseña") })
    
    Button(onClick = { }) {
        Text("Iniciar Sesión")
    }
}

En próximos tutoriales veremos en detalle el uso de estos elementos. Por el momento ten presente que para TextField, value es el texto actual escrito, onValueChange la acción a ejecutar cuando se tipea texto y label es la etiqueta flotante que sugiere el contenido del campo de texto.

En el caso del Button, requiere como parámetro obligatorio a onClick para representar la acción a ejecutar cuando se hace click sobre el elemento. Además recibe como último parámetro una función componible para definir su contenido. En este caso, será el texto asociado con su acción a través de Text().

Adicionalmente, hemos pasado el parámetro fontSize de Text(). Este es el tamaño del texto que se proyectará. Recibe como una instancia del tipo TextUnit. Y si quieres usar un valor en SP, entonces accede a la propiedad de extensión Int.sp.

Previsualizar Composable Functions

Al igual que con el editor de layouts XML, Android Studio también te provee tres pestañas de presentación para las funciones componibles que usen la anotación @Preview.

Previsualización de funciones componibles

Para previsualizar nuestro layout crearemos una composable function llamada LoginView en LoginActivity y la marcaremos con @Preview:

@Composable
@Preview
fun LoginView() {
    Text("Login", fontSize = 30.sp)

    TextField(
        value = "",
        onValueChange = {},
        label = { Text("Usuario") })

    TextField(
        value = "",
        onValueChange = {},
        label = { Text("Constraseña") })

    Button(onClick = { }) {
        Text("Iniciar Sesión")
    }
}

Si seleccionas la pestaña Split o Design verás como resultó la organización de nuestros elementos dentro de LoginView():

Usar Columnas

La vista previa anterior evidencia que nuestra UI ha sobrepuesto los elementos uno tras otro sin ninguna consideración.

Y justo aquí es donde entran las columnas de Compose. Estas son representadas por la función Column(), la cual recibe un conjunto de elementos que serán organizados verticalmente en el layout.

Así que invoquemos una columna que cubra nuestros elementos del login:

@Composable
@Preview
fun LoginView() {

    Column {
        Text("Login", fontSize = 30.sp)
        
        TextField(
            value = "",
            onValueChange = {},
            label = { Text("Usuario") })
        
        TextField(
            value = "",
            onValueChange = {},
            label = { Text("Constraseña") })
        
        Button( onClick = { }) {
            Text("Iniciar Sesión")
        }
    }
}

Si revisas la previsualización esta vez tendremos:

Ejemplo de Column en Jetpack Compose

Modificadores

Los modificadores son funciones que extienden las características de los elementos generados por una función componible. Con ellos podremos cambiar propiedades visuales, añadir información de accessibilidad, detectar gestos, etc.

Estos viven el la interfaz Modifier, donde existen múltiples métodos que pueden ser encadenados para combinar efectos.

Nuestro layout actual aunque está ordenado, no tiene espacios ni alineaciones que mejoren la pulcritud de la UI. Para ordenarlo usaremos los siguientes modificadores:

  • fillMaxWidth() y fillMaxHeight(): Expande el contenido horizontal o verticalmente
  • fillMaxSize(): Expande ambas dimensiones de contenido
  • padding(): Añade un espacio en Dps a todos los bordes del elemento
  • align(): Alinea el elemento con respecto a su contenedor

Teniendo esto en cuenta, mejores nuestro layout de la siguiente forma:

  • Extender columna en ambas direcciones
  • Añadir 16 dps de padding para la columa
  • Extender campos de texto horizontalmente
  • Centrar horizontalmente al botón de login

La implementación con todos estos modificadores de nuestra función componible LoginView() se vería así:

@Composable
@Preview
fun LoginView() {
    val spaceKeyline = 16.dp

    Column(
        Modifier
            .fillMaxSize()
            .padding(spaceKeyline)
    ) {
        Text("Login", fontSize = 30.sp)

        Spacer(modifier = Modifier.size(spaceKeyline))

        TextField(
            value = "",
            modifier = Modifier.fillMaxWidth(),
            onValueChange = {},
            label = { Text("Usuario") })

        Spacer(modifier = Modifier.size(spaceKeyline))

        TextField(
            value = "",
            modifier = Modifier.fillMaxWidth(),
            onValueChange = {},
            label = { Text("Constraseña") })

        Spacer(modifier = Modifier.size(spaceKeyline))
        
        Button(modifier = Modifier.align(Alignment.CenterHorizontally), onClick = { }) {
            Text("Iniciar Sesión")
        }
    }
}

Como ves, introducimos un elemento llamado Spacer que representa un espacio vacío en la jerarquía. De esta forma tendremos el siguiente resultado:

Previsualización de función componible para Login con Compose

Nota: Lee el tutorial Modificadores en Jetpack Compose (todo) para profundizar en este tema.


9. Procesar Click En Botón

Recordemos que la función Button() recibe un parámetro llamado onClick. Este es del tipo función ()->Unit para que asignes una lambda con las acciones a realizar.

Nuestro objetivo es mostrar un Toast que informe la intención de loguear al usuario cuando el botón sea presionado. Esto podemos lograrlo pasando una lambda como parámetro en LoginView() y luego asignarla a onClick:

@Composable
@Preview
fun LoginView(onClick: () -> Unit = {}) {
    //...

    Button(
        modifier = Modifier.align(Alignment.CenterHorizontally),
        onClick = onClick
    ) {
        Text("Iniciar Sesión")
    }
}

Aquellas funciones componibles cuyos parámetros no tengan valores por defecto, no podrán ser renderizadas en la previsualización. Por esta razón hemos asignado un bloque vacío a onClick.

Ahora bien, para mostrar el Toast crearemos un método llamado login() y pasaremos su referencia desde setContent():

class LoginActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            LoginView(::login)
        }
    }

    private fun login() {
        Toast.makeText(
            this,
            "Iniciar sesión",
            Toast.LENGTH_SHORT
        ).show()
    }
}

Al ejecutar la aplicación podrás ver lo siguiente:

Ejemplo de onClick en Button Jetpack Compose

Plantilla Compose Empty Activity

Como había dicho al inicio, la nueva plantilla Compose Empty Activity añade automáticamente toda la configuración de Jetpack Compose en tus archivos Gradle.

Plantilla Empty Compose Activity en Android Studio

Por lo que la actividad creada con ella hereda directamente de ComponentActivity y trae definido un layout por defecto con un texto de «Hello World» producido por la función componible Greeting().

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeTemplateTheme {
                // A surface container using the 'background' color from the theme
                Surface(color = MaterialTheme.colors.background) {
                    Greeting("Android")
                }
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    ComposeTemplateTheme {
        Greeting("Android")
    }
}

Adicionalmente crea varios elementos para establecer el tema de la actividad a partir de una función componible en el paquete ui.theme:

En el tutorial Aplicación de temas con Jetpack Compose (todo) veremos el uso de estos elementos añadidos por la plantilla.

Únete Al Discord De Develou

Si tienes problemas con el código de este tutorial, preguntas, recomendaciones o solo deseas discutir sobre desarrollo Android conmigo y otros desarrolladores, únete a la comunidad de Discord de Develou y siéntete libre de participar como gustes. ¡Te espero!