12 - Componente Navigation (funciones rememberNavController y NavHost)

Esta componente nos permite administrar el desplazamiento entre diferentes partes de nuestra aplicación, por ejemplo distintas pantallas.

Una ventaja de utilizar la componente de navegación es facilitar la implementación de flujos de nuestra aplicación similares a otras aplicaciones que utilizan esta misma componente.

Veremos con un ejemplo el empleo de la componente de navegación.

Problema

Disponer tres botones con los textos "Tabla del 2", "Tabla del 5" y "Tabla del 10". Según el botón que se presione mostrar por pantalla la tabla de multiplicar respectiva.

Crearemos un proyecto llamado "Compose14".

Lo primero que tenemos que hacer es modificar el archivo "build.gradle" para indicar a nuestro proyecto que utilizaremos la componente de navegación (es decir que por defecto Android Studio no agrega esta dependencia):

dependencies {

    implementation 'androidx.core:core-ktx:1.6.0'
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.material:material:$compose_version"
    implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
    implementation 'androidx.activity:activity-compose:1.3.1'
    implementation("androidx.navigation:navigation-compose:2.4.0-alpha06")
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
    debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
}

Siempre conviene instalar la última versión estable, la cual podemos consultar en la página: aquí

Las distintas pantalla que mostrará nuestra aplicación según el botón presionado serán:

Componente Navigation Jetpack Compose

Componente Navigation Jetpack Compose

Componente Navigation Jetpack Compose

El código fuente de la aplicación es:

package com.tutorialesprogramacionya.compose14

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            PantallaNavegacion()
        }
    }
}

@Composable
fun PantallaNavegacion() {
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = "pantalla1") {
        composable("pantalla1") {
            Pantalla1(navController)
        }
        composable("pantalla2") {
            Pantalla2(navController)
        }
        composable("pantalla3") {
            Pantalla3(navController)
        }
    }
}

@Composable
fun Pantalla1(navController: NavController) {
    Column() {
        BarraBotones(navController = navController)
        Text(text = "Tabla del 2")
        for (x in 1..10) {
            Text("2 * $x = ${x * 2}")
        }
    }
}

@Composable
fun Pantalla2(navController: NavController) {
    Column() {
        BarraBotones(navController = navController)
        Text(text = "Tabla del 5")
        for (x in 1..10) {
            Text("5 * $x = ${x * 5}")
        }
    }
}

@Composable
fun Pantalla3(navController: NavController) {
    Column() {
        BarraBotones(navController = navController)
        Text(text = "Tabla del 10")
        for (x in 1..10) {
            Text("5 * $x = ${x * 10}")
        }
    }
}

@Composable
fun BarraBotones(navController: NavController) {
    Row(horizontalArrangement = Arrangement.SpaceAround,modifier=Modifier.fillMaxWidth().padding(10.dp)) {
        Button(onClick = {
            navController.navigate("pantalla1")
        }) {
            Text("Tabla 2")
        }
        Button(onClick = {
            navController.navigate("pantalla2")
        }) {
            Text("Tabla 5")
        }
        Button(onClick = {
            navController.navigate("pantalla3")
        }) {
            Text("Tabla 10")
        }
    }
}

Desde el Activity llamamos a la función composable 'PantallaNavegacion', donde creamos la componente de navegación llamando a la función 'rememberNavController', seguidamente llamamos a la función 'NavHost' indicando en el parámetro navController la variable creada previamente y el parámetro startDestination con la pantalla por defecto a mostrar.
Luego llamamos a la función composable indicando una ruta diferente para cada pantalla y registrando la función lambda con la función composable a llamar según la ruta activa:

@Composable
fun PantallaNavegacion() {
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = "pantalla1") {
        composable("pantalla1") {
            Pantalla1(navController)
        }
        composable("pantalla2") {
            Pantalla2(navController)
        }
        composable("pantalla3") {
            Pantalla3(navController)
        }
    }
}

Cada pantalla son representadas por una función composable que muestra la tabla de multiplicar respectiva:

@Composable
fun Pantalla1(navController: NavController) {
    Column() {
        BarraBotones(navController = navController)
        Text(text = "Tabla del 2")
        for (x in 1..10) {
            Text("2 * $x = ${x * 2}")
        }
    }
}

Como cada pantalla tiene en común la barra de botones, hemos agrupado dichos botones en la función composable 'BarraBotones':

@Composable
fun BarraBotones(navController: NavController) {
    Row(horizontalArrangement = Arrangement.SpaceAround,modifier=Modifier.fillMaxWidth().padding(10.dp)) {
        Button(onClick = {
            navController.navigate("pantalla1")
        }) {
            Text("Tabla 2")
        }
        Button(onClick = {
            navController.navigate("pantalla2")
        }) {
            Text("Tabla 5")
        }
        Button(onClick = {
            navController.navigate("pantalla3")
        }) {
            Text("Tabla 10")
        }
    }
}

Para cargar una pantalla distinta en la función BarraBotones cuando se presiona un botón se llama al método 'navigate' del controlador de navegación.

Este proyecto lo puede descargar en un zip desde este enlace: Compose14.zip