Gestion de la Navigation avec React Navigation
Introduction à la Navigation Mobile et React Navigation
Bienvenue dans cette leçon dédiée à la gestion de la navigation dans vos applications mobiles cross-plateforme avec React Native. La navigation est l'épine dorsale de toute application moderne, permettant aux utilisateurs de se déplacer intuitivement entre différentes vues (écrans) pour accéder aux fonctionnalités et contenus. Sans un système de navigation bien conçu, même l'application la plus performante serait inutilisable.
Dans l'écosystème React Native, React Navigation s'est imposé comme la solution standard et la plus robuste pour gérer les flux de navigation complexes. C'est une bibliothèque complète, flexible et hautement personnalisable, qui prend en charge tous les patterns de navigation courants (empilement, onglets, tiroir latéral, etc.) et offre une expérience utilisateur native.
Pourquoi React Navigation ?
- Expérience Native : Utilise des composants natifs pour les animations et les gestes, offrant une fluidité et une réactivité comparables aux applications natives pures.
- Flexibilité : Permet de créer des structures de navigation simples ou très complexes, incluant des navigateurs imbriqués.
- Personnalisation : Offre de nombreuses options pour styliser les en-têtes, les barres d'onglets, et les transitions entre écrans.
- Performance : Optimisé pour maintenir des performances élevées, même sur des appareils moins puissants.
- Écosystème Riche : Une grande communauté et une documentation exhaustive facilitent l'apprentissage et le dépannage.
Prérequis
Avant de plonger dans les détails de React Navigation, assurez-vous de maîtriser les concepts fondamentaux de React Native :
- La création et l'utilisation de composants fonctionnels.
- La gestion de l'état (Hooks comme
useState,useEffect). - L'utilisation des
props. - Les bases du JSX et du stylisme avec
StyleSheet.
Installation et Configuration de Base
L'installation de React Navigation se fait en plusieurs étapes, car elle dépend de quelques bibliothèques natives.
1. Installation des Dépendances Core
Commencez par installer la bibliothèque principale de React Navigation :
npm install @react-navigation/native
# ou
yarn add @react-navigation/native
2. Installation des Dépendances Nécéssaires
React Navigation nécessite des dépendances natives supplémentaires pour fonctionner correctement. Il est crucial de les installer :
npm install react-native-screens react-native-safe-area-context
# ou
yarn add react-native-screens react-native-safe-area-context
Pour les projets Expo, ces bibliothèques sont généralement gérées automatiquement. Pour les projets React Native CLI, vous devrez lier les bibliothèques natives. Pour les versions récentes de React Native (0.60+), l'auto-linking est activée :
cd ios && pod install && cd ..
3. "Wrapping" Votre Application
Pour que votre application puisse utiliser le contexte de navigation, vous devez envelopper le composant racine de votre application (App.js ou index.js) avec le composant NavigationContainer fourni par @react-navigation/native.
// App.js
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import MyStackNavigator from './src/navigation/MyStackNavigator'; // Nous allons créer ceci plus tard
export default function App() {
return (
<NavigationContainer>
{/* Ici viendront vos navigateurs (Stack, Tab, Drawer, etc.) */}
<MyStackNavigator />
</NavigationContainer>
);
}
Note : Le
NavigationContainerest crucial. Il gère l'état de la navigation et lie votre application au système de navigation natif. Il ne doit être rendu qu'une seule fois dans votre application.
Les Types de Navigateurs Courants
React Navigation propose différents types de navigateurs, chacun adapté à des patterns de navigation spécifiques. Les plus couramment utilisés sont le Stack Navigator, le Tab Navigator et le Drawer Navigator.
1. Le Stack Navigator (@react-navigation/native-stack)
Le Stack Navigator implémente le pattern de navigation le plus basique : une pile d'écrans. Lorsqu'un nouvel écran est ouvert, il est poussé au-dessus de la pile ; lorsque l'utilisateur revient en arrière, l'écran supérieur est retiré de la pile. C'est le comportement par défaut de la plupart des applications.
Installation
npm install @react-navigation/native-stack
# ou
yarn add @react-navigation/native-stack
Utilisation Basique
// src/navigation/MyStackNavigator.js
import * as React from 'react';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import HomeScreen from '../screens/HomeScreen';
import DetailsScreen from '../screens/DetailsScreen';
const Stack = createNativeStackNavigator();
function MyStackNavigator() {
return (
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Accueil de l\'App' }} // Options spécifiques à cet écran
/>
<Stack.Screen
name="Details"
component={DetailsScreen}
options={{ title: 'Détails de l\'Article' }}
/>
</Stack.Navigator>
);
}
export default MyStackNavigator;
Dans cet exemple :
createNativeStackNavigator()crée un objetStackqui contient les composantsNavigatoretScreen.<Stack.Navigator>est le composant parent qui gère la pile d'écrans.initialRouteNamedéfinit l'écran qui sera affiché au démarrage.<Stack.Screen>définit chaque écran individuel dans la pile.name: Un nom unique pour l'écran, utilisé pour la navigation.component: Le composant React Native qui sera rendu pour cet écran.options: Un objet pour configurer l'en-tête de l'écran (titre, style, boutons, etc.).
Naviguer entre les Écrans
Chaque composant rendu par un Stack.Screen reçoit une prop navigation et une prop route.
- La
propnavigationcontient des méthodes pour interagir avec le navigateur (naviguer, revenir en arrière, etc.). - La
proproutecontient des informations sur l'écran actuel, y compris les paramètres passés.
Voici un exemple simple de HomeScreen et DetailsScreen :
// src/screens/HomeScreen.js
import * as React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
function HomeScreen({ navigation }) { // navigation est destructuré des props
return (
<View style={styles.container}>
<Text style={styles.title}>Bienvenue sur l'Accueil !</Text>
<Button
title="Aller aux Détails"
onPress={() => navigation.navigate('Details', { itemId: 86, otherParam: 'Hello World' })}
/>
{/* Vous pouvez aussi naviguer vers l'écran Details avec push() pour ajouter un nouvel écran à la pile */}
{/* <Button
title="Aller aux Détails (Push)"
onPress={() => navigation.push('Details', { itemId: 87, otherParam: 'Autre message' })}
/> */}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f0f0f0',
},
title: {
fontSize: 24,
marginBottom: 20,
},
});
export default HomeScreen;
// src/screens/DetailsScreen.js
import * as React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
function DetailsScreen({ route, navigation }) { // route et navigation sont destructurés des props
// Récupérer les paramètres passés depuis l'écran précédent
const { itemId, otherParam } = route.params;
return (
<View style={styles.container}>
<Text style={styles.title}>Écran de Détails</Text>
<Text>ID de l'article : {JSON.stringify(itemId)}</Text>
<Text>Autre paramètre : {JSON.stringify(otherParam)}</Text>
<Button
title="Revenir en arrière"
onPress={() => navigation.goBack()}
/>
<Button
title="Revenir à l'Accueil (popToTop)"
onPress={() => navigation.popToTop()} // Permet de revenir au premier écran de la pile
/>
<Button
title="Aller aux Détails encore"
onPress={() => navigation.push('Details', { itemId: Math.floor(Math.random() * 100) })}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#e6e6fa',
},
title: {
fontSize: 24,
marginBottom: 20,
},
});
export default DetailsScreen;
Méthodes de Navigation Courantes :
navigation.navigate('NomDeLEcran', { params }): Navigue vers un écran. Si l'écran est déjà dans la pile, il le ramène au sommet.navigation.push('NomDeLEcran', { params }): Pousse toujours un nouvel écran sur la pile, même s'il est déjà présent. Utile pour des flux comme l'ouverture d'un même article à partir de plusieurs liens.navigation.goBack(): Revient à l'écran précédent dans la pile.navigation.popToTop(): Revient au tout premier écran de la pile.navigation.replace('NomDeLEcran', { params }): Remplace l'écran actuel par un nouvel écran, en le retirant de la pile.navigation.setOptions({ ...options }): Permet de modifier dynamiquement les options de l'en-tête de l'écran (titre, boutons) depuis le composant de l'écran lui-même.
Conseil : Pour accéder aux objets
navigationetroutedans des composants qui ne sont pas rendus directement par unStack.Screen, utilisez les hooksuseNavigation()etuseRoute()fournis par@react-navigation/native.
2. Le Tab Navigator (@react-navigation/bottom-tabs)
Le Tab Navigator affiche une barre d'onglets (généralement en bas de l'écran) permettant de passer rapidement d'une section principale à une autre. Chaque onglet gère souvent sa propre pile de navigation.
Installation
npm install @react-navigation/bottom-tabs
# ou
yarn add @react-navigation/bottom-tabs
Utilisation Basique
// src/navigation/MyTabNavigator.js
import * as React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { Ionicons } from '@expo/vector-icons'; // Exemple avec Expo Vector Icons
import FeedScreen from '../screens/FeedScreen';
import SettingsScreen from '../screens/SettingsScreen';
import ProfileScreen from '../screens/ProfileScreen';
const Tab = createBottomTabNavigator();
function MyTabNavigator() {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Feed') {
iconName = focused ? 'home' : 'home-outline';
} else if (route.name === 'Settings') {
iconName = focused ? 'settings' : 'settings-outline';
} else if (route.name === 'Profile') {
iconName = focused ? 'person' : 'person-outline';
}
return <Ionicons name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: 'tomato',
tabBarInactiveTintColor: 'gray',
headerShown: false, // Cache l'en-tête du Stack Navigator si un Tab Navigator est imbriqué dans un Stack.
})}
>
<Tab.Screen name="Feed" component={FeedScreen} options={{ title: 'Accueil' }} />
<Tab.Screen name="Profile" component={ProfileScreen} options={{ title: 'Profil' }} />
<Tab.Screen name="Settings" component={SettingsScreen} options={{ title: 'Paramètres' }} />
</Tab.Navigator>
);
}
export default MyTabNavigator;
// src/screens/FeedScreen.js
import React from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';
import { useNavigation } from '@react-navigation/native'; // Utilisation du hook useNavigation
function FeedScreen() {
const navigation = useNavigation();
return (
<View style={styles.container}>
<Text style={styles.title}>Votre Fil d'Actualité</Text>
<Button
title="Aller à un écran de détail (Stack)"
onPress={() => navigation.navigate('Details', { articleId: 123 })} // Navigue vers un écran "Details" si ce TabNavigator est imbriqué dans un StackNavigator
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f8f8f8',
},
title: {
fontSize: 24,
marginBottom: 20,
},
});
export default FeedScreen;
Dans le Tab.Navigator :
screenOptions: Permet de définir des options communes à tous les onglets. Ici, nous utilisonstabBarIconpour afficher des icônes différentes selon l'onglet actif ou inactif.tabBarActiveTintColorettabBarInactiveTintColor: Définissent les couleurs des icônes et labels des onglets.
3. Le Drawer Navigator (@react-navigation/drawer)
Le Drawer Navigator (ou "tiroir latéral") est un menu qui glisse sur le côté de l'écran, généralement accessible via une icône "hamburger". Il est souvent utilisé pour des sections de l'application moins fréquemment visitées ou pour des paramètres globaux.
Installation
npm install @react-navigation/drawer
# ou
yarn add @react-navigation/drawer
Le Drawer Navigator dépend également de react-native-gesture-handler et react-native-reanimated.
npm install react-native-gesture-handler react-native-reanimated
# ou
yarn add react-native-gesture-handler react-native-reanimated
Pour react-native-reanimated, une configuration supplémentaire est nécessaire. Ajoutez plugins: ['react-native-reanimated/plugin'] à votre fichier babel.config.js.
Utilisation Basique
// src/navigation/MyDrawerNavigator.js
import * as React from 'react';
import { createDrawerNavigator } from '@react-navigation/drawer';
import HomeScreen from '../screens/HomeScreen'; // Peut être un StackNavigator ou TabNavigator
import NotificationsScreen from '../screens/NotificationsScreen';
const Drawer = createDrawerNavigator();
function MyDrawerNavigator() {
return (
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} options={{ title: 'Accueil Principal' }} />
<Drawer.Screen name="Notifications" component={NotificationsScreen} />
</Drawer.Navigator>
);
}
export default MyDrawerNavigator;
L'ouverture et la fermeture du tiroir se font avec navigation.openDrawer() et navigation.closeDrawer(), ou via des gestes de balayage.
Concepts Avancés et Personnalisation
Options de Navigation
La propriété options de Stack.Screen, Tab.Screen, ou Drawer.Screen permet une personnalisation profonde de l'affichage de l'écran.
Exemples d'options courantes pour Stack.Screen :
<Stack.Screen
name="Details"
component={DetailsScreen}
options={{
title: 'Détails du Produit', // Titre dans l'en-tête
headerStyle: {
backgroundColor: '#f4511e', // Couleur de fond de l'en-tête
},
headerTintColor: '#fff', // Couleur du texte de l'en-tête
headerTitleStyle: {
fontWeight: 'bold',
},
headerRight: () => ( // Ajouter un bouton personnalisé à droite
<Button
onPress={() => alert('Panier ajouté!')}
title="Panier"
color="#fff"
/>
),
}}
/>
Vous pouvez aussi définir des options globales pour tout un navigateur en utilisant screenOptions sur le Navigator parent :
<Stack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: '#6200EE', // En-tête violet par défaut pour tous les écrans
},
headerTintColor: '#fff',
headerBackTitleVisible: false, // Masque le texte "Retour" sur iOS
}}
>
{/* Vos écrans */}
</Stack.Navigator>
Navigateurs Imbriqués (Nested Navigators)
Il est très courant d'imbriquer des navigateurs pour construire des structures d'application complexes. Par exemple, chaque onglet d'un Tab Navigator peut avoir son propre Stack Navigator.
// App.js (simplifié)
import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import MyTabNavigator from './src/navigation/MyTabNavigator';
import AuthScreen from './src/screens/AuthScreen'; // Écran d'authentification
import DetailsScreen from './src/screens/DetailsScreen'; // Écran de détails partagé
const RootStack = createNativeStackNavigator();
export default function App() {
const [isAuthenticated, setIsAuthenticated] = React.useState(false); // État d'authentification
// Simulation d'une logique d'authentification
React.useEffect(() => {
// Vérifier le token, etc.
setTimeout(() => {
setIsAuthenticated(true); // Supposons que l'utilisateur est authentifié après 2 secondes
}, 2000);
}, []);
return (
<NavigationContainer>
<RootStack.Navigator>
{isAuthenticated ? (
<>
<RootStack.Screen
name="MainApp"
component={MyTabNavigator} // Le Tab Navigator est imbriqué ici
options={{ headerShown: false }} // Cache l'en-tête pour le Tab Navigator
/>
<RootStack.Screen name="Details" component={DetailsScreen} />
{/* D'autres écrans qui ne font pas partie du flux principal (comme un écran de réglages globaux) */}
</>
) : (
<RootStack.Screen
name="Auth"
component={AuthScreen}
options={{ headerShown: false }}
/>
)}
</RootStack.Navigator>
</NavigationContainer>
);
}
Dans cet exemple, un RootStack gère le flux principal de l'application (authentification vs. application principale). Une fois authentifié, l'application principale est rendue via MyTabNavigator, qui contient lui-même d'autres écrans ou navigateurs.
Passer des Paramètres
Les paramètres sont passés via le deuxième argument de navigate() ou push():
navigation.navigate('Details', { itemId: 123, otherParam: 'Anything' });
Et récupérés via route.params dans le composant de l'écran de destination :
const { itemId, otherParam } = route.params;
Il est essentiel de gérer les cas où route.params est undefined ou que les clés ne sont pas présentes, surtout si l'écran peut être appelé sans paramètres.
Conclusion
La gestion de la navigation est un aspect fondamental du développement d'applications mobiles. React Navigation fournit un ensemble d'outils puissants et flexibles pour construire des interfaces utilisateur fluides et intuitives dans vos applications React Native.
Dans cette leçon, nous avons couvert les bases :
- L'installation de React Navigation et de ses dépendances.
- Les trois principaux types de navigateurs : Stack, Tab et Drawer, et comment les utiliser.
- Les méthodes pour naviguer entre les écrans et passer des paramètres.
- Les concepts de personnalisation des en-têtes et des barres d'onglets.
- Un aperçu des navigateurs imbriqués pour des structures complexes.
Maîtriser React Navigation vous permettra de créer des expériences utilisateur riches et performantes, essentielles au succès de toute application mobile. N'hésitez pas à explorer la documentation officielle de React Navigation pour des cas d'utilisation plus avancés et des options de personnalisation approfondies.