Neste post estarei compartilhando e documentando minha experiência durante o processo de criação de aplicativos Android e iOS utilizando React Native.
1) Configuração do ambiente
1.2) Quais as ferramentas de desenvolvimento?
- Visual Studio Code
- Extensões: React Native Tools e Expo Tools
- Genynobile Scrcpy (Para espelhar o smartphone no computador)
1.3) Criando um app em React Native (com Expo)
Criando o app
npx create-expo-app NomeApp
- O comando acima irá criar um o aplicativo utilizando o Expo.
Executando o app
cd NomeApp npx expo start
Observações:
- Instalar o app Expo Go (Android/iOS*)
- Ler o QR-Code gerado através do aplicativo Expo Go;
- *no iOS basta ler o QR-Code com o aplicativo de câmera;
1.4) Criando um app em React Native (por CLI)
Observações:
- Ambiente: Windows
- Dispositivo: Android
- Conectar o dispositivo Android no computador via cabo USB;
- No aparelho, habilitar o modo de desenvolvedor e permitir a depuração USB;
- Ao conectar o celular no computador, permitir a transferência de arquivos;
Criando o app
npx react-native@latest init NomeApp
- O comando acima irá criar todos os arquivos necessários para o app;
Iniciando o app
cd NomeApp npx react-native run-android
- O aplicativo será instalado automaticamente no aparelho Android
- Também é possível iniciar com Metro* separadamente com o comando:
npx react-native start
- *o Metro é muito parecido com o webpack (React para web) — só que p/ para apps — ele é um empacotador de JavaScript.
Atalhos
- Podemos personalizar os atalhos para executar o app em modo de desenvolvimento;
- Para isso, basta editar o arquivo
package.json.
"scripts": { "android": "react-native run-android", "ios": "react-native run-ios", "lint": "eslint .", "start": "react-native start", "dev": "react-native start --reset-cache", "test": "jest" },
- No exemplo acima foi adicionado o atalho
“dev”
com—reset-cache
.
- Assim podemos iniciar o modo de desenvolvimento com:
npm run dev
2) O básico
2.1) O que é o React Native?
- React Native é uma estrutura híbrida e de código aberto para criar apps para Android e iOS usando React;
- Para aprender React Native é necessário saber os fundamentos de Javascript, HTML e CSS.
2.2) Desenvolvimento Nativo x Híbrido
- Para criar apps nativos em Android utilizamos a linguagem Java ou Kotlin;
- Para criar apps nativos para iOS é necessário codificar em Swift ou Objective-C;
- O React Native é uma estrutura híbrida e utiliza a linguagem Javascript para criar as visualizações correspondentes do Android e iOS em tempo de execução;
2.3) Componentes principais
- O React Native possui muitos componentes principais, como por exemplo:
React Native | Android | iOS | Web (similaridade) |
<View> | <ViewGroup> | <UIView> | <div> |
<Text> | <TextView> | <UITextView> | <p> |
<Image> | <ImageView> | <UIImageImage> | <img> |
<ScrollView> | <ScrollView> | <UIScrollView> | <div> |
<TextInput> | <EditText> | <UITextField> | <input type="text"> |
2.3.1) Amostra das visualizações no Android x iOS
2.4) Fundamentos de React
- React Native é executado com base no React, por isso, é necessário saber os fundamentos de:
- 2.4.1) componentes
- 2.4.2) JSX
- 2.4.3) props
- 2.4.4) estado
2.4.1) Componentes
- Um componente é um elemento básico da interface do usuário;
- Podemos criar componentes de função (hooks com estado) e com classes (forma antiga);
- O React Native irá renderizar no aparelho do usuário o componente de acordo com o sistema operacional do dispositivo (iOS ou Android);
import React from 'react'; import {Text} from 'react-native'; const Cat = () => { return <Text>Hello, I am your cat!</Text>; }; export default Cat;
2.4.2) JSX
- JSX é uma extensão da sintaxe do JavaScript que permite escrever HTML dentro do JavaScript;
- Em JSX podemos misturar JavaScript e HTML (ou XML) em um único arquivo;
- Como JSX é JavaScript, podemos usar variáveis dentro dele;
- Qualquer expressão JavaScript funcionará entre chaves no JSX, como por exemplo:
import React from 'react'; import {Text} from 'react-native'; const getFullName = ( firstName: string, secondName: string, thirdName: string, ) => { return firstName + ' ' + secondName + ' ' + thirdName; }; const Cat = () => { return <Text>Hello, I am {getFullName('Rum', 'Tum', 'Tugger')}!</Text>; }; export default Cat;
2.4.3) Props
- Props é uma abreviação para propriedades;
- As propriedades permitem personalizar os componentes React, por exemplo:
import React from 'react'; import {Text, View} from 'react-native'; const Cat = props => { return ( <View> <Text>Hello, I am {props.name}!</Text> </View> ); }; const Cafe = () => { return ( <View> <Cat name="Maru" /> <Cat name="Jellylorum" /> <Cat name="Spot" /> </View> ); }; export default Cafe;
2.4.4) Estado
- Estado é a forma de armazenar dados de um componente;
- O estado fornece memória aos componentes;
- Ele é útil para lidar com dados que mudam com o tempo ou que vêm da interação do usuário;
- É possível adicionar estado a um componente React usando o hook
useState
;
useState
é um hook que permite adicionar estado aos componentes da função;
import React, {useState} from 'react'; import {Button, Text, View} from 'react-native'; const Cat = props => { const [isHungry, setIsHungry] = useState(true); return ( <View> <Text> I am {props.name}, and I am {isHungry ? 'hungry' : 'full'}! </Text> <Button onPress={() => {setIsHungry(false)}} disabled={!isHungry} title={isHungry ? 'Pour me some milk, please!' : 'Thank you!'} /> </View> ); }; const Cafe = () => { return ( <Cat name="Munkustrap" /> ); }; export default Cafe;
2.4.5) Fragmentos
- As tags “vazias”
<>
e</>
acima são bits do JSX chamadas de fragmentos.
- Fragmentos permitem envolver todo código sem precisar incluir um elemento extra e desnecessário, como uma
View
, por exemplo.
const Cafe = () => { return ( <> <Cat name="Munkustrap" /> <Cat name="Spot" /> </> ); };
2.5) Código específico Android/iOS
- Com React Native é possível implementar componentes visuais separados p/ Android e iOS;
- Há duas maneiras de separar o código por plataforma:
- 1) Usando o Platform Module;
- 2) Usando extensões de arquivos específicas;
Platform Module
- Essa opção é recomendada para pequenas partes de código;
- É possível usar a lógica de detecção para implementar o código específico da plataforma;
- Platform:
import {Platform, StyleSheet} from 'react-native'; const styles = StyleSheet.create({ height: Platform.OS === 'ios' ? 200 : 100, });
- Platform.select - pode ser:
'ios' | 'android' | 'native' | 'default'
import {Platform, StyleSheet} from 'react-native'; const styles = StyleSheet.create({ container: { flex: 1, ...Platform.select({ ios: { backgroundColor: 'red', }, android: { backgroundColor: 'green', }, default: { // outras plataformas, web por exemplo backgroundColor: 'blue', }, }), }, });
- Detectar a versão do Android:
import {Platform} from 'react-native'; if (Platform.Version === 25) { console.log('Running on Nougat!'); }
- Detectar a versão do iOS:
import {Platform} from 'react-native'; const majorVersionIOS = parseInt(Platform.Version, 10); if (majorVersionIOS <= 9) { console.log('Work around a change in behavior'); }
Extensões de arquivos específicas
- Recomendado para códigos específicos da plataforma mais complexos;
- Com essa opção é possível dividir o código em arquivos separados;
- Para utilizar esse opção é só criar o componente com o nome de cada plataforma, por exemplo:
BigButton.ios.js BigButton.android.js
- Depois, basta importar o componente e o React Native selecionará automaticamente o arquivo correto com base na plataforma em execução:
import BigButton from './BigButton';
3) Design
3.1) Style
- Todos os componentes principais aceitam a propriedade
style
- Os nomes dos estilos e valores correspondem à maneira como o CSS funciona na web;
- Porém os nomes são escritos no formato camelCase, por exemplo:
backgroundColor
ao invés debackground-color
;
- A medida que o componente cresce em complexidade é mais limpo usar
StyleSheet.create
import React from 'react'; import {StyleSheet, Text, View} from 'react-native'; const styles = StyleSheet.create({ container: { marginTop: 50, }, red: { color: 'red', }, }); const LotsOfStyles = () => { return ( <View style={styles.container}> <Text style={styles.red}>just red</Text> </View> ); }; export default LotsOfStyles;
3.2) Largura e altura (Width/Height)
- Todas as dimensões no React Native não precisam de unidade (px, rem, etc)
- As dimensões representam pixels independentes da densidade;
import React from 'react'; import {View} from 'react-native'; const FixedDimensionsBasics = () => { return ( <View> <View style={{ width: 100, height: 100, backgroundColor: 'skyblue', }} /> </View> ); }; export default FixedDimensionsBasics;
- Também é possível utilizar valores percentuais no estilo do componente
- As dimensões percentuais também requerem um elemento pai com o tamanho definido;
import React from 'react'; import {View} from 'react-native'; const PercentageDimensionsBasics = () => { return ( <View style={{height: '100%'}}> <View style={{ height: '10%', backgroundColor: 'powderblue', }} /> <View style={{ width: '66%', height: '90%', backgroundColor: 'skyblue', }} /> </View> ); }; export default PercentageDimensionsBasics;
3.3) Layout com Flexbox
- Por padrão todos os componentes do React Native são flex e direcionados por coluna;
- Na web o padrão é por linha (row), um ao lado do outro;
- Já no React Native, o padrão é por coluna (column), um em baixo do outro;
display: flex
flexDirection: column
- Para que um componente seja expandido e reduzido dinamicamente com base no espaço disponível podemos utilizar
flex: 1
;
Um componente só pode expandir para preencher o espaço disponível se seu pai tiver dimensões maiores que
0
.- Exemplo de layout com Flexbox:
import React from 'react'; import {View} from 'react-native'; const FlexDimensionsBasics = () => { return ( <View style={{flex: 1}}> <View style={{flex: 1, backgroundColor: 'powderblue'}} /> <View style={{flex: 2, backgroundColor: 'skyblue'}} /> <View style={{flex: 3, backgroundColor: 'steelblue'}} /> </View> ); }; export default FlexDimensionsBasics;
- Todas as outras propriedades do Flexbox estão disponíveis no React Native:
- Flex-Direction:
column
row
columnReverse
rowReverse
- Layout-Direction:
LTR
RTL
- Justify-Content:
flexStart
flexEnd
center
spaceBetween
spaceAround
spaceEvenly
- Align-Items:
stretch
flex-start
flex-end
center
baseline
- Align-Self e Align-Content
- Flex-Wrap, Flex-Basis, Grow e Shrink
- Row-Gap, Column-Gap e Gap
- Posição absoluta e relativa:
relative
absolute
3.4) Imagens
- O React Native fornece uma maneira unificada de gerenciar imagens e outros ativos de mídia em seus aplicativos Android e iOS;
Imagens Estáticas
- Para adicionar uma imagem estática, basta importar o componente
Image
import {Image} from 'react-native';
- Depois é só chamar o componente de imagem e inserir o caminho do arquivo:
<Image source={require('./src/assets/imagem.png')} />
Observações:
- Podemos usar os sufixos
@2x
e@3x
para fornecer imagens para diferentes densidades de tela.
└── img ├── check.png ├── check@2x.png └── check@3x.png
- E chamar a imagem normalmente:
<Image source={require('./img/check.png')} />
Imagens na Rede
- Também podemos exibir imagens que não estão no projeto
- Porém, ao contrário dos recursos estáticos, precisamos especificar as dimensões da imagem;
// CORRETO <Image source={{uri: 'https://reactjs.org/logo-og.png'}} style={{width: 400, height: 400}} /> // ERRADO <Image source={{uri: 'https://reactjs.org/logo-og.png'}} />
Imagens de fundo (Background-Image)
- Para exibir imagens de fundo podemos usar o componente
<ImageBackground>
;
- Ele tem as mesmas propriedades que o componente
<Image>
- Exemplo simples:
<ImageBackground source={...} style={{width: '100%', height: '100%'}}> <Text>Inside</Text> </ImageBackground>
- Exemplo completo com imagem na rede:
import React from 'react'; import {ImageBackground, StyleSheet, Text, View} from 'react-native'; const image = {uri: 'https://reactjs.org/logo-og.png'}; const App = () => ( <View style={styles.container}> <ImageBackground source={image} resizeMode="cover" style={styles.image}/> </View> ); const styles = StyleSheet.create({ container: { flex: 1, }, image: { flex: 1, justifyContent: 'center', }, }); export default App;
3.5) Cores
- Os componentes no React Native são estilizados usando Javascript;
- As propriedades de cores geralmente correspondem à forma como o CSS funciona na web;
Cores em Hexadecimal
- É possível inserir cores conforme o padrão da web que é hexadecimal, como por exemplo:
#fafafa
#000000
#000
(abreviado)
Cores em RGB
- React Native tem suporte para cores em
rgb()
ergba()
'#f0f'
(#rgb)'#ff00ff'
(#rrggbb)'#f0ff'
(#rgba)'#ff00ff00'
(#rrggbbaa)'rgb(255, 0, 255)'
'rgb(255 0 255)'
'rgba(255, 0, 255, 1.0)'
'rgba(255 0 255 / 1.0)'
Cores Nomeadas
- No React Native, também podemos usar strings de nomes de cores como valores;
transparent
(Um atalho parargba(0,0,0,0)
)aliceblue, coral, green, blue, etc...
- A lista completa de cores pode ser acessada aqui
Cores em HSL
- Também é possível usar
hsl()
ehsla()
: 'hsl(360, 100%, 100%)'
'hsl(360 100% 100%)'
'hsla(360, 100%, 100%, 1.0)'
'hsla(360 100% 100% / 1.0)'
Cores em Matriz (HWB)
- React Native suporta
hwb()
em notação funcional: 'hwb(0, 0%, 100%)'
'hwb(360, 100%, 100%)'
'hwb(0 0% 0%)'
'hwb(70 50% 0%)'
4) Gestos e Toques
- Com o React Native é possível lidar com todos os tipos de gestos comuns;
4.1) Botões
- Um botão é um componente básico que é renderizado em todas as plataformas;
Botão simples
- Um exemplo básico é o botão:
<Button onPress={() => { console.log('O botão foi pressionado!'); }} title="Toque aqui" color="#841584" />;
Componentes tocáveis (Touchables)
- Os componentes "tocáveis" podem capturar gestos de toque e exibir feedback;
<TouchableHighlight onPress={() => { console.log('O botão foi pressionado!'); }} underlayColor="white"> <View> <Text>TouchableHighlight</Text> </View> </TouchableHighlight>;
4.2) Navegação entre telas
- Aplicativos raramente são compostos de uma única tela.
- No React Native, a forma mais simples de criar a navegação entre telas e guias é usando o React Natigation;
React Navigation
- Primeiramente é preciso instalar a biblioteca:
npm install @react-navigation/native @react-navigation/native-stack
Dependências do React Navigation
- Se for um projeto React Native simples, instale as dependências com
npm
:
npm install react-native-screens react-native-safe-area-context
- Se for um projeto gerenciado pelo Expo, instale as dependências com
expo
:
npx expo install react-native-screens react-native-safe-area-context
Envolvendo a aplicação
- Agora precisamos envolver todo o aplicativo com o
NavigationContainer
.
- Isso é feito no arquivo de entrada de nosso projeto, como
index.js
ouApp.js
:
import * as React from 'react'; import {NavigationContainer} from '@react-navigation/native'; const App = () => { return ( <NavigationContainer> {/* Restante da aplicação */} </NavigationContainer> ); }; export default App;
Criando as telas
- Exemplo completo abaixo com 2 telas:
- 1) Tela inicial (HomeScreen)
- 2) Tela de perfil (ProfileScreen)
import * as React from 'react'; import {NavigationContainer} from '@react-navigation/native'; import {createNativeStackNavigator} from '@react-navigation/native-stack'; const Stack = createNativeStackNavigator(); const App = () => { return ( <NavigationContainer> <Stack.Navigator> <Stack.Screen name="Home" component={HomeScreen} options={{title: 'Welcome'}} /> <Stack.Screen name="Profile" component={ProfileScreen} options={{title: 'Profile'}} /> </Stack.Navigator> </NavigationContainer> ); }; export default App;
Navegando entre as telas
- Cada tela
<Stack.Screen>
tem uma propriedade chamada:component
;
- A propriedade
component
é uma referência a um componente React;
- Esses componentes recebem uma propriedade chamada
navigation
;
- O
navigation
possui vários métodos para vincular a outras telas;
Componente HomeScreen.jsx
const HomeScreen = ({navigation}) => { return ( <Button title="Go to profile screen" onPress={() => navigation.navigate('Profile', {name: 'Jane'}) } /> ); };
Componente ProfileScreen.jsx
const ProfileScreen = ({navigation, route}) => { return ( <Text> This is {route.params.name}'s profile </Text> ); };
5) Observações Expo
- Fluxos de trabalho no Expo
- Managed Workflow (padrão, sem acesso as pastas Android e iOS)
- Bare Workflow
- Development Builds
- Config Plugins
- Custom Clients
Parar mudar um fluxo Managed Workflow para Bare Workflow basta rodar o comando:
npx expo prebuild
Configurar Build
Gerar APK