Cómo implementar un menú lateral (Drawer Navigator) en React Native
En este artículo, te explico paso a paso cómo agregar un menú lateral (Drawer Navigator) a tu aplicación utilizando @react-navigation/drawer
y otras dependencias necesarias. También detallo por qué es importante incluir <Drawer.Screen name="StackNavigator" component={StackNavigator} />
en el menú para que la navegación funcione correctamente.
Pasos para configurar el Drawer Navigator
1. Instalar la dependencia de Drawer Navigator
El paquete @react-navigation/drawer
es necesario para implementar un menú lateral en tu aplicación.
Ejecuta el siguiente comando para instalarlo:
npm install @react-navigation/drawer
2. Instalar las dependencias requeridas
El Drawer Navigator depende de otras bibliotecas como react-native-gesture-handler
y react-native-reanimated
para manejar gestos y animaciones fluidas. Instálalas con:
npm install react-native-gesture-handler react-native-reanimated
3. Configurar Babel para usar Reanimated
Reanimated requiere un plugin específico en tu archivo babel.config.js
para funcionar correctamente.
Edita tu archivo babel.config.js
y asegúrate de incluir el siguiente código:
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: [
'react-native-reanimated/plugin', // Este plugin debe estar al final de la lista
],
};
Una vez modificado, reinicia el servidor de tu aplicación con:
npm start --reset-cache
Nota: Puedes consultar más información sobre la configuración en la documentación oficial de Reanimated.
4. Crear el componente del Drawer Navigator
Vamos a configurar el Drawer Navigator en un nuevo archivo. Este componente será responsable de definir el menú lateral de nuestra aplicación.
- Ve al directorio
presentation/routes
. - Crea un nuevo archivo llamado
SideMenuNavigator.tsx
. - Añade el siguiente código:
import React from 'react';
import { createDrawerNavigator } from '@react-navigation/drawer';
import { ProfileScreen } from '../screens/profile/ProfileScreen';
import { StackNavigator } from './StackNavigator';
const Drawer = createDrawerNavigator();
export const SideMenuNavigator = () => {
return (
<Drawer.Navigator>
<Drawer.Screen name="StackNavigator" component={StackNavigator} />
<Drawer.Screen name="Profile" component={ProfileScreen} />
</Drawer.Navigator>
);
};
Por qué es necesario incluir <Drawer.Screen name="StackNavigator" component={StackNavigator} />
El Drawer Navigator actúa como un contenedor para las pantallas que se mostrarán en el menú lateral. Cada <Drawer.Screen />
define una ruta que puede ser seleccionada desde el menú.
-
Componente principal de la navegación: En este caso,
StackNavigator
es probablemente el componente que maneja las rutas principales de tu aplicación mediante un Stack Navigator. Al incluirlo en el Drawer, permites que los usuarios puedan acceder a las pantallas principales desde el menú lateral. -
Acceso a múltiples pantallas: Si omites el
<Drawer.Screen />
correspondiente alStackNavigator
, las rutas definidas dentro del Stack Navigator no estarán accesibles a través del menú lateral. Esto significa que no podrás navegar hacia otras pantallas de tu aplicación. -
Navegación jerárquica: El diseño habitual en aplicaciones con menús laterales es tener un Stack Navigator como parte de una de las opciones del Drawer. Esto permite que el Drawer maneje la navegación global, mientras que el Stack gestiona la navegación interna de pantallas específicas.
En resumen, incluir <Drawer.Screen name="StackNavigator" component={StackNavigator} />
asegura que las pantallas definidas dentro del Stack Navigator estén accesibles desde el Drawer Navigator, proporcionando una navegación fluida y jerárquica en la aplicación.
Personalizando el Drawer Navigator en React Native
En esta guía, exploraremos cómo personalizar un Drawer Navigator
en React Native para crear un menú lateral más interactivo y atractivo. A través de ejemplos detallados, aprenderás cómo modificar el comportamiento del menú, quitar elementos predeterminados y diseñar un sidebar completamente personalizado.
Agregar un botón de menú en la pantalla Home
Código base:
import React, { useEffect } from 'react';
import { PrimaryButton } from '../../components/shared/PrimaryButton';
import { Pressable, View } from 'react-native';
import { DrawerActions, NavigationProp, useNavigation } from '@react-navigation/native';
import { RootStackParams } from '../../routes/StackNavigator';
import { Text } from 'react-native-paper';
export const HomeScreen = () => {
const navigation = useNavigation<NavigationProp<RootStackParams>>();
useEffect(() => {
navigation.setOptions({
headerLeft: () => (
<Pressable
onPress={() => navigation.dispatch(DrawerActions.toggleDrawer())}
>
<Text>menu</Text>
</Pressable>
),
});
}, [navigation]);
return (
<View>
<PrimaryButton
onPress={() => navigation.navigate('Products' as never)}
text="Productos"
/>
<PrimaryButton
onPress={() => navigation.navigate('Settings' as never)}
text="Settings"
/>
</View>
);
};
Explicación del código:
-
Uso de
useNavigation
:- Este hook nos permite acceder al objeto
navigation
, que es esencial para manejar la navegación en React Navigation. - En este caso, lo tipamos con
NavigationProp<RootStackParams>
para garantizar que las rutas sean correctas.
- Este hook nos permite acceder al objeto
-
navigation.setOptions()
:- Dentro del
useEffect
, usamossetOptions
para personalizar el encabezado (header
) de la pantallaHome
. - Añadimos un botón en el lado izquierdo (
headerLeft
) que, al presionarse, abre o cierra el menú lateral usandoDrawerActions.toggleDrawer()
.
- Dentro del
-
Botón del menú lateral:
- Se define un componente
Pressable
que muestra el texto menu. Al presionarlo, se dispara la acción de alternar el Drawer Navigator.
- Se define un componente
-
Botones de navegación:
- Utilizamos botones personalizados (
PrimaryButton
) para navegar a otras pantallas, como “Productos” y “Settings”.
- Utilizamos botones personalizados (
Resultado: Ahora, al presionar el botón “menu”, se abrirá el
SideMenuNavigator
.
Quitar el encabezado automático del Drawer Navigator
Cuando usamos el Drawer.Navigator
, se genera automáticamente un encabezado predeterminado. Para eliminarlo, podemos configurar headerShown: false
en las opciones del Drawer.
Código modificado:
import React from 'react';
import { createDrawerNavigator } from '@react-navigation/drawer';
import { ProfileScreen } from '../screens/profile/ProfileScreen';
import { StackNavigator } from './StackNavigator';
const Drawer = createDrawerNavigator();
export const SideMenuNavigator = () => {
return (
<Drawer.Navigator
screenOptions={{
headerShown: false, // Oculta el encabezado automático
}}
>
<Drawer.Screen name="StackNavigator" component={StackNavigator} />
<Drawer.Screen name="Profile" component={ProfileScreen} />
</Drawer.Navigator>
);
};
Explicación:
headerShown: false
:- Oculta el encabezado predeterminado que React Navigation crea automáticamente.
- Esto es útil cuando deseas usar encabezados personalizados o no necesitas encabezado en absoluto.
Diseñar un Sidebar totalmente personalizado
Para modificar el diseño del menú lateral, podemos usar la propiedad drawerContent
, que nos permite definir un componente personalizado para el contenido del Drawer.
Código completo del Sidebar personalizado:
import React from 'react';
import { DrawerContentComponentProps, DrawerContentScrollView, DrawerItemList, createDrawerNavigator } from '@react-navigation/drawer';
import { ProfileScreen } from '../screens/profile/ProfileScreen';
import { StackNavigator } from './StackNavigator';
import { View, useWindowDimensions } from 'react-native';
const Drawer = createDrawerNavigator();
const CustomDrawerContent = (props: DrawerContentComponentProps) => {
return (
<DrawerContentScrollView>
<View style={{
height: 200,
backgroundColor: '#000',
margin: 30,
borderRadius: 50,
}} />
<DrawerItemList {...props} />
</DrawerContentScrollView>
);
};
export const SideMenuNavigator = () => {
const dimensions = useWindowDimensions();
return (
<Drawer.Navigator
drawerContent={props => <CustomDrawerContent {...props} />}
screenOptions={{
headerShown: false,
drawerType: dimensions.width >= 758 ? 'permanent' : 'slide', // Cambia el tipo de menú según el tamaño de la pantalla
drawerActiveBackgroundColor: 'pink', // Color de fondo para el item seleccionado
drawerActiveTintColor: 'black', // Color del texto en el item seleccionado
drawerInactiveTintColor: 'red', // Color del texto en los items no seleccionados
drawerItemStyle: {
borderRadius: 100, // Bordes redondeados en los items
paddingHorizontal: 20, // Espaciado interno en los items
},
}}
>
<Drawer.Screen name="StackNavigator" component={StackNavigator} />
<Drawer.Screen name="Profile" component={ProfileScreen} />
</Drawer.Navigator>
);
};
Explicación del código:
-
Componente
CustomDrawerContent
:- Este componente define el diseño del menú lateral.
- Incluye un encabezado visual (una vista de color negro con bordes redondeados) y los elementos del Drawer (
DrawerItemList
), que son las rutas configuradas.
-
Propiedad
drawerContent
:- Permite usar un componente personalizado como contenido del menú lateral.
-
Propiedad
screenOptions
:drawerType
:- Cambia el comportamiento del Drawer dependiendo del ancho de la pantalla. Por ejemplo, en pantallas grandes, se fija como “permanent” (siempre visible), mientras que en pantallas pequeñas se usa “slide”.
drawerActiveBackgroundColor
ydrawerActiveTintColor
:- Personalizan el color de fondo y texto del elemento seleccionado.
drawerInactiveTintColor
:- Define el color del texto de los elementos no seleccionados.
drawerItemStyle
:- Aplica estilos personalizados a los elementos del menú, como bordes redondeados y espaciado.
Conclusión
Personalizar un Drawer Navigator
en React Native es una excelente forma de mejorar la experiencia del usuario y darle un toque único a tu aplicación. Con estas técnicas, puedes:
- Agregar botones interactivos al encabezado.
- Eliminar elementos predeterminados que no necesitas.
- Diseñar un menú lateral completamente personalizado, adaptado a tu estilo.
Cómo usar useSafeAreaInsets
en React Native para respetar las áreas seguras
Cuando desarrollamos aplicaciones en React Native, uno de los desafíos comunes es asegurarnos de que nuestra interfaz se adapte correctamente a dispositivos modernos con notches, barras de navegación, y otras características físicas de la pantalla. Aquí es donde entra en juego useSafeAreaInsets
, un hook proporcionado por la librería react-native-safe-area-context
, que nos ayuda a manejar estos espacios de manera efectiva.
¿Qué es useSafeAreaInsets
?
El hook useSafeAreaInsets
permite acceder a los márgenes seguros (safe area insets) de un dispositivo. Este hook es especialmente útil para evitar que el contenido de la UI se superponga con áreas restringidas del dispositivo, como:
- El notch (la pestaña superior en algunos dispositivos iOS).
- La barra de estado (en iOS y Android).
- La barra de navegación o gestos (en la parte inferior).
El hook devuelve un objeto con las siguientes propiedades:
top
: El espacio seguro desde la parte superior.bottom
: El espacio seguro desde la parte inferior.left
: El espacio seguro desde el lado izquierdo.right
: El espacio seguro desde el lado derecho.
¿Por qué es importante?
En Android, los valores suelen ser 0 porque muchos dispositivos no tienen notches o configuraciones similares. Sin embargo, en iOS, dispositivos como los iPhones con notches (iPhone X y más nuevos) pueden devolver valores como 59
o más para la propiedad top
, dependiendo del modelo.
Si ignoramos estos valores, nuestra UI podría verse cortada o solapada con elementos del sistema, lo que generaría una mala experiencia para el usuario.
Ejemplo práctico
Supongamos que tenemos una pantalla llamada ProfileScreen
y queremos asegurarnos de que su contenido respete el área segura del dispositivo. Aquí está el código completo que lo implementa:
import React from 'react';
import { Text, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { PrimaryButton } from '../../components/shared/PrimaryButton';
import { DrawerActions, useNavigation } from '@react-navigation/native';
export const ProfileScreen = () => {
// Obtenemos los márgenes seguros
const { top } = useSafeAreaInsets();
const navigation = useNavigation();
return (
<View
style={{
flex: 1, // Para ocupar toda la pantalla
paddingHorizontal: 20, // Espaciado lateral
marginTop: top, // Respetar el margen seguro superior
}}
>
<Text style={{ marginBottom: 10 }}>ProfileScreen</Text>
<PrimaryButton
onPress={() => navigation.dispatch(DrawerActions.toggleDrawer())}
label="Abrir menú"
/>
</View>
);
};
Desglose del código
-
Importación del hook:
import { useSafeAreaInsets } from 'react-native-safe-area-context';
Esto nos permite acceder a los márgenes seguros del dispositivo.
-
Uso del hook:
const { top } = useSafeAreaInsets();
Aquí extraemos la propiedad
top
del objeto devuelto por el hook. Este valor corresponde al margen seguro superior del dispositivo. -
Estilo dinámico con el margen seguro:
<View style={{ flex: 1, paddingHorizontal: 20, marginTop: top, }} >
Utilizamos el valor de
top
para establecer el margen superior dinámicamente, asegurándonos de que el contenido de nuestra pantalla no se superponga con el notch o la barra de estado. -
Uso del botón: El botón
PrimaryButton
en este ejemplo utilizanavigation.dispatch(DrawerActions.toggleDrawer())
para abrir el menú lateral.
Diferencias entre Android e iOS
-
Android: La mayoría de los dispositivos no tienen un notch o áreas seguras especiales, por lo que
top
suele ser0
. -
iOS: Dispositivos con notches o bordes curvos (como el iPhone X en adelante) devolverán valores más altos para
top
. Por ejemplo, en un iPhone X, este valor puede ser59
, dependiendo del modelo.
Ventajas de usar useSafeAreaInsets
- Adaptabilidad: Funciona automáticamente en dispositivos con diferentes configuraciones de pantalla.
- Compatibilidad multiplataforma: Respeta tanto Android como iOS sin necesidad de configuraciones específicas.
- Mejor experiencia de usuario: Asegura que los elementos de la UI no se solapen con partes del sistema, mejorando la presentación de la app.
Conclusión
El hook useSafeAreaInsets
es una herramienta imprescindible para garantizar que tu aplicación React Native sea amigable en dispositivos modernos. Si estás desarrollando una app con elementos que necesitan posicionarse cerca de los bordes de la pantalla, ¡asegúrate de utilizar este hook!
Con este enfoque, puedes crear experiencias visualmente atractivas y funcionales, sin importar el dispositivo o el sistema operativo. 😊