14 - Componente TopAppBar (integración con otras componentes de Material Design)

La barra superior de la aplicación muestra información y acciones relacionadas con la pantalla actual. Se integra gracias a la componente Scaffold.

Vamos a continuar el problema del concepto anterior agregando una barra superior.

Problema

Confeccionar una aplicación con tres pantalla que muestren datos generales de 3 países. Disponer una barra de navegación inferior para poder seleccionar un país específico. Además agregar una barra superior con un título y dos íconos que permitan agrandar y reducir la fuente de pantalla.

Crear un proyecto llamado Compose16, la interfaz visual de la misma debe ser similar a:

TopAppBar Jetpack Compose

Agregamos la dependencia de Navigation Compose en el archivo Gradle:

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"
}

El código fuente que tenemos que implementar es:

package com.tutorialesprogramacionya.compose16


import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.sp
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()
    var tamanoFuente by remember { mutableStateOf(12)}
    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text(text = "Países Latinoamericanos")
                },
                navigationIcon = {
                    IconButton(onClick = {  }) {
                        Icon(Icons.Filled.Menu, contentDescription = null)
                    }
                },
                actions = {
                    IconButton(onClick = { tamanoFuente++ }) {
                        Icon(Icons.Default.Add, contentDescription = null)
                    }
                    IconButton(onClick = { tamanoFuente=12 }) {
                        Icon(Icons.Filled.Home, contentDescription = null)
                    }
                }
            )
        },
        bottomBar = {
            BottomNavigation() {
                var argentina by remember { mutableStateOf(true) }
                var colombia by remember { mutableStateOf(false) }
                var brasil by remember { mutableStateOf(false) }
                BottomNavigationItem(selected = argentina,
                    onClick = {
                        argentina = true;colombia = false;brasil = false;
                        navController.navigate("pantallaargentina")
                    },
                    icon = { Icon(Icons.Default.Info, contentDescription = null) },
                    label = {
                        Text(text = "Argentina")
                    }
                )
                BottomNavigationItem(selected = brasil,
                    onClick = {
                        brasil = true;argentina = false;colombia = false;
                        navController.navigate("pantallabrasil")
                    },
                    icon = { Icon(Icons.Default.Info, contentDescription = null) },
                    label = {
                        Text(text = "Brasil")
                    }
                )
                BottomNavigationItem(selected = colombia,
                    onClick = {
                        colombia = true;argentina = false;brasil = false;
                        navController.navigate("pantallacolombia")
                    },
                    icon = { Icon(Icons.Default.Info, contentDescription = null) },
                    label = {
                        Text(text = "Colombia")
                    }
                )
            }
        }
    ) {
        NavHost(navController = navController, startDestination = "pantallaargentina") {
            composable("pantallaargentina") {
                PantallaArgentina(tamanoFuente)
            }
            composable("pantallabrasil") {
                PantallaBrasil(tamanoFuente)
            }
            composable("pantallacolombia") {
                PantallaColombia(tamanoFuente)
            }
        }
    }
}

@Composable
fun PantallaArgentina(tamanoFuente:Int) {
    val scroll = rememberScrollState(0)
    Text(
        fontSize=tamanoFuente.sp,
        text = "Argentina, oficialmente República Argentina",
        modifier = Modifier.verticalScroll(scroll)
    )
}

@Composable
fun PantallaBrasil(tamanoFuente:Int) {
    val scroll = rememberScrollState(0)
    Text(
        fontSize=tamanoFuente.sp,
        text = "Brasil, oficialmente República Federativa de Brasil",
        modifier = Modifier.verticalScroll(scroll)
    )
}

@Composable
fun PantallaColombia(tamanoFuente:Int) {
    val scroll = rememberScrollState(0)
    Text(
        fontSize=tamanoFuente.sp,
        text = "Colombia, oficialmente República de Colombia",
        modifier = Modifier.verticalScroll(scroll)
    )
}

De manera similar al problema anterior debemos llamar a la función composable Scaffold y pasar un segundo parámetro llamado 'topBar', al mismo le pasamos una función lambda que llama a la función TopAppBar.

A la función TopAppBar le hemos pasado el parámetro title que requiere una función lambda donde mediante la función Text indicamos el texto a mostrar en la barra superior. El segundo parámetro que hemos configurado es navigationIcon el cual muestra el menú de hamburguesa de la derecha, en nuetro problema no le hemos asociado ningún evento.

El parámetro action requiere una función donde llamamos tantas veces a la función IconButton como íconos queremos disponer en la barra superior.

A los íconos hemos asociado la modificación de la variable de estado 'tamanoFuente' que posteriormente y en forma automática cambiará el tamaño de la fuente:

@Composable
fun PantallaNavegacion() {
    val navController = rememberNavController()
    var tamanoFuente by remember { mutableStateOf(12)}
    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text(text = "Países Latinoamericanos")
                },
                navigationIcon = {
                    IconButton(onClick = {  }) {
                        Icon(Icons.Filled.Menu, contentDescription = null)
                    }
                },
                actions = {
                    IconButton(onClick = { tamanoFuente++ }) {
                        Icon(Icons.Default.Add, contentDescription = null)
                    }
                    IconButton(onClick = { tamanoFuente=12 }) {
                        Icon(Icons.Filled.Home, contentDescription = null)
                    }
                }
            )
        },
        bottomBar = {
            BottomNavigation() {
                var argentina by remember { mutableStateOf(true) }
                var colombia by remember { mutableStateOf(false) }
                var brasil by remember { mutableStateOf(false) }
                BottomNavigationItem(selected = argentina,
                    onClick = {
                        argentina = true;colombia = false;brasil = false;
                        navController.navigate("pantallaargentina")
                    },
                    icon = { Icon(Icons.Default.Info, contentDescription = null) },
                    label = {
                        Text(text = "Argentina")
                    }
                )
                BottomNavigationItem(selected = brasil,
                    onClick = {
                        brasil = true;argentina = false;colombia = false;
                        navController.navigate("pantallabrasil")
                    },
                    icon = { Icon(Icons.Default.Info, contentDescription = null) },
                    label = {
                        Text(text = "Brasil")
                    }
                )
                BottomNavigationItem(selected = colombia,
                    onClick = {
                        colombia = true;argentina = false;brasil = false;
                        navController.navigate("pantallacolombia")
                    },
                    icon = { Icon(Icons.Default.Info, contentDescription = null) },
                    label = {
                        Text(text = "Colombia")
                    }
                )
            }
        }
    ) {
        NavHost(navController = navController, startDestination = "pantallaargentina") {
            composable("pantallaargentina") {
                PantallaArgentina(tamanoFuente)
            }
            composable("pantallabrasil") {
                PantallaBrasil(tamanoFuente)
            }
            composable("pantallacolombia") {
                PantallaColombia(tamanoFuente)
            }
        }
    }
}

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