30 de Outubro de 2019 • 15 min de leitura
GoBarber - Aplicação Mobile com React Native
Minhas notas de aula e código do bootcamp da Rocketseat, nesse projeto criamos o aplicativo mobile em React Native que consome a API GoBarber
Vamos construir o GoBarber Mobile com React Native que vai consumir a API Rest Gobarber Backend em NodeJS, vamos controlar rotas privadas, fazer a autenticação JWT e receber um token de autenticação. A autenticação do usuário vai ficar guardada no Redux para sempre que precisarmos do usuário logado, ter acesso aos dados do profile. Ele é diferente do Go Barber Web pois nele não cadastramos os prestadores, apenas listamos os prestadores e fazemos o agendamento, esse é um app cliente, e o web é o provedor.
Aula 01 - Estrutura do projeto
Para começar vamos criar um projeto com React Native CLI, executando o comando:
react-native init gobarberRN
Depois pegamos as libs do Go Barber Web e as configurações e aplicamos para o Go Barber Mobile, veja no commit o que foi feito, é semelhante a tudo que já foi feito em outros tutoriais de React Native que já escrevi para estruturar uma aplicação.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-01-estrutura-projeto
Aula 02 - Configurando Root Import
Root Import
se você não sabe, é a ferramenta que permite fazemos imports de forma mais fácil, exemplo:
Como é:
import Card from '../../../../components/Card'
Como fica com root import:
import Card from '~/components/Card'
Ou seja fica muito mais fácil fazer o import, não precisamos saber onde está, a ferramenta faz isso pra gente, após uma prévia configuração.
Vamos instalar as duas libs necessárias para a configuração:
yarn add babel-plugin-root-import eslint-import-resolver-babel-plugin-root-import -D
No React Native temos acesso as configurações do Babel, no Create React App não, por isso fizemos outras configurações usando o react-app-rewired por exemplo.
Então no babel.config.js
adiciono um plugin `babel-plugin-root-import e informo a pasta raiz do código:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
[
'babel-plugin-root-import',
{
rootPathSuffix: 'src',
},
],
],
};
E no eslintrc.js
:
...
settings: {
'import/resolver': {
'babel-plugin-root-import': {
rootPathSuffix: 'src',
},
},
},
Crio também o arquivo jsconfig.json
que ajuda na importação dos arquivos, e para pode acessar os arquivos clicando no caminho da importação:
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"~/*": ["*"]
}
}
}
Pronto, só testar criando um componente qualquer e colocando dentro do App.js, a importação e o acesso ao arquivo devem funcionar.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-02-configurando-root-import
Aula 03 - Rotas de autenticação
Vamos configurar as rotas de autenticação, como foi feito no gobarber-react
Primeiro vamos adicionar as libs que gerenciam rotas:
yarn add react-navigation react-native-gesture-handler
Depois instalamos jetifier:
yarn add jetifier -D
Depois só rodar o comando:
yarn run jetify
Jetifify server para corrigir as bibliotecas. Ele conserta as libs para a arquitetura do AndroidX. (Talvez não precise mais disso na versão atual do React Native 0.61++)
Para o React Navigation funcionar precisamos configurar um arquivo Java do Android, conforme a documentação.
Após isso só executar o comando para o ios ou android, em qual vc estiver emulando durante o desenvolvimento:
react-native run-ios
react-native run-android
Agora vamos trabalhar com as rotas, crie um arquivo: routes.js
dentro do src
.
import {createAppContainer, createSwitchNavigator} from 'react-navigation';
import SignUp from './pages/SignUp';
import SignIn from './pages/SignIn';
export default createAppContainer(
createSwitchNavigator({
SignIn,
SignUp,
}),
);
E depois criamos um functional components SignIn e SignUp na pasta pages. E está configurando, veja os detalhes no código.
Na execução deve aparecer o texto SignIn no canto esquerdo do celular ou emulador.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-03-rotas-auth
Aula 04 - Configurando Background
Vamos começar a fazer a parte visual da aplicação.
Instalando a lib:
yarn add react-native-linear-gradient
Só rodar o comando para instalar essa biblioteca se estiver no macos e se estiver emulando no iOS, no Android não precisa fazer isso:
cd ios/ && pod install && cd..
Agora vamos criar o background:
Criamos a pasta src/components/Background/
e dentro o arquivo index.js
com a seguinte configuração:
import LinearGradient from 'react-native-linear-gradient';
import styled from 'styled-components/native';
export default styled(LinearGradient).attrs({
colors: ['#7159c1', '#ab59c1'],
})`
flex: 1;
`;
Legal bem simples, apenas estamos reestilizando um componente passando uma cor na primeira posição e uma segunda cor na segunda posição, que vai dar um efeito vertical de gradiente.
Esse componentes Background será usando em todas as telas.
Depois só aplicar no SignIn e no SignUp:
import React from 'react';
import {Text} from 'react-native';
// import { Container } from './styles';
import Background from '~/components/Background';
export default function SignIn() {
return (
<Background>
<Text>SignIn</Text>
</Background>
);
}
Deve aparecer uma tela muito bonita em gradiente no seu emulador/simulador ou celular.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-04-configurando-background
Aula 05 - Input & Button
Criei dois componentes globais, isolados, na pasta componentes.
Ambos serão usados no SignIn e no SignUp.
Detalhe que usei o forwardRef
do React para passar o ref para o componente TIInput e dar o foco manual via programação, que será feito nas próximas aulas.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-05-input-button
Aula 06 - Página SignIn
- Estilizei a página SignIn
- Detalhe que usei KeyboardAvoidingView ao invés da View, pois no iOS quando clico no input ele não joga o conteúdo para cima, ele sobrescreve o conteúdo de forma que não dá para ver o input. Então usei o KeyboardAvoidingView e use o Platform para habilitar o behavior padding desse componente. No Android não precisa, ele já faz isso por natureza, veja os detalhes na implementação
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-06-pagina-signin
Aula 07 - Página SignUp
- Basicamente só copiar e colar do SignIn e adicionar um campo e trocar os labels
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-07-pagina-signup
Aula 08 - Configurando StatusBar
Status Bar é a barra que fica no topo onde mostrar os status de bateria, wifi, relógio, e nesse caso estamos colocando a cor de fundo com a mesma da aplicação e o conteúdo na cor branca ao invés de preto que é o padrão.
import React from 'react';
import { StatusBar } from 'react-native';
import Routes from './routes';
export default function App() {
return (
<>
<StatusBar barStyle="light-content" backgroundColor="#7159c1" />
<Routes />
</>
);
}
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-08-configurando-statusbar
Aula 09 - Dicas de Acessibilidade
Aqui vai uma dica bem legal para otimizar o tempo do usuário e dar uma experiência legal.
No SignUp declaramos emailRef
e passwordRef
, criamos também a função handleSubmit :
import React, { useRef } from 'react';
...
const emailRef = useRef();
const passwordRef = useRef();
function handleSubmit() {}
...
Cada ref
desses vai para seu respectivo componente:
<FormInput
icon="person-outline"
autoCorrect={false}
autoCapitalize="none"
placeholder="Nome completo"
returnKeyType="next"
onSubmitEditing={() => emailRef.current.focus()}
/>
<FormInput
icon="mail-outline"
keyboardType="email-address"
autoCorrect={false}
autoCapitalize="none"
placeholder="Digite seu email"
returnKeyType="next"
ref={emailRef}
onSubmitEditing={() => passwordRef.current.focus()}
/>
<FormInput
icon="lock-outline"
secureTextEntry
placeholder="Sua senha secreta"
returnKeyType="send"
ref={passwordRef}
onSubmitEditing={handleSubmit}
/>
<SubmitButton onPress={handleSubmit}>Criar</SubmitButton>
Depois usamos a prop onSubmitEditing={() => emailRef.current.focus()}
para poder chamar o focus do próximo input. Nesse caso essa prop está no Input do Nome do usuário, e quando ele clica no teclado em next vai para o email, o email ganha o foco. A prop returnKeyType="next"
é o botão de next
ou próximo
que aparece no teclado (depende da configuração do seu idioma no celular)
No último input temos a prop: returnKeyType="send"
com o valor send
onde muda o comportamento, o qual chama a função que envia o formulário onSubmitEditing={handleSubmit}
. Logo o botão de de criar ou acessar acaba sendo desnecessário, se o usuário clicar no enviar que aparece no teclado, isso é um ganho de acessibilidade, claro que não dá para tirar o botão acessar ou criar da tela, pois às vezes o usuário não manja disso.
Para a tela de SignIn, foi feito a mesma coisa que descrito, veja o código.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-09-dicas-de-acessibilidade
Aula 10 - Reactotron
Na aula 05 desse post https://www.tgmarinho.com/introducao-ao-react-native/ explico cada um dos passos.
Veja o código abaixo no link.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-10-reactotron
Aula 11 - Configurando Redux
- Peguei toda a pasta
store
do gobarber-web e colei dentro desrc
. - Removi o history do sagas e também o toast, usei o Alert do React Native para exibir mensagens de feedback para o usuário
- Nas configurações alterei o
process.env.NODE
para__DEV__
- Grande benefício do Redux, reaproveitamos 99% da lógica da aplicação ao integrar com React Native o mesmo que estava funcionando na web com React.s
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-11-configurando-redux
Aula 12 - Conectando Redux
- Conectei com o Redux para poder fazer o login (sign in) e o signup de novos usuários, chamando as actions que são interceptados pelo saga que vai no servidor e efetua o procedimento de login e cadastro de usuário volta com os dados de sucesso do servidor e atualizam os reducers na store e mostram algo visual ou alertas para o usuário.
Veja o código.
No post https://www.tgmarinho.com/arquitetura_flux/ explico os mesmos conceitos.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-12-conectando-redux
Aula 13 - Rota inicial
- Depois do usuário logar ele deve ser redirecionado para uma nova rota (Dashboard).
Instalei a lib react-navigation-tabs
e react-native-reanimated
, e executei pod install
na pasta ios
Criei um arquivo pages/Dashboard/index.js
com um componente inicial usando o snippet: rnfc
, que vai ser o componente de Dashboard que o usuário vai ver após se logar na aplicação.
No arquivo routes.js
importei react-navigation-tabs
para poder usar o createBottomTabNavigator
Importei também o Dashboard.
Modifiquei a routes para exportar uma função que recebe um parâmetro, no caso isSigned
com o valor padrão false
.
E dentro do createAppContainer
criei dois grupos, primeiro Sign e o segundo App, o usuário será redirecionado para esse grupo se não estiver logado, se estiver logado vai para App, conforme a regra:
{ initialRouteName: isSigned ? 'App' : 'Sign', }
E assim vai acessar a rota que foi solicitada de dentro desse grupo, seja a rota SignIn ou SignUp our Dashboard se estiver logado.
Observe que nas telas de SignIn e SignUp estou usando createSwitchNavigator
pois não preciso de nenhum feedback visual nessas rotas. Agora na rota de Dashboard estou usando createBottomTabNavigator
que cria um botão no inferior da tela com o nome da rota (Dashboard) e tudo isso pode ser customizado, é o que iremos fazer na próxima aula. Legal que cada tela pode ter um tipo de navegação através desses agrupamentos.
- routes.js:
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
import { createBottomTabNavigator } from 'react-navigation-tabs';
import SignUp from './pages/SignUp';
import SignIn from './pages/SignIn';
import Dashboard from './pages/Dashboard';
export default (isSigned = false) =>
createAppContainer(
createSwitchNavigator(
{
Sign: createSwitchNavigator({
SignIn,
SignUp,
}),
App: createBottomTabNavigator({
Dashboard,
}),
},
{
initialRouteName: isSigned ? 'App' : 'Sign',
}
)
);
Criei um outro arquivo App.js
dentro da src
e o arquivo index.js
renomei o componente para Index
, fiz isso para poder importar o App dentro de index.js e no App.js poder acessar os reducers da store, para poder verificar se o usuário está logado para determinar o valor que será passado como parâmetro na função que cria a rota, lembrando que o arquivo routes agora recebe uma função e retorna outra função, ela se tornou um High Order Component, outro artigo., mais um post sobre HOC. No index.js da pasta src
importamos o App e trocamos pelos Routes, uma vez que o Routes foi para o arquivo App.js.
Basicamente esse lance entre o index.js e o App.js ocorreu pois não tinhamos como acessar o store de dentro do index.js onde estávamos passando o Provider, por isso separamos essas configurações, agora perceba que o App ficou dentro do Provider e dessa forma o App tem acesso aos reducers da store.
- App.js:
import React from 'react';
import { useSelector } from 'react-redux';
import createRouter from './routes';
export default function App() {
const signed = useSelector(state => state.auth.signed);
const Routes = createRouter(signed);
return <Routes />;
}
- index.js:
import './config/ReactotronConfig';
import React from 'react';
import { StatusBar } from 'react-native';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from './store';
import App from './App';
routes.js:
export default function Index() {
return (
<Provider store={store}>
<PersistGate persistor={persistor}>
<StatusBar barStyle="light-content" backgroundColor="#7159c1" />
<App />
</PersistGate>
</Provider>
);
}
Olhando o código abaixo no link, fica fácil pelos diffs ver a necessidade da alteração.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-13-rota-inicial
Aula 14 - Estilizações das rotas
- Estilizei as rotas Dashboard e Profile (criei esse novo componente), e estilizei as abas do menu que está no canto inferior da tela.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-14-estilizacao-rotas
Aula 15 - Lista de Agendamentos
- Criei uma Flatlist no Dashboard para mostrar os agendamentos (Appointments) e estilizei a FlatList e os items da FlatList criando um novo componente Appointment.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-15-lista-de-agendamentos
Aula 16 - Agendamentos da API
- Coloquei os dados da API na Flatlist
- Adicionei o método cancelar agendamento
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-16-agendamentos-api
Aula 17 - Atualização de perfil
- Criei, estilizei e atualizei os dados do Profile usando a API.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-17-atualizacao-perfil
Aula 18 - Logout
- Implementei o logout da aplicação.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-18-logout
Aula 19 - Rotas de agendamento
- Criei um outro agrupamentos de rotas chamado New que mostra na TabBar o ícone de + com o label 'Agendar', clicando nesse ícone ele vai para o processo de agendamentos. Nesse agrupamento contém três rotas de pilha para poder ir e voltar entre os passos de escolher fornecedor, escolher data e confirmar o agendamento.
- Criei os componentes SelectProvider, SelectDateTime e Confirm, porém não estilizei, vou fazer na implementação das próximas aulas.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-19-rotas-agendamento
Aula 20 - Listagem de Prestadores
- Estilizei o componente SelectProvider e usei navigation.navigate para ir para a próxima tela, com o provider selecionado.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-20-listagem-prestadores
Aula 21 - DatePicker por Plataforma
- Implementei o DatePicker tanto para Android quanto para o iOS.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-21-datepicker-por-plataforma
Aula 22 - Selecionando horário
- Implementei a chamada da API para buscar a disponibilidade (available) do provider na data selecionada e trazer os horários (disponíveis) para que o usuário selecione e seja enviado para a próxima tela (Confirmação).
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-22-selecionando-horario
Aula 23 - Confirmando Agendamento
- Implementei a estilização da confirmação de agendamento e seu comportamento, quando o usuario cai nessa rota, ele recebe o provider e o time como parâmetro, exibo na tela os dados e quando ele clica no botão confirmar é finalizado o fluxo e o agendamento é concluído. Para finalizar falta agora implementar a atualização de lista de agendamentos.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-23-confirmando-agendamento
Aula 24 - Load de Agendamentos
- Para finalizar implementei o load de agendamento, toda vez que o agendamento é finalizado o usuário é redirecionado para a página de Dashboard onde mostra os agendamentos, e para poder recarregar novamente a página usei o
withNavigationFocus
do react-navigation para que dispor da proisFocused
que sempre que a tela receber o foco essa variável recebetrue
então se fortrue
eu faço o load Novamente e passo ela como dependência do useEffect que faz a busca na API.
E também por fim usei no arquivo routes.js a prop: resetOnBlur: true,
que faz com que a pilha da navegação seja limpa, pois toda vez que eu clicava em agendar após um agendamento anterior, ele estava indo para a tela final, e eu queria começar do zero, com essa prop resolveu o problema.
Código: https://github.com/tgmarinho/gobarberRN/tree/aula-24-load-agendamento
Fim!
OBS: Ficou alguns detalhes de implementação que devo voltar a escrever sobre eles no futuro. Por enquanto é isso, veja as telas da aplicação.
Telas do aplicativo:
- Login:
- Cadastro:
- Perfil:
- Selecionar Prestar:
- Selecionar Horário:
- Confirmar Agendamento:
- Listagem de Agendamentos: