• 30 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 12/12/2019

Concevez une navigation entre vos vues

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Vous connaissez à présent toutes les bases de la programmation en React Native. Mais je sens que vous avez envie d'en connaître bien plus, en tout cas, vous êtes là pour ça, non ? :D On va découvrir ensemble comment créer une application plus complète. Je vais vous apprendre à créer plusieurs vues et surtout à naviguer entre elles.

En développement mobile, la navigation représente le passage d'une vue à une autre. À moins que votre application n'ait qu'une seule et unique vue, vous allez forcément y être confronté.

C'est un point essentiel dans les applications mobiles. Une navigation bien gérée, cohérente, rend votre application intuitive et fluide. Pourtant, c'est souvent un sujet délicat lors de la réalisation d'applications cross-platforms, non pas parce que c'est difficile à maîtriser, mais parce que l'animation de la navigation n'est pas la même entre iOS et Android.

  • Sur iOS, on pousse nos vues : une animation affiche nos nouvelles vues par un slide de droite à gauche.

  • Sur Android, on présente nos vues : une animation affiche nos nouvelles vues par un slide de bas en haut.

React Native ne propose pas de solutions pour respecter cette différence dans les comportements natifs. :( Mais, heureusement pour nous, la communauté React Native a été confrontée au même problème et a créé une librairie pour gérer cette différence d'animation sur la navigation iOS et Android. On découvre cette librairie ensemble.

Librairie React Navigation

La librairie React Navigation est devenue un incontournable du développement en React Native, si bien qu'aujourd'hui elle dispose de son propre site avec sa propre documentation

On y apprend comment utiliser les différents types de navigation :

  • StackNavigator : c'est la navigation la plus basique où on pousse une vue sur iOS et présente une vue sur Android. Le StackNavigator gère une pile de vues qui augmente lorsque vous naviguez vers une nouvelle vue et diminue lorsque vous revenez en arrière. C'est cette navigation que l'on va utiliser dans ce chapitre.

  • TabNavigator : permet de créer une barre d'onglets, en haut ou en bas, de votre application. On utilisera ce type de navigation un peu plus loin dans ce cours, lorsque l'on voudra couper notre application en plusieurs onglets.

  • DrawerNavigator : permet de créer un menu dit "hamburger", à gauche de nos vues, avec une liste d'entrées pour chacune de nos vues.

A gauche un TabNavigator / A droite un DrawerNavigator
À gauche, un TabNavigator / À droite, un DrawerNavigator

Bien sûr, dans une application mobile, on mixe ces types de navigation. Généralement, on opte soit pour un TabNavigator, soit pour un DrawerNavigation, qui sont des navigations lourdes, souvent considérées comme navigations principales. Puis on utilise des StackNavigator pour des navigations légères, comme pour passer à la vue suivante ou revenir en arrière.

NavigatorIOS

Bon, quand je vous ai dit que React Native ne proposait aucune solution pour reproduire les navigations natives iOS et Android, je vous ai à moitié menti. Sur la documentation React Native, partie Navigation between screens, on aborde le sujet de React Navigation, mais aussi de NavigatorIOS.

NavigatorIOS est un component React Native qui respecte les comportements de la navigation native iOS, mais, comme son nom l'indique, il est spécifique à iOS. Il n'y a pas de solution équivalente pour Android.

Restons sur React Navigation

Dans notre application, on veut garder un code unique au maximum et que le résultat soit le plus proche possible d'une application native. On va donc rester sur React Navigation.

Avant de passer à la suite, on va ajouter React Navigation à notre projet. Car oui, ce n'est pas un component intégré dans React Native, mais bien une librairie externe. L'installation de librairie passe par le gestionnaire de paquets Node.js.

Pour installer une librairie avec Node.JS, il faut d'abord, dans votre terminal, arrêter l'exécution de votre serveur Node.JS en cours en faisant Ctrl + C. Vous devriez voir apparaître ces lignes dans votre terminal : 

10:07:16: Stopping packager...
10:07:16: Packager stopped.

Votre serveur Node.JS est arrêté, vous pouvez à présent installer la nouvelle librairie. Toujours dans le terminal, à la racine du projet, saisissez la commande :

$ npm install --save react-navigation

Une multitude de lignes s'affiche pour finir sur :

+ react-navigation@...
added XX packages, ...

Voilà, ni vu ni connu, vous savez maintenant comment ajouter des librairies externes à vos projets. Ce n'était pas bien compliqué, non ? :)

Vous pouvez à présent relancer votre serveur Node.JS avec votre toute nouvelle librairie installée en faisant, comme à notre habitude :

$ npm start

Créez un StackNavigator

 La librairie React Navigation est correctement installée, nous allons créer notre tout premier StackNavigatorRappelez-vous, un StackNavigator permet de passer d'un écran à l'autre

En React Native, on a pour habitude d'isoler toute la gestion de la navigation dans un fichier à part. Je vous demande donc de créer un dossier Navigation et d'y ajouter un fichier Javascript Navigation.js.

Ici, on va importer la fonction  createStackNavigator  de la librairie  react-navigation-stack  . Une fonction qui, comme son nom l'indique, permet de créer un StackNavigator. On ne va pas créer de component, donc pas besoin d'importer React :

// Navigation/Navigation.js

import { createStackNavigator } from 'react-navigation'

Pour la suite, il faut s'appuyer sur la documentation de React Navigation sur les StackNavigators. On y apprend comment initialiser un StackNavigator et son fonctionnement.

Fonctionnement

Un StackNavigator s'initialise avec les écrans qu'il va contenir. Cela signifie que vous devez renseigner la vue principale, mais aussi les vues qui vont être poussées et affichées. React Navigation associe ensuite des noms à vos vues et est capable d'appeler une vue, de revenir en arrière, de retourner sur une vue, etc.

Parfois, un exemple est beaucoup plus parlant. :) Dans notre application, la vue principale correspond à notre component Search. On va donc créer notre StackNavigator avec la vue du component Search et on va lui donner un nom :

// Navigation/Navigation.js

import { createStackNavigator } from 'react-navigation-stack'
import Search from '../Components/Search'

const SearchStackNavigator = createStackNavigator({
  Search: { // Ici j'ai appelé la vue "Search" mais on peut mettre ce que l'on veut. C'est le nom qu'on utilisera pour appeler cette vue
    screen: Search,
    navigationOptions: {
      title: 'Rechercher'
    }
  }
})

Utilisez la nouvelle navigation

Il nous reste une dernière chose à faire : utiliser notre StackNavigator et l’afficher dans notre application. Pour ce faire, il faut utiliser la fonction  createAppContainer  de React Navigation. Elle permet de formater votre navigation pour la rendre utilisable dans l’application.

On commence donc par importer la fonction  createAppContainer  :

// Navigation/Navigation.js

import { createStackNavigator, createAppContainer } from 'react-navigation-stack'

Ensuite, on va initialiser notre AppContainer avec la navigation que l’on souhaite afficher dans l’application et on va l’exporter pour le rendre accessible par les autres components. Autrement dit :

// Navigation/Navigation.js

export default createAppContainer(SearchStackNavigator)

Ça va, jusque là vous suivez ? ;)

Dernière étape, afficher notre navigation dans l’application. Si vous ouvrez le fichier App.js à la racine de votre projet, vous avez actuellement ceci :

// App.js

import React from 'react'
import Search from './Components/Search'

export default class App extends React.Component {
  render() {
    return (
      <Search/>
    )
  }
}

On appelle toujours notre component Search comme vue principale. Il faut juste indiquer ici à React Native de ne plus utiliser le component Search comme vue principale, mais d'utiliser notre AppContainer (qui lui-même contient notre StackNavigator) :

// App.js

import React from 'react'
import Navigation from './Navigation/Navigation'

export default class App extends React.Component {
  render() {
    return (
      <Navigation/>
    )
  }
}

Et c'est tout. Plutôt simple, n'est-ce pas ? :)

Rendu d'un StackNavigator

Vous pouvez retourner côté application et constater le rendu de notre StackNavigator :

StackNavigator sur iOS et Android
StackNavigator sur iOS et Android

Vous remarquez le bandeau en haut de vos écrans ? Il s'agit de la barre de navigation

À présent, toute votre navigation est gérée par cette barre de navigation. Vos vues vont se placer au-dessous à chaque fois.

Notre StackNavigator est prêt. Il affiche un joli bandeau en haut de notre écran. C'est bien, mais on n'a pas encore utilisé sa véritable fonctionnalité, à savoir naviguer. :pirate:

Préparez le terrain

Avant de pouvoir naviguer d'une vue à l'autre, il nous manque quelque chose. C'est tout bête, mais il nous manque une deuxième vue. :lol:

On va donc créer un component FilmDetail qui va correspondre à notre deuxième vue. Vous commencez à avoir l'habitude de créer des components, alors allons-y :

// Components/FilmDetail.js

import React from 'react'
import { StyleSheet, View, Text } from 'react-native'

class FilmDetail extends React.Component {
  render() {
    return (
      <View style={styles.main_container}>
        <Text>Détail du film</Text>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  main_container: {
    flex: 1,
  }
})

export default FilmDetail

Je vous l'ai dit tout à l'heure, on initialise un StackNavigator avec toutes les vues qu'il va contenir. On va donc ajouter notre nouvelle vue FilmDetail à notre StackNavigator.

// Navigation/Navigation.js

import { createStackNavigator, createAppContainer } from 'react-navigation-stack'
import Search from '../Components/Search'
import FilmDetail from '../Components/FilmDetail'

const SearchStackNavigator = createStackNavigator({
  Search: {
    screen: Search,
    navigationOptions: {
      title: 'Rechercher'
    }
  },
  FilmDetail: { // Encore une fois j'ai mis le même nom que celui du component mais libre à vous de choisir un nom différent
    screen: FilmDetail
  }
})

export default createAppContainer(SearchStackNavigator)

Créez une action au clic sur un item de notre FlatList

Vous l'aurez compris, avec le nom de notre seconde vue, FilmDetail, je souhaite que l'on affiche le détail d'un film. On va donc créer une action dans notre component Search qui va s'occuper spécifiquement de naviguer vers notre nouvel écran FilmDetail. Cette action prend en paramètre l'identifiant du film que l'on souhaite afficher en détail.

// Components/Search.js

class Search extends React.Component {

  _displayDetailForFilm = (idFilm) => {
    console.log("Display film with id " + idFilm)
  }
  
  ...
}

Pour l'instant, on va se contenter d'afficher un log et de vérifier que l'on récupère bien l'identifiant du film sélectionné.

Passez une fonction dans les props d'un component

Essayez de réfléchir à la question suivante :

Quel évènement, et surtout quel component, doit déclencher l'appel à notre fonction  _displayDetailForFilm déclarée juste au-dessus ? Je vous remets sous les yeux notre application à l'heure actuelle.

Application MoviesAndMe
Application MoviesAndMe

Cela paraît évident qu'il s'agit d'un clic sur un item (FilmItem) de notre FlatList. On veut qu'au clic sur un component FilmItem, le détail du film cliqué soit affiché. On va donc avoir besoin d'appeler la fonction  _displayDetailForFilm  dans le component FilmItem.

J'en ai parlé rapidement dans les précédents chapitres, les props permettent de transmettre toutes sortes d'informations. On a vu ensemble comment faire passer des données à un component. À présent, je vais vous montrer comment passer une fonction à un component.

Oui oui, une fonction dans les props, ^^ et devinez quoi ? C'est aussi simple que pour le passage de données :

// Components/Search.js

<FlatList
  ...
  renderItem={({item}) => <FilmItem film={item} displayDetailForFilm={this._displayDetailForFilm} />}
  ...
/>

Côté component FilmItem, on récupère la fonction passée via les props, comme on le fait pour une donnée :

// Components/FilmItem.js

class FilmItem extends React.Component {
  render() {
    const { film, displayDetailForFilm } = this.props
    return (
      <View style={styles.main_container}>
        ...
      </View>
    )
  }
}

Maintenant, on veut appeler la fonction récupérée des props  displayDetailForFilm  au clic sur notre component FilmItem. En gros, ici, on veut qu'au clic sur la vue de notre FilmItem (celle avec le style styles.main_container juste au-dessus), on appelle la fonction  displayDetailForFilm . Niveau code, cela donne ça, n'est-ce pas ? 

// Components/Search.js

class FilmItem extends React.Component {
  render() {
    const { film, displayDetailForFilm } = this.props
    return (
        <View
            style={styles.main_container}
            onPress={() => displayDetailForFilm(film.id)}> // On définit la props onPress sur notre View pour appeler notre fonction displayDetailForFilm
            ...
        </View>
    )
  }
}

C'est une très bonne idée. Malheureusement, cela ne marche pas. :( La fonction  onPress  n'existe pas sur le component View ni même  onClick  ,  onTouch, etc. En fait, il n'y a aucun moyen de récupérer un évènement sur un component View.

Je vous donne l'astuce, vous ne pouvez pas le deviner, pour le coup. Il faut utiliser un autre component que View. Et ici, on va utiliser un TouchableOpacity

TouchableOpacity

 On va donc remplacer la première View du component FilmItem par une TouchableOpacity qui est capable de récupérer des évènements, et notamment l'évènement  onPress  : 

// Components/FilmItem.js

import { ..., TouchableOpacity } from 'react-native'

class FilmItem extends React.Component {
  render() {
    const { film, displayDetailForFilm } = this.props
    return (
      <TouchableOpacity
        style={styles.main_container}
        onPress={() => displayDetailForFilm(film.id)}>
        <Image
          style={styles.image}
          source={{uri: getImageFromApi(film.poster_path)}}
        />
        ...
      </TouchableOpacity>
    )
  }
}
    

C'est tout bon. Vous pouvez vous rendre côté application et vérifier que l'on appelle bien la fonction  _displayDetailForFilm  avec l'identifiant du film sélectionné. Pour cela, faites une recherche et cliquez sur des films. Au niveau des logs, vous devriez voir les identifiants des films cliqués :

08:33:01: Display film with id 11
08:33:02: Display film with id 355547
08:33:03: Display film with id 181808
08:33:05: Display film with id 13475
08:33:13: Display film with id 188927

Super, cela fonctionne. :magicien: Bon, que s'est-il passé ici, exactement ? On va tout reprendre :

  • On définit une fonction  _displayDetailForFilm  dans le component Search. 

  • On fait passer la fonction  _displayDetailForFilm  au component FilmItem.

  • Dans le component FilmItem, on appelle la fonction  _displayDetailForFilm  au clic sur notre TouchableOpacity et on fait passer l'identifiant du film de l'item en cours.

  • Dans la fonction  _displayDetailForFilm, on affiche l'identifiant du film cliqué.

Ça va, vous suivez ? :) On va maintenant aller plus loin et faire ce que l'on est venu tester à la base : la navigation.

Naviguez d'une vue à l'autre

Depuis que l'on a ajouté notre vue Search à notre StackNavigator, quelque chose a changé dans ses props. Ajoutez un log dans le render du component Search, vous allez voir : 

// Components/Search.js

class Search extends React.Component {
    ...
    
    render() {
        console.log(this.props)
        return (
            ...
        )
    }
}

Rechargez votre application et constatez ce changement dans les logs :

08:49:52: Object {
08:49:52:   "navigation": Object {
08:49:52:     "addListener": [Function anonymous],
08:49:52:     "dispatch": [Function anonymous],
08:49:52:     "goBack": [Function goBack],
08:49:52:     "navigate": [Function navigate],
08:49:52:     "pop": [Function pop],
08:49:52:     "popToTop": [Function popToTop],
08:49:52:     "push": [Function push],
08:49:52:     "setParams": [Function setParams],
08:49:52:     "state": Object {
08:49:52:       "key": "Init-id-1517989792446-0",
08:49:52:       "routeName": "Search",
08:49:52:     },
08:49:52:   },
08:49:52:   "screenProps": undefined,
08:49:52: }

On a un objet navigation qui s'est ajouté à nos props. Et bien, c'est cet objet qui va nous permettre d'utiliser la navigation dans notre component Search. 

Et maintenant, que fait-on de cette prop ?

On va s'en servir pour naviguer d'une vue à l'autre. Si vous prêtez attention aux fonctions de la prop navigation , visible dans les logs ci-dessus, vous apercevez une fonction  navigate  :  "navigate": [Function navigate] .

C'est cette fonction que l'on va utiliser pour passer sur notre vue FilmDetail. La fonction  navigate  prend en paramètre le nom de la vue que l'on souhaite afficher, vous savez ce nom que l'on a défini lors de l'initialisation de notre StackNavigator. On va appeler cette fonction au clic sur un item de notre FlatList, donc dans  _displayDetailForFilm  :

// Components/Search.js

_displayDetailForFilm = (idFilm) => {
    console.log("Display film with id " + idFilm)
    this.props.navigation.navigate("FilmDetail")
}

Une nouvelle fois, placez-vous côté application. Faites une recherche, sélectionnez un film et TADAAM : :magicien:

Naviguer sur la vue FilmDetail sur iOS et Android
Naviguer sur la vue FilmDetail sur iOS et Android

On a réussi à naviguer d'une vue à l'autre. Je vous accorde qu'on y a passé pas mal de temps, mais il nous fallait au préalable préparer le terrain, avec l'action à appeler notamment. L'action de naviguer en soi est très simple et rapide à réaliser.

De plus, notre StackNavigator gère tout seul le retour à l'écran précédent. Si vous cliquez sur la flèche en haut à gauche sur la vue FilmDetail, vous revenez à l'écran Search.

Passez une prop à notre vue FilmDetail

Tout à l'heure, nous avons tout fait pour récupérer l'identifiant du film cliqué par l'utilisateur. Il ne nous reste plus qu'à le transmettre à notre vue FilmDetail lors de la navigation.

React Navigation a tout prévu. Voici la définition de la fonction  navigate  que l'on utilise pour naviguer entre les vues : 

navigate('RouteName', { parameters })

 On va donc ajouter notre identifiant dans l'objet  parameters  , comme ceci : 

// Components/Search.js

_displayDetailForFilm = (idFilm) => {
    console.log("Display film with id " + idFilm)
    this.props.navigation.navigate("FilmDetail", { idFilm: idFilm })
}

Récupérez une prop de la navigation

On a fait passer notre identifiant de film dans les paramètres de navigation au moment d'afficher la vue FilmDetail. On va se positionner de l'autre côté à présent, côté FilmDetail, et récupérer ce paramètre.

Étant donné que l'on n'a pas trop d'idée de l'endroit où se situe le paramètre et que l'on n'a pas le courage d'ouvrir la documentation, :lol: on va afficher un log avec toutes les informations de notre navigation :

// Components/FilmDetail.js

class FilmDetail extends React.Component {
  render() {
    console.log(this.props.navigation)
    return (
      ...
    )
  }
}

On se place côté application. On fait une recherche. On clique sur un film, vous commencez à connaître. Penchez-vous sur les logs, vous devriez voir ceci :

16:43:27: Display film with id 11
16:43:27: Object {
16:43:27:   "actions": Object {
16:43:27:     ...
16:43:27:   },
16:43:27:   "addListener": [Function addListener],
16:43:27:   "dangerouslyGetParent": [Function anonymous],
16:43:27:   "dismiss": [Function anonymous],
16:43:27:   "dispatch": [Function anonymous],
16:43:27:   "getParam": [Function getParam],
16:43:27:   "goBack": [Function anonymous],
16:43:27:   "isFocused": [Function anonymous],
16:43:27:   "navigate": [Function anonymous],
16:43:27:   "pop": [Function anonymous],
16:43:27:   "popToTop": [Function anonymous],
16:43:27:   "push": [Function anonymous],
16:43:27:   "replace": [Function anonymous],
16:43:27:   "reset": [Function anonymous],
16:43:27:   "setParams": [Function anonymous],
16:43:27:   "state": Object {
16:43:27:     "key": "id-1528555404165-1",
16:43:27:     "params": Object {
16:43:27:       "idFilm": 11,
16:43:27:     },
16:43:27:     "routeName": "FilmDetail",
16:43:27:   },
16:43:27: }

Notre identifiant de film se trouve donc dans un objet  this.props.navigation.state.params . C'est long comme chemin pour récupérer un paramètre, :o mais c'est ici que seront stockés les paramètres passés via la navigation. Vous pouvez vérifier que tout ce que l'on vient de voir fonctionne en faisant : (je vous mets le component en entier pour que l'on soit sûr d'avoir tous le même code)

// Components/FilmDetail.js

import React from 'react'
import { StyleSheet, View, Text } from 'react-native'

class FilmDetail extends React.Component {
  render() {
    return (
      <View style={styles.main_container}>
        <Text>Détail du film {this.props.navigation.state.params.idFilm}</Text>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  main_container: {
    flex: 1
  }
})

export default FilmDetail

Testez le rendu sur vos applications :

Passer des paramètres via la navigation
Passer des paramètres via la navigation

Cela reste basique, mais cela fonctionne. Beau boulot. :soleil:

On arrive au terme de ce chapitre consacré exclusivement à la navigation sur les applications React Native.

Vous savez à présent créer une navigation simple, avec React Navigation et ses StackNavigators, comment passer d'une vue à une autre, faire passer des paramètres et les récupérer. Vous pouvez enfin envisager d'ajouter plusieurs vues à votre projet et naviguer entre elles.

Plus tard, dans cette partie, je vous présenterai les autres types de navigation et notamment le TabNavigator avec lequel j'ai réalisé la barre d'onglets en bas, dans mon application de gestion de films.

Dans le prochain chapitre, on aborde un sujet qui, pour les développeurs mobiles, va être très facile. Pour les autres, un peu moins. :p On va parler de cycle de vie des components. On en profitera pour construire notre vue de détail du film, quoique... je vous laisserai gérer cette fois. Après tout, les styles en React Native n'ont plus de secret pour vous.

Exemple de certificat de réussite
Exemple de certificat de réussite