Développement Mobile Cross-Plateforme avec React Native : Créez des Applications iOS et Android Performantes
Développement Mobile Cross-Plateforme avec React Native : Créez des Applications iOS et Android Performantes

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 NavigationContainer est 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 objet Stack qui contient les composants Navigator et Screen.
  • <Stack.Navigator> est le composant parent qui gère la pile d'écrans. initialRouteName dé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.).

Chaque composant rendu par un Stack.Screen reçoit une prop navigation et une prop route.

  • La prop navigation contient des méthodes pour interagir avec le navigateur (naviguer, revenir en arrière, etc.).
  • La prop route contient 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 navigation et route dans des composants qui ne sont pas rendus directement par un Stack.Screen, utilisez les hooks useNavigation() et useRoute() 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 utilisons tabBarIcon pour afficher des icônes différentes selon l'onglet actif ou inactif.
  • tabBarActiveTintColor et tabBarInactiveTintColor : 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>

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.