Implementación de Barra de Navegación Inferior en Android Compose

Clase 14 de 19Curso de Android: Modo Offline con Room y Realm

Resumen

La navegación en aplicaciones Android es un componente esencial para crear experiencias de usuario fluidas y coherentes. Implementar una barra de navegación inferior (Bottom Navigation Bar) permite a los usuarios acceder rápidamente a las principales secciones de la aplicación, mejorando significativamente la usabilidad. En este artículo, exploraremos cómo implementar esta funcionalidad utilizando Jetpack Compose, el moderno toolkit de UI para Android.

¿Cómo implementar una barra de navegación inferior en Jetpack Compose?

Para implementar una barra de navegación inferior en nuestra aplicación, necesitamos trabajar con Navigation en Compose. Este enfoque nos permite definir las diferentes pantallas de nuestra aplicación y navegar entre ellas de manera estructurada.

Primero, organizaremos nuestro código creando una estructura adecuada. Dentro del paquete "presentation", crearemos una nueva carpeta llamada "navigation" para mantener separados los componentes relacionados con la navegación.

Definición de las rutas de navegación

El primer paso es crear una clase sellada (sealed class) llamada "Screen" que contendrá la información necesaria para cada destino de navegación:

sealed class Screen(
    val route: String,
    val title: String,
    val icon: ImageVector
) {
    object Create : Screen(
        route = "create",
        title = "Crear",
        icon = Icons.Default.Add
    )
    
    object Home : Screen(
        route = "home",
        title = "Órdenes",
        icon = Icons.Default.Home
    )
    
    object PreOrders : Screen(
        route = "list",
        title = "Pre Órdenes",
        icon = Icons.AutoMirrored.Filled.List
    )
}

En este código, hemos definido tres destinos principales para nuestra aplicación:

  • Create: Para crear nuevas preórdenes
  • Home: La pantalla principal que muestra las órdenes
  • PreOrders: Para listar todas las preórdenes creadas

Cada destino tiene una ruta única que se utilizará para la navegación, un título que se mostrará en la barra inferior y un icono que ayudará a los usuarios a identificar visualmente cada sección.

Implementación del componente de navegación inferior

Una vez definidas nuestras rutas, procedemos a crear el componente de la barra de navegación inferior:

@Composable
fun BottomNavigation(navController: NavController) {
    val items = listOf(
        Screen.Create,
        Screen.Home,
        Screen.PreOrders
    )
    
    NavigationBar {
        val currentDestination = navController.currentBackStackEntryAsState().value?.destination?.route
        
        items.forEach { screen ->
            NavigationBarItem(
                icon = { Icon(screen.icon, contentDescription = screen.title) },
                label = { Text(screen.title) },
                selected = currentDestination == screen.route,
                onClick = {
                    navController.navigate(screen.route) {
                        popUpTo(navController.graph.startDestinationId)
                        launchSingleTop = true
                        restoreState = true
                    }
                }
            )
        }
    }
}

En este componente:

  • Creamos una lista con los destinos de navegación
  • Utilizamos NavigationBar como contenedor principal
  • Obtenemos la ruta actual para resaltar el ítem seleccionado
  • Para cada destino, creamos un NavigationBarItem con su icono, etiqueta y comportamiento de navegación

El orden de los elementos en la lista determina su posición en la barra de navegación. En nuestro caso, "Create" estará a la izquierda, "Home" en el centro y "PreOrders" a la derecha.

Creación de la pantalla principal

Ahora necesitamos un componente principal que integre la barra de navegación con el contenido de cada pantalla:

@Composable
fun MainScreen(navController: NavController) {
    Scaffold(
        bottomBar = {
            BottomNavigation(navController = navController)
        }
    ) { padding ->
        NavHost(
            navController = navController,
            startDestination = Screen.Home.route
        ) {
            // Aquí se agregarán las composables para cada destino
            // Por ahora, solo tenemos implementado el HomeScreen
            composable(Screen.Home.route) {
                HomeScreen(modifier = Modifier.padding(padding))
            }
            // Próximamente se agregarán las pantallas Create y PreOrders
        }
    }
}

En este componente:

  • Utilizamos Scaffold para proporcionar la estructura básica de la pantalla
  • Configuramos la barra inferior con nuestro componente BottomNavigation
  • Utilizamos NavHost para gestionar la navegación entre destinos
  • Definimos Screen.Home.route como destino inicial
  • Aplicamos el padding del Scaffold a nuestros composables para evitar que el contenido quede oculto por la barra inferior

Integración en la actividad principal

Finalmente, integramos nuestro MainScreen en la actividad principal:

@Composable
fun AppContent() {
    val navController = rememberNavController()
    MainScreen(navController = navController)
}

En lugar de mostrar directamente el HomeScreen, ahora utilizamos nuestro MainScreen que incluye la barra de navegación inferior y la lógica de navegación.

¿Cómo funciona la navegación en Jetpack Compose?

La navegación en Jetpack Compose se basa en el concepto de destinos y rutas. Cada pantalla de la aplicación se define como un destino con una ruta única. El NavController es el componente central que gestiona la navegación entre estos destinos.

El flujo de navegación funciona de la siguiente manera:

  • El usuario toca un elemento en la barra de navegación
  • El onClick del NavigationBarItem llama a navController.navigate() con la ruta correspondiente
  • El NavHost detecta el cambio y muestra el composable asociado a esa ruta
  • La barra de navegación actualiza el elemento seleccionado basándose en la ruta actual

Las opciones como launchSingleTop = true y restoreState = true mejoran la experiencia de navegación evitando múltiples instancias de la misma pantalla y preservando el estado cuando el usuario regresa a una pantalla previamente visitada.

La implementación de una barra de navegación inferior en Jetpack Compose proporciona una forma intuitiva para que los usuarios naveguen por las principales secciones de nuestra aplicación. Este patrón de diseño, ampliamente utilizado en aplicaciones modernas, mejora significativamente la usabilidad y la experiencia general del usuario. ¿Has implementado barras de navegación en tus proyectos? Comparte tu experiencia en los comentarios.