loading...
Cover image for React Native: Navegações complexas com React Navigation

React Native: Navegações complexas com React Navigation

oieduardorabelo profile image Eduardo Rabelo ・6 min read

Créditos da Imagem

Descobri que as pessoas podem se intimidar combinando diferentes navegadores no React Navigation para obter padrões de navegação mais "complexos". Hoje, quero guiá-lo brevemente por uma configuração de navegação mais complexa. Iremos falar de:

  • O Switch Navigator irá representar nosso status do aplicativo autenticado vs. não autenticado
  • Stack Navigator para navegação normal da direita para a esquerda em vários locais (telas de autenticação, pilha para cada aba, etc)
  • Stack Navigator para navegação de baixo para cima
  • Tab Navigator
  • Drawer Navigator

Antes de começar

No tutorial original, Spencer Carli não está usando a última versão do React Navigation, tomei a liberdade e atualizei os exemplos e irei utilizar versões fixas das dependências. Iremos usar o expo-cli, do mesmo modo que é recomendado na documentação do React Native.

Instale o expo-cli globalmente:

$ yarn global add expo-cli@3.2.3

Criaremos um novo projeto:

$ expo init ExemploNavegacoesComplexas

Iremos escolher blank na tela seguinte:

Navegue até a sua nova pasta:

$ cd ExemploNavegacoesComplexas

E iremos instalar as dependências necessárias para React Navigation:

$ expo install react-navigation@4.0.10 react-native-gesture-handler@1.3.0 react-native-reanimated@1.2.0 

Agora, iremos adicionar os pacotes de navegadores do React Navigation:

$ expo install react-navigation-tabs@2.5.5 react-navigation-stack@1.9.3 react-navigation-drawer@2.2.2
  • react-navigation-drawer: Para podermos criar Drawer Navigator.
  • react-navigation-stack: Para podermos criar Stack Navigator.
  • ‌react-navigation-tabs: Para podermos criar Tab Navigator

E agora podemos iniciar nossa aplicação:

$ yarn start

Vale notar que os conceitos explicados nesse artigo, podem ser portados para qualquer biblioteca de navegação.


Pré-requisitos

Antes de começarmos, adicionarei um Example.js para servir como uma tela para todas as nossas rotas (afinal, é apenas uma demonstração). Este componente gera uma cor de fundo aleatória e exibe todas as rotas disponíveis na tela atual:

// Example.js
import React from 'react';
import { View, TouchableOpacity, Text } from 'react-native';

const getAvailableRoutes = navigation => {
  let availableRoutes = [];
  if (!navigation) return availableRoutes;

  const parent = navigation.dangerouslyGetParent();
  if (parent) {
    if (parent.router && parent.router.childRouters) {
      // Grab all the routes the parent defines and add it the list
      availableRoutes = [
        ...availableRoutes,
        ...Object.keys(parent.router.childRouters),
      ];
    }

    // Recursively work up the tree until there are none left
    availableRoutes = [...availableRoutes, ...getAvailableRoutes(parent)];
  }

  // De-dupe the list and then remove the current route from the list
  return [...new Set(availableRoutes)].filter(
    route => route !== navigation.state.routeName
  );
};

const getRandomColor = () => {
  var letters = '0123456789ABCDEF';
  var color = '#';
  for (var i = 0; i < 6; i++) {
    color += letters[Math.floor(Math.random() * 16)];
  }
  return color;
};

const Example = ({ navigation }) => {
  return (
    <View
      style={{
        flex: 1,
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: getRandomColor(),
      }}
    >
      {getAvailableRoutes(navigation).map(route => (
        <TouchableOpacity
          onPress={() => navigation.navigate(route)}
          key={route}
          style={{
            backgroundColor: '#fff',
            padding: 10,
            margin: 10,
          }}
        >
          <Text>{route}</Text>
        </TouchableOpacity>
      ))}
    </View>
  );
};

export default Example;

Com isso feito, vamos começar.

Switch Navigator

Para alternar entre os diferentes "estados" da jornada de um usuário, usaremos um navegador switch para que o usuário não possa voltar atrás. Obviamente, teremos uma tela para a jornada principal do aplicativo. Também teremos um para usuários não autenticados.

Além disso, gosto de adicionar uma espécie de tela de Loading. Normalmente, isso não exibe nada - ela serve apenas para determinar se um usuário está autenticado ou não e encaminhá-lo para o lugar certo.


// App.js
import React from 'react';
import {
  createAppContainer,
  createSwitchNavigator,
} from 'react-navigation';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import { createDrawerNavigator } from 'react-navigation-drawer';
import { createStackNavigator } from 'react-navigation-stack';

import Example from './Example';

const App = createSwitchNavigator({
  Loading: {
    screen: Example,
  },
  Auth: {
    screen: Example,
  },
  App: {
    screen: Example,
  },
});

export default createAppContainer(App);

Stack Navigator de autenticação

Se um usuário não estiver autenticado, configuraremos um Stack Navigator para que ele saia de uma tela de início, entre, crie uma conta, esqueça a senha ou redefina a senha. As opções típicas que você vê quando precisa se autenticar.


// App.js

// ...

const AuthStack = createStackNavigator({
  Landing: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Landing',
    },
  },
  SignIn: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Sign In',
    },
  },
  CreateAccount: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Create Account',
    },
  },
  ForgotPassword: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Forgot Password',
    },
  },
  ResetPassword: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Reset Password',
    },
  },
});

const App = createSwitchNavigator({
  Loading: {
    screen: Example,
  },
  Auth: {
    screen: AuthStack,
  },
  App: {
    screen: Example,
  },
});

export default createAppContainer(App);

App Tabs

Quando o usuário estiver no aplicativo, usaremos guias para permitir que ele acesse os principais recursos do aplicativo - um feed, pesquisa e uma página de descobertas. Em seguida, substituiremos o item App em nosso navegador App pelo resultado da criação de nossas guias.

A saída da criação de qualquer navegador é apenas um componente para que possamos aninhá-los infinitamente no React Navigation.


// App.js

// ...

const MainTabs = createBottomTabNavigator({
  Feed: {
    screen: Example,
    navigationOptions: {
      tabBarLabel: 'Feed',
    },
  },
  Search: {
    screen: Example,
    navigationOptions: {
      tabBarLabel: 'Search',
    },
  },
  Discover: {
    screen: Example,
    navigationOptions: {
      tabBarLabel: 'Discover',
    },
  },
});

const App = createSwitchNavigator({
  Loading: {
    screen: Example,
  },
  Auth: {
    screen: AuthStack,
  },
  App: {
    screen: MainTabs,
  },
});

// ...

Stack Navigator para cada guia do App Tab

Assim como aninhamos o MainTabs em nosso navegador App, permitiremos que cada guia em nosso aplicativo tenha seu próprio stack navigator. Fazendo isso, significa que cada guia terá seu próprio estado, para que o usuário possa ir para a tela de detalhes de uma guia, mudar para outra e, ao voltar, poderá manter o mesmo estado para cada guia.

Além disso, com este exemplo, você pode ver que os navegadores obterão o nome da rota correspondente mais próxima. Isso significa que podemos reutilizar os nomes de tela e cada pilha apenas captura a tela Details mais próxima disponível, na pilha ou acima dela, na hierarquia do navegador.


// App.js

// ...

const FeedStack = createStackNavigator({
  Feed: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Feed',
    },
  },
  Details: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Details',
    },
  },
});

const SearchStack = createStackNavigator({
  Search: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Search',
    },
  },
  Details: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Details',
    },
  },
});

const DiscoverStack = createStackNavigator({
  Discover: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Discover',
    },
  },
  Details: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Details',
    },
  },
});

const MainTabs = createBottomTabNavigator({
  Feed: {
    screen: FeedStack,
    navigationOptions: {
      tabBarLabel: 'Feed',
    },
  },
  Search: {
    screen: SearchStack,
    navigationOptions: {
      tabBarLabel: 'Search',
    },
  },
  Discover: {
    screen: DiscoverStack,
    navigationOptions: {
      tabBarLabel: 'Discover',
    },
  },
});

// ...

App Drawer

Faremos o mesmo com o Drawer Navigator. Criaremos o navegador (também criaremos uma pilha para tela de configurações, dando um motivo criarmos o drawer) e renderizaremos isso como uma tela.

Desta vez, vamos substituir a renderização de MainTabs com MainDrawer e tornaremos nossas guias dentro do drawer. Construir essa hierarquia significa que estamos apenas adicionando mais navegadores, mas tudo o que já estava lá continuará funcionando.


// App.js

// ...

const SettingsStack = createStackNavigator({
  SettingsList: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Settings List',
    },
  },
  Profile: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Profile',
    },
  },
});

const MainDrawer = createDrawerNavigator({
  MainTabs: MainTabs,
  Settings: SettingsStack,
});

const App = createSwitchNavigator({
  Loading: {
    screen: Example,
  },
  Auth: {
    screen: AuthStack,
  },
  App: {
    screen: MainDrawer,
  },
});

// ...

Stack Navigator no estilo modal

Finalmente, queremos adicionar um navegador que se mova de baixo para cima e cubra qualquer outra tela. Isso significa que ele precisa estar na posição mais alta da nossa pilha (raiz/root). Se estiver na raiz, estará disponível para ser renderizado a partir de qualquer um de seus filhos.


// App.js

// ...

const AppModalStack = createStackNavigator(
  {
    App: MainDrawer,
    Promotion1: {
      screen: Example,
    },
  },
  {
    mode: 'modal',
    headerMode: 'none',
  }
);

const App = createSwitchNavigator({
  Loading: {
    screen: Example,
  },
  Auth: {
    screen: AuthStack,
  },
  App: {
    screen: AppModalStack,
  },
});

export default createAppContainer(App);

Código final do nosso navegador

E para finalizar, aqui está o nosso código ao final desse tutorial:

import React from 'react';
import {
  createAppContainer,
  createSwitchNavigator,
} from 'react-navigation';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import { createDrawerNavigator } from 'react-navigation-drawer';
import { createStackNavigator } from 'react-navigation-stack';

import Example from './Example';

const AuthStack = createStackNavigator({
  Landing: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Landing',
    },
  },
  SignIn: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Sign In',
    },
  },
  CreateAccount: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Create Account',
    },
  },
  ForgotPassword: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Forgot Password',
    },
  },
  ResetPassword: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Reset Password',
    },
  },
});

const FeedStack = createStackNavigator({
  Feed: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Feed',
    },
  },
  Details: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Details',
    },
  },
});

const SearchStack = createStackNavigator({
  Search: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Search',
    },
  },
  Details: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Details',
    },
  },
});

const DiscoverStack = createStackNavigator({
  Discover: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Discover',
    },
  },
  Details: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Details',
    },
  },
});

const MainTabs = createBottomTabNavigator({
  Feed: {
    screen: FeedStack,
    navigationOptions: {
      tabBarLabel: 'Feed',
    },
  },
  Search: {
    screen: SearchStack,
    navigationOptions: {
      tabBarLabel: 'Search',
    },
  },
  Discover: {
    screen: DiscoverStack,
    navigationOptions: {
      tabBarLabel: 'Discover',
    },
  },
});

const SettingsStack = createStackNavigator({
  SettingsList: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Settings List',
    },
  },
  Profile: {
    screen: Example,
    navigationOptions: {
      headerTitle: 'Profile',
    },
  },
});

const MainDrawer = createDrawerNavigator({
  MainTabs: MainTabs,
  Settings: SettingsStack,
});

const AppModalStack = createStackNavigator(
  {
    App: MainDrawer,
    Promotion1: {
      screen: Example,
    },
  },
  {
    mode: 'modal',
    headerMode: 'none',
  }
);

const App = createSwitchNavigator({
  Loading: {
    screen: Example,
  },
  Auth: {
    screen: AuthStack,
  },
  App: {
    screen: AppModalStack,
  },
});

export default createAppContainer(App);

Você pode encontrar o código final nesse repositório no GitHub. Aproveitei e separei cada exemplo em um commit separado, para você poder ver o que mudou em cada etapa.

https://github.com/oieduardorabelo/react-native-complex-navigation-with-react-navigation

GitHub logo oieduardorabelo / react-native-complex-navigation-with-react-navigation

📱React Native example for complex react-navigation setup

React Native: Complex navigation with React Navigation

Example project for my articles on:

All credits to Complex Navigation Example with React Navigation by Spencer Carli

changelog

Créditos

Posted on by:

oieduardorabelo profile

Eduardo Rabelo

@oieduardorabelo

☕ 🇳🇿 - https://eduardorabelo.me

Discussion

markdown guide