Alura > Cursos de Mobile > Cursos de > Conteúdos de > Primeiras aulas do curso React Native: integrando câmera nativa para capturar fotos

React Native: integrando câmera nativa para capturar fotos

Primeiros passos no Desbrava App - Apresentação

Olá, estudante da Alura! Boas-vindas a mais um curso de React Native.

Audiodescrição: Pedro Mello é um homem branco, com cabelos e barba escuros. No nariz, utiliza um piercing no septo e outro na narina direita. No corpo, veste uma camiseta preta. Ao fundo um ambiente branco iluminado em tons de rosa e azul, com quadros à esquerda e guitarras e uma estante cheia de figuras de ação à direita.

Conhecendo o projeto

Este curso está repleto de conteúdos interessantes. Vamos explorar o Desbrava App, um aplicativo voltado para registros de aventuras. Nossa missão será cadastrar um formulário de novas aventuras, permitindo que a pessoa usuária adicione uma foto através da câmera nativa do celular.

O que aprenderemos?

Vamos desenvolver tudo do zero. Receberemos o código, mas visitaremos toda a estrutura, desde a adição das permissões nativas, o desenvolvimento do formulário, até a captura de uma foto utilizando os emuladores. Faremos isso em conjunto, desde a instalação das dependências até a codificação e verificação do funcionamento completo.

Teremos tanto o simulador do iOS quanto do Android, pois estamos utilizando uma plataforma macOS que possibilita ter ambas as plataformas de emuladores. A tela em ambos é a mesma:

Interface inicial do aplicativo Desbrava App com fundo preto. No topo, está escrito 'Minhas Aventuras'. Abaixo, há um ícone de uma pessoa caminhando, em verde. O texto centralizado diz: 'Parece que você ainda não tem aventuras registradas, mas é sempre hora de começar!'. Há um botão verde fluorescente no centro inferior com a inscrição 'Adicionar aventura!'. Na parte inferior da tela, há um menu com ícones cinza escuro para 'Aventuras', que está destacado à esquerda, e 'Lembretes', à direita.

Clicando em "Adicionar aventura!", temos o formulário de aventuras e acesso à câmera.

'Formulário em tela escura com o título 'Adicionar aventura'. Há quatro campos de texto: 'Nome', 'Data' com um ícone de calendário à direita, 'Descrição', e 'Adicionar uma imagem' com um ícone de upload à direita. No canto inferior direito, um botão verde fluorescente com a palavra 'Adicionar'.'

No Android, ao clicar no campo "Adicionar uma imagem", acessamos uma tela com a simulação da câmera do dispositivo para registrar a foto e exibir; no iOS, não temos essa possibilidade pelo simulador.

Pré-requisitos

Vamos fazer isso do zero. Portanto, este curso pode requerer conhecimento prévio sobre como funciona o React e o React Native, incluindo aspectos como ScrollView e a necessidade de colocar o texto em volta de um componente de textos. Essas informações serão abordadas de forma superficial, focando na parte nativa e na configuração mais avançada.

Também é necessário conhecer o Expo e o TypeScript, utilizados neste curso para realizar as tipagens e tornar nosso código mais robusto durante a codificação.

Se você não tiver conhecimento nesses assuntos, não se preocupe! Temos esses conteúdos disponíveis na Alura para consulta, caso prefira assisti-los antes de mergulhar neste mundo do Desbrava App.

Próximos passos

Vamos começar sem muita demora, pois queremos iniciar e codificar este projeto. Vejo vocês na sequência. Vamos lá!

Primeiros passos no Desbrava App - Conhecendo o Desbrava App

Vamos explorar o aplicativo que a Desbrava App nos forneceu e identificar os pontos que precisamos trabalhar e implementar.

Analisando a estrutura e a configuração do projeto

Estamos com o editor de texto aberto no projeto do Desbrava App. Acessando oa aba do explorador de arquivos na lateral esquerda, abriremos o package.json na raiz do projeto.

Em seu interior, perceberemos que ele utiliza o Expo, pela forma como executa os scripts. Nas dependências, já estão implementadas funcionalidades de navegação, o Expo, o Gesture Handler e o React Native Paper. Este último traz componentes do Material UI convertidos para o React Native, já que o Material UI é voltado para web.

Explorando os arquivos, encontramos a pasta "assets" com os PNG do favicon e da logo do projeto. Dentro de "componentes", já temos o AppHeader implementado junto com a navegação. Observamos também as pastas "android" e "ios", indicando que o projeto utiliza a arquitetura Expo Bare Workflow, que consiste em ter acesso aos Expo Modules e às pastas nativas de ambas as plataformas.

Antes de começar, vale ressaltar que estamos utilizando um macOS, o que nos permite rodar emuladores de iOS e Android. Quem utilizar Linux ou Windows provavelmente terá acesso apenas ao Android, mas isso não impede o funcionamento do código em ambas as plataformas.

Comparando a aplicação no iOS e Android

Acessando o terminal integrado do editor de texto com o atalho "Ctrl + '", executaremos o comando abaixo para rodar no emulador do iOS:

npm run ios

Abriremos outra instância do terminal, clicando em "Split terminal" no canto direito, para executar o comando abaixo que roda no Android:

npm run android

Fazemos isso separadamente porque esse comando realiza um build do zero do projeto (essencial na primeira execução) ou após a instalação de novas bibliotecas. No iOS, o processo envolve a execução do pod, enquanto no Android, refaz os links.

Com a aplicação rodando no simulador do iOS, a tela "Minhas Aventuras" exibe um botão para adicionar aventura, que por enquanto não realiza nenhuma ação. O cabeçalho contém o título da tela e um botão de lupa, também sem funcionalidade no momento.

O bottom tab navigator (navegador de abas inferiores) permite navegar entre as telas "Lembretes" e "Aventuras". Em "Lembretes", não há a lupa de pesquisa e há um botão de adicionar lembrete sem a funcionalidade implementada.

No Android, notamos algumas diferenças de design: o título do cabeçalho é alinhado à esquerda por padrão, enquanto no iOS ele aparece centralizado. Além disso, no Android, o botão flutuante na parte inferior da tela mantém a cor preta, enquanto no iOS, ele se integra à cor da bottom tab.

Criando a tela para adicionar novas aventuras

Nosso objetivo agora é criar um formulário para adicionar novas aventuras, permitindo edição posterior e a possibilidade de anexar imagens. Esse recurso será útil para registrar momentos, como trilhas ou visitas a cachoeiras.

No editor de texto, fecharemos os arquivos abertos. No explorador lateral, dentro do caminho de pastas "src > screens", criaremos a pasta "AdventureForm" e o arquivo index.tsx em seu interior.

Acessando esse arquivo, começaremos com a estrutura básica abaixo, importando View, Text e React. Alteraremos o nome da constante para AdventureForm e o Text para "Adicionar aventura". Por fim, salvaremos o arquivo.

index.tsx:

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

const AdventureForm = () => {
    return (
        <View>
            <Text>Adicionar aventura</Text>
        </View>
    );
};

export default AdventureForm;

Próximos passos

Na sequência, cadastraremos essa tela na navegação do projeto. Até lá!

Primeiros passos no Desbrava App - Criando a navegação para o formulário

Discutiremos a navegação em nossa aplicação. Vamos acessar o arquivo src/navigation/index.tsx para analisar como está funcionando a navegação.

Compreendendo a estrutura de navegação

Observamos que já existe uma bottom tab criada, que pode ser visualizada no bloco export default function BottomTabNavigator(), que se estende por 74 linhas. Essa bottom tab é exibida na parte inferior do aplicativo, onde temos as seções "Aventuras" e "Lembretes".

index.tsx de "navigation":

export default function BottomTabNavigator() {
    {/* Código omitido */}
}

Abaixo desse trecho, temos a stack, que é o nosso AppRootNavigator, definindo que a tela principal é a tela de aventuras. O componente utilizado é o BottomTabNavigator, ou seja, a tela principal da stack é o nosso BottomTabNavigator.

const Stack = createStackNavigator();

export function AppRootNavigator() {
    return (
        <Stack.Navigator id={undefined} screenOptions={{ headerShown: false }}>
            <Stack.Screen name='Adventures' component={BottomTabNavigator} />
        </Stack.Navigator>
    );
}

A stack funciona de forma empilhada. Por exemplo, podemos imaginar duas paletas, onde uma é colocada sobre a outra. A rota inicial, representada pela paleta de baixo, é como o BottomTabNavigator. Se quisermos acessar outra rota a partir do BottomTabNavigator, seria como a paleta de cima.

Tipando a tela AdventureForm

Vamos iniciar a tipagem da tela AdventureForm. No início do arquivo, temos o bloco RootStackParamList, que identifica os parâmetros que podemos passar para nossa rota. Atualmente, está definido como undefined, pois não estamos passando nada específico para as telas de aventuras ou lembretes.

No entanto, para a tela AdventureForm, podemos passar um ID ou algo que facilite a identificação, especialmente quando o modo de edição estiver habilitado. Isso pode ser feito pelo lado do pageContext. Por enquanto, entre as chaves do RootStackParamList, adicionaremos AdventureForm: undefined.

export type RootStackParamList = {
    Adventures: undefined;
    Reminders: undefined;
    AdventureForm: undefined;
};

A partir desse momento, temos a rota AdventureForm dentro da nossa navegação. Ainda não haverá redirecionamento, pois não criamos o redirecionamento dentro da stack, mas já temos uma identificação.

Redirecionando a navegação

Para que o redirecionamento ocorra, acessaremos o return da função AppRootNavigator(), no final do arquivo. Em seu interior, abaixo da Stack.Screen Adventures, adicionaremos outra Stack.Screen com o nome AdventureForm, e o componente a ser renderizado será o AdventureForm.

export default function AppRootNavigator() {
    return (
        <Stack.Navigator id={undefined} screenOptions={{ headerShown: false }}>
            <Stack.Screen name='Adventures' component={BottomTabNavigator} />
            <Stack.Screen name='AdventureForm' component={AdventureForm} />
        </Stack.Navigator>
    );
}

Pode parecer confuso ver um Navigator dentro de outro. No momento da gravação deste vídeo, existem discussões sobre versões mais recentes do React Native com o Expo adotarem uma navegação semelhante ao Next.js, baseada em arquivos e estrutura de pastas. No entanto, neste projeto específico, não utilizamos essa arquitetura de navegação por pastas, comum em Next.js e outros frameworks voltados para React.

No React Native com o Expo que estamos utilizando, precisamos usar o React Navigation para lidar com as rotas.

Ajustando o arquivo app.tsx

Vamos remover o export default do BottomTabNavigator, por volta da linha 24, e colocar o export default no nosso AppRootNavigator.

function BottomTabNavigator() {
    {/* Código omitido */}
}
export default function AppRootNavigator() {
    {/* Código omitido */}
}

A alteração na parte de navegação é essa. Precisamos apenas alterar o App.tsx.

No interior desse arquivo, o MyComponent está utilizando o export default do Navigation, que era do nosso BottomTabNavigator. Na raiz do nosso projeto, em vez de MyComponent, vamos importar AppRootNavigator. Na linha que utiliza o MyComponent, faremos essa troca para melhorar a organização do código.

import AppRootNavigator from './src/navigation';

export default function App() {
    return (
        <NavigationContainer theme={DarkTheme}>
            <StatusBar style='light' />
            <AppRootNavigator />
        </NavigationContainer>
    );
}

Como esse código veio da Desbrava para que pudéssemos fazer as alterações, estamos aprimorando dentro das possibilidades para o nosso projeto.

Testando a navegação no simulador

Para testar essa navegação, vamos acessar as pastas "screens > Adventures" e o arquivo index.tsx. Vamos importar o hook de navegação chamado useNavigation. Passaremos a tipagem do RootStackNavigationProp para garantir que a navegação ocorra apenas para rotas existentes.

Em seguida, faremos a alteração no botão para testar se resolvemos o problema de navegação. Atualmente, ao abrir ambos os simuladores, a navegação não ocorre, mesmo já tendo adicionado a tela na stack.

Na primeira linha dentro das chaves do bloco const Adventures = () => {}, declararemos const navigation = useNavigation. Vamos adicionar os sinais <> para fazer a tipagem RootStackNavigationProp.

index.tsx de "Adventures":

const Adventures = () => {
    const navigation = useNavigation<RootStackNavigationProp>();
    
    {/* Código omitido */}
}

Faremos a importação do useNavigation de @react-navigation/native e a importação da tipagem da NavigationProps do nosso RootStack.

import { useNavigation } from '@react-navigation/native';
import { RootStackNavigationProp } from '../../navigation';

No bloco const Adventures = () => {} vamos alterar o onPress do Button, substituindo o par de chaves da arrow function pelo navigation.navigate(). Clicando entre seus parênteses, o editor de texto já identifica, através da tipagem, que as rotas disponíveis para navegação na stack são AdventureForm, Adventures e Reminders. Após clicar em AdventureForm, salvaremos a tela.

<Button
    mode='contained'
    onPress={() => navigation.navigate('AdventureForm')}
    style={{ marginTop: 16, backgroundColor: colors.primary }}
    textColor={colors.black}
>
    Adicionar aventura!
</Button>

Realizando ajustes visuais

Vamos conferir nos emuladores, começando pelo Android. Ao clicar em "Adicionar aventura!", a navegação ocorre. No iOS, ocorre o mesmo.

Para visualizar o conteúdo em tela, já que a fonte está na cor preta e o fundo também é preto, vamos adicionar um estilo. Acessando o arquivo index.tsx da pasta "AdventureForm", definiremos a cor do Text como colors.surface, o que já dará uma diferença.

index.tsx de "AdventureForm":

<View>
    <Text style={{ color: colors.onSurface }}>Adicionar aventura</Text>
</View>

Faremos a importação pelos nossos tokens de cor.

import { colors } from '../../styles/colors';

No simulador, observaremos que o "Adicionar aventura!" está no topo da aplicação, sendo difícil de ver. Para resolver isso, adicionaremos um AppHeader da nossa aplicação acima da View do texto, passando a propriedade title como "Adicionar aventura" e um showBackButton.

Por fim, adicionaremos um fragment envolvendo e aninhando todos esses componentes.

<>
    <AppHeader title='Adicionar aventura' showBackButton />
    <View>
        <Text style={{ color: colors.onSurface }}>Adicionar aventura</Text>
    </View>
</>

Voltando aos emuladores, tudo está funcionando corretamente. O texto "Adicionar aventura" voltará a ser exibido no local correto.

Próximos passos

A navegação já está funcionando para ir e voltar entre as telas. A seguir, discutiremos as diferenças entre os componentes de header e criaremos os componentes de botão e outros elementos.

Sobre o curso React Native: integrando câmera nativa para capturar fotos

O curso React Native: integrando câmera nativa para capturar fotos possui 126 minutos de vídeos, em um total de 47 atividades. Gostou? Conheça nossos outros cursos de em Mobile, ou leia nossos artigos de Mobile.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas