Navigation
La navigation mobile est fondamentalement différente de la navigation web. Sur le web, vous changez d'URL. Sur mobile, vous empiler des écrans ou basculez entre des onglets. Expo Router gère tout cela avec un système de routing basé sur les fichiers.
Navigation web vs navigation mobile
Sur le web (R4A10)
En R4A10, vous avez utilisé React Router pour naviguer entre des pages via des URLs :
// React Router (web) - R4A10
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users/:id" element={<UserDetail />} />
</Routes>
La navigation web repose sur des URLs : chaque page a une adresse unique, et le navigateur gère l'historique avec les boutons Précédent/Suivant.
Sur mobile (R4A11)
Sur mobile, la navigation utilise des patterns physiques spécifiques aux smartphones :
- Stack (pile) : les écrans s'empilent les uns sur les autres, avec un geste de swipe ou un bouton retour pour revenir en arrière
- Tabs (onglets) : une barre d'onglets en bas de l'écran permet de basculer entre les sections principales
- Drawer (tiroir) : un menu latéral glissant (moins utilisé aujourd'hui)
Stack : Tabs :
┌─────────┐ ┌─────────────────┐
│ Détail │ ← swipe back │ │
│─────────│ │ Contenu │
│ Liste │ │ │
│─────────│ │─────────────────│
│ Accueil │ │ 🏠 📋 ⚙️ │
└─────────┘ └─────────────────┘
Quelle est la principale différence entre la navigation web et la navigation mobile ?
Expo Router : routing basé sur les fichiers
Expo Router utilise un système de routing basé sur les fichiers, similaire à Next.js. Chaque fichier dans le dossier app/ correspond à un écran.
Principe de base
app/
├── _layout.tsx → Layout racine (configuration)
├── index.tsx → Écran d'accueil (route "/")
├── about.tsx → Écran À propos (route "/about")
└── settings.tsx → Écran Paramètres (route "/settings")
Chaque fichier exporte un composant par défaut qui devient un écran :
// app/about.tsx
import { View, Text, StyleSheet } from 'react-native';
export default function AboutScreen() {
return (
<View style={styles.container}>
<Text style={styles.title}>À propos</Text>
<Text>Cette application est un exemple R4A11.</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 16,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 12,
},
});
Comparaison avec React Router (R4A10)
| React Router (web) | Expo Router (mobile) | |
|---|---|---|
| Définition des routes | Configuration dans le code (JSX) | Structure de fichiers dans app/ |
| Lien | <Link to="/about"> | <Link href="/about"> |
| Navigation programmatique | useNavigate() | useRouter() |
| Route dynamique | /users/:id | app/users/[id].tsx |
| Layout | Composant wrapper manuel | Fichier _layout.tsx |
Comment crée-t-on une route /settings dans Expo Router ?
Stack Navigation (pile d'écrans)
La Stack Navigation empile les écrans les uns sur les autres. C'est le pattern le plus courant : vous naviguez vers un écran, et vous pouvez revenir en arrière avec le geste de swipe (iOS) ou le bouton retour (Android).
Configuration avec _layout.tsx
Le fichier _layout.tsx définit le type de navigation pour un groupe d'écrans :
// app/_layout.tsx
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen
name="index"
options={{ title: 'Accueil' }}
/>
<Stack.Screen
name="about"
options={{ title: 'À propos' }}
/>
<Stack.Screen
name="settings"
options={{ title: 'Paramètres' }}
/>
</Stack>
);
}
Options de l'en-tête (header)
<Stack.Screen
name="index"
options={{
title: 'Mon App', // Titre dans la barre
headerStyle: { backgroundColor: '#f5f5f5' }, // Fond de la barre
headerTintColor: '#333', // Couleur du texte et des icônes
headerTitleStyle: { fontWeight: 'bold' }, // Style du titre
headerShown: true, // Afficher/masquer la barre (true par défaut)
}}
/>
Naviguer entre les écrans
Avec le composant Link
import { Link } from 'expo-router';
import { View, Text, StyleSheet } from 'react-native';
export default function HomeScreen() {
return (
<View style={styles.container}>
<Text style={styles.title}>Accueil</Text>
{/* Lien vers l'écran About */}
<Link href="/about" style={styles.link}>
<Text style={styles.linkText}>Aller à À propos</Text>
</Link>
{/* Lien vers l'écran Settings */}
<Link href="/settings" style={styles.link}>
<Text style={styles.linkText}>Aller aux Paramètres</Text>
</Link>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
gap: 16,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 24,
},
link: {
backgroundColor: '#0066cc',
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
},
linkText: {
color: '#fff',
fontSize: 16,
fontWeight: '600',
},
});
Navigation programmatique avec useRouter
import { useRouter } from 'expo-router';
import { View, Text, Pressable, StyleSheet } from 'react-native';
export default function HomeScreen() {
const router = useRouter();
function handleNavigate() {
router.push('/about'); // Ajouter un écran à la pile
}
function handleReplace() {
router.replace('/settings'); // Remplacer l'écran actuel (pas de retour)
}
function handleGoBack() {
router.back(); // Revenir à l'écran précédent
}
return (
<View style={styles.container}>
<Pressable onPress={handleNavigate} style={styles.button}>
<Text style={styles.buttonText}>Aller à About (push)</Text>
</Pressable>
<Pressable onPress={handleReplace} style={styles.button}>
<Text style={styles.buttonText}>Remplacer par Settings</Text>
</Pressable>
<Pressable onPress={handleGoBack} style={styles.button}>
<Text style={styles.buttonText}>Retour</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
gap: 12,
},
button: {
backgroundColor: '#0066cc',
paddingHorizontal: 24,
paddingVertical: 12,
borderRadius: 8,
},
buttonText: {
color: '#fff',
fontSize: 16,
},
});
push vs replace vs back
router.push('/about'): ajoute l'écran About à la pile. L'utilisateur peut revenir en arrière.router.replace('/settings'): remplace l'écran actuel par Settings. L'utilisateur ne peut pas revenir en arrière (utile après une connexion réussie).router.back(): retourne à l'écran précédent (comme le bouton retour).
Quelle est la différence entre router.push() et router.replace() ?
Tab Navigation (onglets)
La Tab Navigation affiche une barre d'onglets en bas de l'écran. C'est le pattern utilisé par la plupart des applications (Instagram, Twitter, etc.).
Structure de fichiers
Les onglets sont définis dans un dossier avec des parenthèses (groupe de routes) :
app/
├── _layout.tsx → Layout racine (Stack)
├── (tabs)/ → Groupe d'onglets
│ ├── _layout.tsx → Configuration des onglets
│ ├── index.tsx → Onglet Accueil
│ ├── search.tsx → Onglet Recherche
│ └── profile.tsx → Onglet Profil
└── details/[id].tsx → Écran détail (hors onglets, dans le stack)
Configuration des onglets
// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
export default function TabLayout() {
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: '#0066cc',
tabBarInactiveTintColor: '#999',
}}
>
<Tabs.Screen
name="index"
options={{
title: 'Accueil',
tabBarIcon: ({ color, size }) => (
<Ionicons name="home" size={size} color={color} />
),
}}
/>
<Tabs.Screen
name="search"
options={{
title: 'Recherche',
tabBarIcon: ({ color, size }) => (
<Ionicons name="search" size={size} color={color} />
),
}}
/>
<Tabs.Screen
name="profile"
options={{
title: 'Profil',
tabBarIcon: ({ color, size }) => (
<Ionicons name="person" size={size} color={color} />
),
}}
/>
</Tabs>
);
}
Ionicons
@expo/vector-icons est inclus par défaut dans les projets Expo. Il fournit des milliers d'icônes via plusieurs bibliothèques (Ionicons, MaterialIcons, FontAwesome, etc.). Consultez icons.expo.fyi pour parcourir les icônes disponibles.
Layout racine avec Tabs et Stack
Pour combiner onglets et navigation par pile (ex : onglet Accueil qui pousse un écran détail) :
// app/_layout.tsx
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen
name="(tabs)"
options={{ headerShown: false }}
/>
<Stack.Screen
name="details/[id]"
options={{ title: 'Détails' }}
/>
</Stack>
);
}
Avec cette structure, les onglets sont la vue principale, et les écrans détail s'affichent au-dessus (empilés) quand on navigue vers eux.
Comment définir les onglets d'une application avec Expo Router ?
Routes dynamiques
Les routes dynamiques utilisent des crochets dans le nom du fichier, comme en R4A10 avec React Router :
app/
├── users/
│ ├── [id].tsx → Route dynamique : /users/1, /users/42, etc.
│ └── index.tsx → Route : /users
// app/users/[id].tsx
import { useLocalSearchParams } from 'expo-router';
import { View, Text, StyleSheet } from 'react-native';
export default function UserDetailScreen() {
// Récupérer le paramètre dynamique
const { id } = useLocalSearchParams();
return (
<View style={styles.container}>
<Text style={styles.title}>Utilisateur #{id}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
title: {
fontSize: 24,
fontWeight: 'bold',
},
});
Pour naviguer vers cette route :
import { Link } from 'expo-router';
// Avec Link
<Link href="/users/42">
<Text>Voir utilisateur 42</Text>
</Link>
// Avec useRouter
const router = useRouter();
router.push('/users/42');
useLocalSearchParams vs useParams
En R4A10 avec React Router, vous utilisiez useParams(). En Expo Router, c'est useLocalSearchParams(). Le principe est le même : récupérer les paramètres dynamiques de la route.
// React Router (web)
const { id } = useParams();
// Expo Router (mobile)
const { id } = useLocalSearchParams();
Comment créer une route dynamique /products/42 avec Expo Router ?
À retenir
Comprendre, pas mémoriser
Navigation mobile :
- Stack : écrans empilés, swipe pour revenir. Pattern principal.
- Tabs : barre d'onglets en bas, sections principales de l'app.
Expo Router :
- Routing basé sur les fichiers (comme Next.js)
_layout.tsxconfigure le type de navigation (Stack ou Tabs)LinkouuseRouter()pour naviguer entre les écrans[param].tsxpour les routes dynamiquesuseLocalSearchParams()pour récupérer les paramètres
Du web au mobile :
<Link to="/about">(React Router) devient<Link href="/about">(Expo Router)useNavigate()devientuseRouter()avecpush,replace,backuseParams()devientuseLocalSearchParams()