React Native Gestionnaire de mots de passe : La navigation

26 octobre 2017

Thibault MOCELLIN
React Native Gestionnaire de mots de passe : La navigation

Bonjour à tous on se retrouve aujourd'hui pour ajouter la navigation à notre application. Dans cet article nous allons voir sans rentrer dans tous les détails comment fonctionne la navigation avec react-navigation.

Dans notre applications nous allons utiliser quatre concepts de navigation qui sont StackNavigator, TabNavigator, DrawerNavigator et la navigation imbriquée.

StackNavigator

Le StackNavigator est le composant qui permet d'ajouter la navigation la plus basique à notre application. Le principe est le suivant : nous partons d'un écran et ensuite à chaque fois que l'on se rend sur un autre écran celui-ci est ajouté au dessus de la stack et créé un historique qui nous permet de revenir en arrière.

TabNavigtor

Le TabNavigator permet d'ajouter facilement une navigation par tab à notre application.Nous avons juste à lui fournir les écrans et ensuite il s'occupe d'ajouter les tabs pour nous.

DrawerNavigator

Le DrawerNavigator quand à lui permet d'ajouter le menu latérale que l'on est habitué à voir dans les applications mobiles. Le principe est similaire au TabNavigator il suffit de lui passer les écrans et il créé le menu automatiquement.

NestedNavigation (Navigation imbriquée)

Ce principe comme son nom l'indique est d'imbriquer plusieurs éléments de navigation entre eux. Par exemple dans notre application nous allons avoir un StackNavigator qui sera le navigator de premier niveau avec une liste d'écran et parmi ces écrans nous allons en rajouter un qui sera soit un TabNavigator pour iOS ou un DrawerNavigator pour Android.

Voici un schéma qui montre comment va fonctionner la navigation au sein de notre application :

react navigation

Comme vous pouvez le voir notre StackNavigator s'occupera de gérer les écrans d'initialisation, de déverrouillage, de visualisation et édition des mots de passe ainsi que notre TabNavigator/DrawerNavigator.

Enfin les écrans : liste des mots de passe , Réglage et Synchronisation seront gérez par le TabNavigator/DrawerNavigator.

Rectification des articles précédents :

Avant de passer à la navigation il y deux modifications que nous devons apporter.

La première concerne le composant PasswordList lors de la création des composants je nous avais fait ajouter la propriété contentInset et la fonction defineContentInset en justifiant qu'une partie de la liste serais tronqué due à la hauteur des Tabs.

Ce problème était un problème que j'avais rencontré en utilisant l'equivalent du TabNavigtor mais avec l'ancien standard de navigation de React Native or le problème n'apparait pas avec react-navigation nous pouvons donc supprimer cette propriété et cette fonction du composant.

Ensuite le second point concerne la barre de navigation celle-ci est automatiquement générée lorsqu'on utilise le StackNavigator et ajoutée par dessus nos écrans. Cependant elle n'est pas ajoutée lorsqu'on utilise le DrawerNavigator ainsi que le TabNavigator nous allons donc devoir la créer et la rajouter sur les écrans suivants :

  • Liste des mots de passe
  • Réglages
  • Synchronisation

Dans le dossier components créer un dossier NavBar et ajoutez le fichiers index.android.js et index.ios.js.

Ensuite ajoutez le code suivant dans index.ios.js :

/*
* @flow
*/

import React from 'react'
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
import Icon from 'react-native-vector-icons/Ionicons'
import {
  IOS_FULL_HEIGHT,
  IOS_MARGIN,
  IOS_STATUS_BAR_HEIGHT,
  IOS_NAV_BAR_HEIGHT,
} from '../../constants/dimensions'
import { WHITE, PRIMARY } from '../../constants/colors'
import { IconPlateform } from '../../common/PlatformHelper'

type Props = {
  needIconLeft: boolean,
  needIconRight: boolean,
  iconLeft: string,
  iconRight: string,
  title: string,
  actionLeft: () => void,
  actionRight: () => void,
}

const touchableIOS = (icon: string, onPress: () => void, style: Object) => (
  <TouchableOpacity onPress={onPress} style={[styles.icon, style]}>
    <Icon name={IconPlateform(icon)} size={36} color={WHITE} />
  </TouchableOpacity>
)

const renderTouchable = (
  render: boolean,
  icon: string,
  onPress: () => void,
  style: Object
) => {
  if (render) {
    return touchableIOS(icon, onPress, style)
  }
  return null
}

const NavBar = (props: Props) => (
  <View style={styles.container}>
    <View style={styles.content}>
      <View style={styles.iconContainer}>
        {renderTouchable(props.needIconLeft, props.iconLeft, props.actionLeft, {
          justifyContent: 'flex-start',
        })}
      </View>
      <Text style={styles.title}>{props.title}</Text>
      <View style={styles.iconContainer}>
        {renderTouchable(
          props.needIconRight,
          props.iconRight,
          props.actionRight,
          {
            justifyContent: 'flex-end',
          }
        )}
      </View>
    </View>
  </View>
)

NavBar.defaultProps = {
  needIconLeft: false,
  needIconRight: false,
  iconLeft: 'arrow-back',
  iconRight: 'refresh',
  title: 'Title',
  actionLeft: () => console.log('left press'),
  actionRight: () => console.log('left press'),
}
export default NavBar

const styles = StyleSheet.create({
  container: {
    justifyContent: 'flex-start',
    height: IOS_FULL_HEIGHT,

    backgroundColor: PRIMARY,
  },
  content: {
    marginTop: IOS_STATUS_BAR_HEIGHT,
    paddingHorizontal: IOS_MARGIN,
    height: IOS_NAV_BAR_HEIGHT,
    justifyContent: 'space-between',
    alignItems: 'center',

    flexDirection: 'row',
  },
  title: {
    alignSelf: 'center',
    fontSize: 17,
    letterSpacing: 0.5,
    fontWeight: '600',
    color: WHITE,
  },
  iconContainer: {
    width: 50,
  },
  icon: {
    alignItems: 'center',
    flexDirection: 'row',
    width: 50,
  },
})

Et celui ci pour index.android.js :

/*
* @flow
*/

import React from 'react'
import { StyleSheet } from 'react-native'
import Icon from 'react-native-vector-icons/Ionicons'
import { IconPlateform } from '../../common/PlatformHelper'
import { ANDROID_NAV_HEIGHT } from '../../constants/dimensions'
import { WHITE, PRIMARY } from '../../constants/colors'

type Props = {
  title: string,
  actionLeft: () => void,
  toolbarActions: Array<Object>,
  iconLeft: string,
  onActionSelected: (position: number) => void,
}

const NavBar = (props: Props) => (
  <Icon.ToolbarAndroid
    navIconName={IconPlateform(props.iconLeft)}
    onActionSelected={props.onActionSelected}
    onIconClicked={props.actionLeft}
    style={styles.toolbar}
    actions={props.toolbarActions}
    title={props.title}
    iconColor={WHITE}
    titleColor={WHITE}
  />
)

export default NavBar

NavBar.defaultProps = {
  title: 'Title',
  actionLeft: () => console.log('left click'),
  toolbarActions: [],
  iconLeft: 'menu',
  onActionSelected: position => console.log(`action idx${position}`),
}
// sample actions [{ title: 'Done', iconName: 'md-color-wand', show: 'always' }]

const styles = StyleSheet.create({
  toolbar: {
    backgroundColor: PRIMARY,
    height: ANDROID_NAV_HEIGHT,
  },
})

Afin de gagner du temps nous utilison le composant ToolbarAndroid fournit par la librairie react-native-vector-icons.

Ensuite nous allons rajoutez les ressources pour les titres des écrans :

fr.js

...
// screen title
settings: 'Réglages',
passwordList: 'Mots de passe',
synchronization: 'Synchronisation',
edition: 'Edition',
creation: 'Ajouter un mot de passe',

en.js

...
// screen title
settings: 'Settings',
passwordList: 'Passwords',
synchronization: 'Synchronization',
edition: 'Edition',
creation: 'Add a new password',

strings.js

...
settings: I18n.t('settings'),
passwordList: I18n.t('passwordList'),
synchronization: I18n.t('synchronization'),
edition: I18n.t('edition'),
creation: I18n.t('creation'),

Maintenant nous allons modifier les différents écrans :

Settings.js

...
import NavBar from '../components/NavBar';
...
class SettingsScreen extends Component<void, void, State> {
  ...
  render() {
    return (
      <View style={styles.container}>
        <NavBar title={strings.settings} />
        <SliderRow
          label={strings.passwordLength}
          onValueChange={value => this.setPasswordLength(value)}
          selectedValue={this.state.passwordLength}
        />
        <CheckBoxRow
          label={strings.passwordAuto}
          iconName="star"
          isChecked={this.state.autoGeneration}
          switchValueChange={value => this.setAutoGeneration(value)}
        />
        <View style={styles.itemContainer}>
          <SettingRow
            label={strings.deleteAllPasswords}
            iconName="trash"
            iconBackground={DELETE_COLOR}
            iosOultine
            onPress={() => this.deleteAllPasswords()}
          />
        </View>
      </View>
    );
  }
}
...

Synchronization.js

...
import NavBar from '../components/NavBar';

export default class SynchronizationScreen extends Component {
 ...
  render() {
    return (
      <View style={styles.container}>
        <NavBar title={strings.synchronization} />
        <SettingRow
          label={strings.publish}
          iconName="cloud-upload"
          withSeparator
          iosOultine
          iosSeparator
          iconBackground={PRIMARY}
          onPress={() => this.uploadBackup()}
        />
        <SettingRow
          label={strings.pull}
          iconName="cloud-download"
          iosOultine
          iconBackground={PRIMARY}
          onPress={() => this.downloadBackup()}
        />
        <View style={styles.itmCtnr}>
          <SettingRow
            label={strings.deleteBackup}
            iconName="trash"
            iconBackground={DELETE_COLOR}
            iosOultine
            onPress={() => this.deleteBackup()}
          />
        </View>
      </View>
    );
  }
}
...

InitSynchronization.js

...
import NavBar from '../components/NavBar';
...
export default class InitSynchronizationScreen extends Component {
  ...
  render() {
    return (
      <View style={{ flex: 1 }}>
        <NavBar title={strings.synchronization} />
        <ScrollView contentContainerStyle={styles.container}>
          <Image style={styles.image} source={image} />
          <Text style={styles.title}>{strings.synchInstruction}</Text>
          <View style={styles.login}>
            <Button title={strings.login} onPress={() => console.log('log in dropbox')} />
          </View>
        </ScrollView>
      </View>
    );
  }
}
...

Enfin nous allons aussi ajouter une navbar à l'écran d'édition des mots afin de pouvoir déplacer le bouton de génération du mot de passe :

Edit.js

...
import NavBar from '../components/NavBar';
...
class ReadOnlyScreen extends Component<void, void, State> {
  ...
  render() {
    const { icon, color, name, password, login, url, modalIsOpen } = this.state;
    return (
      <View style={styles.main}>
        <NavBar
          title={strings.edition}
          needIconLeft
          needIconRight
          actionLeft={() => this.props.navigation.goBack()}
          actionRight={() => this.generatePassword()}
          iconLeft="arrow-back"
          onActionSelected={() => this.generatePassword()}
          toolbarActions={[{ title: strings.generate, iconName: 'md-refresh', show: 'always' }]}
        />

        <KeyboardAwareScrollView style={styles.scrollContent}>
          <View style={styles.container}>
            <View style={styles.iconCtnr}>
              <View style={styles.icon}>
                <IconPicker icon={icon} color={color} onPress={() => this.toggleModal()} />
              </View>
            </View>

            <TextField
              placeholder={strings.siteName}
              value={name}
              onSubmitEditing={() => this.urlField.focus()}
              onChangeText={text => this.setState({ name: text })}
              returnKeyType="next"
            />
            <TextField
              icon="globe"
              placeholder={strings.siteUrl}
              value={url}
              ref={(c) => {
                this.urlField = c;
              }}
              onSubmitEditing={() => this.loginField.focus()}
              onChangeText={text => this.setState({ url: text })}
              returnKeyType="next"
            />
            <TextField
              icon="person"
              placeholder={strings.userName}
              value={login}
              ref={(c) => {
                this.loginField = c;
              }}
              onSubmitEditing={() => this.passwordField.focus()}
              onChangeText={text => this.setState({ login: text })}
              returnKeyType="next"
            />
            <TextField
              icon="lock"
              placeholder={strings.password}
              value={password}
              ref={(c) => {
                this.passwordField = c;
              }}
              onChangeText={text => this.setState({ password: text })}
              secureTextEntry
            />
            <View style={styles.colorSelector}>
              <ColorSelector onPress={colorValue => this.selectColor(colorValue)} />
            </View>

            <View style={styles.actionContainer}>
              <Button title={strings.save} color={PRIMARY} onPress={() => this.save()} />
            </View>
          </View>
        </KeyboardAwareScrollView>

        <IconModal
          onSelectIcon={iconName => this.selectIcon(iconName)}
          isOpen={modalIsOpen}
          toggleModal={() => this.toggleModal()}
        />
      </View>
    );
  }
}
...

Ici on peut voir que pour revenir en arrière on utilise this.props.navigation.goBack. Dès lors que nous utilisons le StackNavigator chaque écran listé dans les routes va recevoir une propriété navigation qui permet d'interagir avec l'api de navigation.

Navigation

Nous allons maintenant pouvoir nous consacrer à la navigation. Dans un premier temps nous allons créer le TabNavigator ainsi que le DrawerNavigator puis dans un second temps nous rajouterons le StackNavigator ainsi que la navigation entres les différents écrans.

TabNavigator

Dans le dossier screens rajoutez un fichier TabNavigator.js et ajoutez le code suivant :

/*
* @flow
*/

import React from 'react'
import { TabNavigator } from 'react-navigation'
import Icon from 'react-native-vector-icons/Ionicons'
import PasswordsScreen from '../screens/Passwords'
import SettingsScreen from '../screens/Settings'
import SynchronizationScreen from '../screens/Synchronization'
import { PRIMARY, IOS_TABICON, WHITE } from '../constants/colors'
import strings from '../locales/strings'

const tabNavigator = TabNavigator(
  {
    Passwords: {
      screen: PasswordsScreen,
      navigationOptions: () => ({
        tabBarLabel: strings.passwordList,
        tabBarIcon: ({ tintColor }) => (
          <Icon name="ios-apps-outline" color={tintColor} size={26} />
        ),
      }),
    },
    Settings: {
      screen: SettingsScreen,
      navigationOptions: () => ({
        tabBarLabel: strings.settings,
        tabBarIcon: ({ tintColor }) => (
          <Icon name="ios-options-outline" color={tintColor} size={26} />
        ),
      }),
    },
    Synchronization: {
      screen: SynchronizationScreen,
      navigationOptions: () => ({
        tabBarLabel: strings.synchronization,
        tabBarIcon: ({ tintColor }) => (
          <Icon name="ios-cloud-outline" color={tintColor} size={26} />
        ),
      }),
    },
  },
  {
    tabBarPosition: 'bottom',
    animationEnabled: true,
    tabBarOptions: {
      activeTintColor: PRIMARY,
      inactiveTintColor: IOS_TABICON,
      labelStyle: { fontSize: 13 },
      style: { backgroundColor: WHITE },
    },
  }
)
export default tabNavigator

Voyons ce que nous avons dans ce composant. Tout d'abord nous importons les librairies nécessaires ainsi que l'ensemble des écrans puis les ressources.

Ensuite nous définissons notre TabNavigator avec l'ensemble des routes.

Passwords: {
  screen: PasswordsScreen,
  navigationOptions: () => ({
    tabBarLabel: strings.passwordList,
    tabBarIcon: ({ tintColor }) => <Icon name="ios-apps-outline" color={tintColor} size={26} />,
  }),
},

Dans cet exemple nous avons la route Passwords, l'attribut screen nous permet de définir quel écran devra être affiché. Ensuite nous avons les options de navigation la première concerne le label qui sera affiché sous l'icône dans la tabBar, la seconde concerne l'icône ici nous affichons un Icon de la librairie react-native-vector-icons mais on pourrais aussi choisir d'afficher une image.

On passe aussi la variable tintColor ce qui nous permet d'avoir des couleurs dynamiques en fonction du style de la TabBar ce que nous allons voir ci-dessous :

{
  tabBarPosition: 'bottom',
  animationEnabled: true,
  tabBarOptions: {
    activeTintColor: PRIMARY,
    inactiveTintColor: IOS_TABICON,
    labelStyle: { fontSize: 13 },
    style: { backgroundColor: WHITE },
  },
},

Ici nous avons les propriétés de la TabBar nous définissons sa position, nous activons les animations. Ensuite nous définissons les options de la TabBar : la couleur de l'icône et du texte lorsque la Tab est sélectionnée et la couleur lorsqu'elle n'est pas sélectionnée ce sont ces couleurs qui sont passées au paramètre tintColor que nous avons vue sur les routes.

Pour tester vous pouvez modifier le fichier index.js à la racine de src de la manière suivante :

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import TabNavigator from './screens/TabNavigator'
export default TabNavigator

Malgré que nous utilisons pas ce composant sur Android dans notre application il est compatible avec Android vous pouvez lancé un émulateur Android pour voir les différences entre la version iOS et Android.

DrawerNavigator

Nous allons passer au DrawerNavigator toujours dans le dossier screens ajoutez le fichier DrawerNavigator.js :

/*
* @flow
*/

import React from 'react'
import { ScrollView, Image, StyleSheet, View } from 'react-native'
import { DrawerNavigator, DrawerItems } from 'react-navigation'
import Icon from 'react-native-vector-icons/Ionicons'
import PasswordsScreen from '../screens/Passwords'
import SettingsScreen from '../screens/Settings'
import SynchronizationScreen from '../screens/Synchronization'
import { PRIMARY, ANDROID_SEPARATOR } from '../constants/colors'
import strings from '../locales/strings'

const image = require('../img/book.png')

const DrawerContent = (props: Object) => (
  <ScrollView contentContainerStyle={styles.container}>
    <Image source={image} style={styles.logo} />
    <View style={styles.separator} />
    <DrawerItems {...props} />
  </ScrollView>
)

const drawerNavigator = DrawerNavigator(
  {
    Passwords: {
      screen: PasswordsScreen,
      navigationOptions: () => ({
        drawerLabel: strings.passwordList,
        drawerIcon: ({ tintColor }) => (
          <Icon name="ios-apps-outline" color={tintColor} size={26} />
        ),
      }),
    },
    Settings: {
      screen: SettingsScreen,
      navigationOptions: () => ({
        drawerLabel: strings.settings,
        drawerIcon: ({ tintColor }) => (
          <Icon name="ios-options-outline" color={tintColor} size={26} />
        ),
      }),
    },
    Synchronization: {
      screen: SynchronizationScreen,
      navigationOptions: () => ({
        drawerLabel: strings.synchronization,
        drawerIcon: ({ tintColor }) => (
          <Icon name="ios-cloud-outline" color={tintColor} size={26} />
        ),
      }),
    },
  },
  {
    drawerWidth: 300,
    contentComponent: props => DrawerContent(props),
    contentOptions: {
      activeTintColor: PRIMARY,
      style: {
        marginVertical: 0,
      },
    },
  }
)

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  logo: {
    marginVertical: 32,
    alignSelf: 'center',
  },
  separator: {
    height: 1,
    backgroundColor: ANDROID_SEPARATOR,
    marginBottom: 32,
  },
})
export default drawerNavigator

Le fonctionnement du DrawerNavigator est similaire à celui du TabNavigator que se soit au niveau des routes ou biens des options. Ici le seule chose différentes que nous avons fait est de créer un nouveau composant DrawerContent pour remplacer celui de base.

Pour tester modifier index.js de cette manière :

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import DrawerNavigator from './screens/DrawerNavigator'

export default DrawerNavigator

Vous remarquez qu'actuellement le seul moyen pour afficher le menu est en slidant de gauche à droite. Dans notre searchBar et notre navBar nous avons prevue un bouton permettant d'afficher le menu pour ce faire nous allons devoir modifier chacun des écrans pour ajouter l'action permettant de faire apparaitre le menu.

Pour faire apparaitre le menu le princcipe est le même que pour naviguer d'un écran à l'autre sauf qu'au lieu de spécifier le nom de la route nous indiquons d'ouvrir ou de fermer le menu.

Le code est le suivant :

this.props.navigation.navigate('DrawerOpen') // open drawer
this.props.navigation.navigate('DrawerClose') // close drawer

Dès lors que notre écran est utiliser dans un navigator (StakcNavigator,TabNavigator etc...) ce dernier récupère la propriété navigation.

Nous allons commencer par modifier l'écran Passwords.js dans le méthode openMenu replacer le log de console par :

this.props.navigation.navigate('DrawerOpen')

Ensuite pour les écrans Settings.js, Synchronization.js et InitSynchronization.js ajoutez la propriété suivante à la navBar :

actionLeft={() => this.props.navigation.navigate('DrawerOpen')}

StackNavigator

Pour terminer cet article nous allons ajouter le StackNavigator et finaliser la navigation au sein de l'application.

Modifiez le fichier index.js de la manière suivante :

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import { Platform } from 'react-native'
import { StackNavigator } from 'react-navigation'
import DrawerNavigator from './screens/DrawerNavigator'
import TabNavigator from './screens/TabNavigator'
import UnlockScreen from './screens/Unlock'
import SetupScreen from './screens/Setup'
import ReadOnlyScreen from './screens/ReadOnly'
import EditScreen from './screens/Edit'
import strings from './locales/strings'

const NestedNav = Platform.OS === 'android' ? DrawerNavigator : TabNavigator

const App = StackNavigator({
  Setup: {
    screen: SetupScreen,
    navigationOptions: () => ({
      title: strings.setup,
      headerStyle: { backgroundColor: '#01D88D' },
      headerTintColor: 'white',
    }),
  },
  Unlock: {
    screen: UnlockScreen,
    navigationOptions: () => ({
      header: null,
    }),
  },
  App: {
    screen: NestedNav,
    navigationOptions: () => ({
      header: null,
    }),
  },
  ReadOnly: {
    screen: ReadOnlyScreen,
    navigationOptions: ({ navigation }: Object) => ({
      title: navigation.state.params.siteName,
      headerStyle: { backgroundColor: '#01D88D' },
      headerTintColor: 'white',
    }),
  },
  Edit: {
    screen: EditScreen,
    navigationOptions: ({ navigation }: Object) => ({
      header: null,
    }),
  },
})

export default App

Ici premièrement nous avons importé Platform de React Native ainsi que le StackNavigator, ensuite on a importé l'ensemble de nos écrans y compris le TabNavigator ainsi que le DrawerNavigator.

Etant donnée que nous voulons utilisé le TabNavigator sur iOS et le DrawerNavigator sur Android nous utilisons Platform pour déterminer quel composant sera utilisé comme nestedNavigation de la manière suivante :

const NestedNav = Platform.OS === 'android' ? DrawerNavigator : TabNavigator

Enfin on définit l'ensemble des routes du StackNavigator, le principe est toujours similaire à ce que nous avons vue pour le TabNavigator et le DrawerNavigator. Seul les options de navigation sont différentes nous allons voir en détails les différences qu'il y a entre les routes :

Setup: {
 screen: SetupScreen,
 navigationOptions: () => ({
   title: strings.setup,
   headerStyle: { backgroundColor: '#01D88D' },
   headerTintColor: 'white',
 }),
},

Celle ci est la plus basique on définit le titre qui sera affiché sur la navBar ajoutée automatiquement par le StackNavigator ainsi que sa couleur et la couleur du titre.

Edit: {
  screen: EditScreen,
  navigationOptions: ({ navigation }: Object) => ({
    header: null,
  }),
},

Dans cet exemple on définit juste l'écran qui doit être utilisé et on indique que l'on ne souhaite pas avoir de navBar en ajoutant l'option header: null.

Ici nous ne voulons pas de navBar puisque nous l'avons déjà ajouté manuellement sur l'écran d'édition des mots de passe.

ReadOnly: {
  screen: ReadOnlyScreen,
  navigationOptions: ({ navigation }: Object) => ({
    title: navigation.state.params.siteName,
    headerStyle: { backgroundColor: '#01D88D' },
    headerTintColor: 'white',
  }),
},

Ici le principe est le même que sur le premier exemple à la différence que nous récuperons l'objet navigation dans les options de navigation.

Nous allons le voir un peu plus loin dans l'article mais lorsqu'on souhaite naviguer vers un écran il est possible de passer des paramètres supplémentaires à l'écran, le fait de récupérer l'objet navigation comme dans la route ci-dessus va nous permettre de pouvoir utiliser les paramètres passés lorsqu'on navigue vers l'écran. Dans notre cas cela nous permet d'utiliser le nom du site auquel est rattaché le mot de passe dans le titre de la navBar.

Navigation entres les écrans

Maintenant que nous avons mis en place le StackNavigator nous allons devoir modifié chacun de nos écrans afin d'ajouter les actions de navigation pour naviguer entre les différents écrans de notre application.

On commence par l'écran Setup pour commencer rajouter l'import suivant :

import { NavigationActions } from 'react-navigation'

Ensuite dans la fonction submit rajoutez le code suivant :

const resetAction = NavigationActions.reset({
  index: 0,
  actions: [NavigationActions.navigate({ routeName: 'Unlock' })],
})
this.props.navigation.dispatch(resetAction)

Habituellement le moyen le plus simple pour naviguer d'un écran à l'autre est celui-ci :

this.props.navigation.navigate('RouteName')

Cependant dans ce cas la l'application garde un historique de la navigation et permet de revenir à l'écran précédent mais dans notre application nous souhaitons qu'une fois que l'utilisateur a configuré son mot de passe principale il ne puisse pas revenir sur l'écran de configuration pour cela nous devons réinitialiser la navigation et donc utilisé une des différentes actions propose la librairie react-navigation qui est reset.

Pour plus d'information sur les différents types d'actions je vous invite à consulter la documentation de react-navigation.

Ensuite nous allons passer à l'écran de déverrouillage, ajoutez le code suivant dans la fonction submit du fichier Unlock.js :

this.props.navigation.navigate('App')

Maintenant passons à l'écrans de la liste des mots de passe ici nous allons effectué plusieurs modifications :

  1. Remplacer les logs dans la fonction addNewItem par :

this.props.navigation.navigate('Edit');

  1. Remplacer les logs dans la fonction showPassword par :
this.props.navigation.navigate('ReadOnly', { siteName: password.name })

Ici on passe en plus du nom de la route le paramètre qu'on utilise pour le titre de la navBar dans le stackNavigator.

Enfin il ne nous reste plus que l'écran ReadOnly ou il faut remplacer le codede la fonction editPassword par le code suivant :

this.props.navigation.navigate('Edit')

Nous en avons maintenant fini avec la navigation de l'application.

Vous pouvez retrouver le code source sur github.