Fala Samurai, beleza?

Nós da equipe Dev Samurai temos o prazer de lançar essa nova série de tutoriais sobre React Native chamada React Native Tutorials.

Nessa série, iremos descrever o passo a passo e algumas técnicas para utilizar ferramentas e libs relacionadas a React Native, que foram aprendidas e utilizadas aqui no time de desenvolvimento da Dev Samurai.

Para dar inicio a essa série, vamos falar um pouco sobre como podemos utilizar a câmera do celular com React Native.

Link do Github: https://mautic.devsamurai.com.br/asset/11:tutorial-samuraicam

Criando o nosso aplicativo

Iremos partir de uma aplicação React Native do zero, gerada pelo:

react-native init SamuraiCam

Para esse tutorial, a ideia é trazer uma tela com 2 botões, 1 para fazer a utilização da câmera para capturar uma foto e outro para excluir a foto tirada que ficará exibida em um pequeno quadrinho.

Essa é uma prévia da aplicação:

Aplicativo SamuraiCam para testar a Câmera do React Native

De antemão, gostaria de esclarecer que a aplicação gerada no tutorial é um exemplo em que o foco principal é na instalação, configuração e uso de uma lib para React Native que vai auxiliar em acessar a câmera do celular e capturar fotos.

Preparando o Layout

Nosso layout vai ser simples, porém bonito =), e vai nos dar uma base para entender melhor o funcionamento do uso da câmera no React Native.

Vamos utilizar alguns ícones e um logo.

Para os ícones, instalaremos a lib react-native-vector-icons e faremos o link para utilizá-la:

yarn add react-native-vector-icons

react-native link react-native-vector-icons

o logo você pode baixar aqui: https://mautic.devsamurai.com.br/asset/10:logotipo-samurai-cam

Agora crie uma pasta chama src na raiz do projeto e dentro dela crie outra pasta chamada assets e dentro dela coloque as imagens do logo.

Arraste o arquivo inicial App.js do aplicativo gerado pelo react-native init para dentro da pasta src

Depois para que a mudança de lugar do arquivo principal App.js funcione corretamente, temos que mexer no arquivo index.js na raiz do projeto, altere a linha de código import App from './App'; para:

import App from './src/App';

Vamos começar a mexer no arquivo App.js adicionando uma View com nosso logo, os botões TouchableOpacity com seus Icons e o quadrinho para visualizar a foto posteriormente:

import React from "react";
import { View, Image, TouchableOpacity, StyleSheet } from "react-native";

import Icon from "react-native-vector-icons/MaterialIcons";

import logo from "./assets/logo.png";

const App = () => {
  return (
    <View style={styles.container}>
      <Image source={logo} style={styles.logo} />
      <View style={styles.photo}></View>
      <View style={styles.buttons}>
        <TouchableOpacity style={styles.button} onPress={() => {}}>
          <Icon name="camera-alt" size={40} color={"#f37272"} />
        </TouchableOpacity>
        <TouchableOpacity style={styles.button} onPress={() => {}}>
          <Icon name="delete" size={40} color={"#f37272"} />
        </TouchableOpacity>
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#f37272"
  },
  logo: {
    alignSelf: "center",
    marginTop: 60
  },
  photo: {
    width: 300,
    height: 200,
    backgroundColor: "#fff",
    alignSelf: "center",
    marginTop: 80
  },
  buttons: {
    marginTop: 10,
    flexDirection: "row",
    justifyContent: "center"
  },
  button: {
    backgroundColor: "#fff",
    margin: 20,
    borderRadius: 150,
    width: 80,
    height: 80,
    alignItems: "center",
    justifyContent: "center"
  }
});

export default App;

O resultado do app deve estar assim:

Layout Aplicativo SamuraiCam

Instalando e Configurando a Lib react-native-camera do React Native

Para acessar a câmera e capturar fotos vamos utilizar a lib react-native-camera

yarn add react-native-camera

Precisamos realizar alguns passos para configurar a lib e conseguir utiliza lá corretamente.

Primeira coisa é rodar o comando:

react-native link react-native-camera

Configurações para IOS

No arquivo ios/nome_do_projeto/info.plist adicione dentro da tag <dict> esse código:

<key>NSCameraUsageDescription</key>
<string>Teste de aplicativo.</string>

Configurações para Android

No arquivo android/app/build.gradle encontre a parte que tem as configurações default do android chamada defaultConfig e adicione a linha missingDimensionStrategy 'react-native-camera', 'general', seu código deve ficar parecido com esse:

defaultConfig {
        applicationId "com.reactnativecameraexample"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode 1
        versionName "1.0"
        missingDimensionStrategy 'react-native-camera', 'general'
    }

Depois vá até o arquivo android/app/src/main/AndroidManifest.xml e adicione essa linha:

<uses-permission android:name="android.permission.CAMERA" />

Utilizando a câmera

Com a estrutura do Layout pronta e a lib react-native-camera configurada, podemos prosseguir para utilizar a câmera.

Para isso nós iremos criar agora nosso componente Camera.

Primeiro vamos importar o componente RNCamera da lib react-native-camera para utilizar dentro de um Modal

import { RNCamera } from "react-native-camera";

O Modal é um componente do React Native que fica como se fosse empilhado acima do componente anterior, então vamos controlar a visibilidade desse modal com o valor de isCameraVisible no state do componente App:

import React, { useState } from "react";
import { Modal, View, Image, TouchableOpacity, StyleSheet } from "react-native";

import Icon from "react-native-vector-icons/MaterialIcons";
import { RNCamera } from "react-native-camera";

import logo from "./assets/logo.png";

const Camera = ({ isVisible, onChangePhoto, onCloseCamera }) => {
  const [camera, setCamera] = useState();

  return (
    <Modal animationType="slide" transparent={false} visible={isVisible}>
      <RNCamera
        ref={ref => setCamera(ref)}
        style={{ flex: 1 }}
        type={RNCamera.Constants.Type.back}
        autoFocus={RNCamera.Constants.AutoFocus.on}
        flashMode={RNCamera.Constants.FlashMode.off}
        androidCameraPermissionOptions={{
          title: "Permissão para usar a câmera",
          message: "Precisamos da sua permissão para usar a câmera.",
          buttonPositive: "Ok",
          buttonNegative: "Cancelar"
        }}
        captureAudio={false}
      >
        <Icon
          name="photo-camera"
          size={40}
          color={"#fff"}
          onPress={() => {}}
          style={styles.buttonTakePicture}
        />
        <Icon
          name="close"
          size={50}
          color={"#fff"}
          onPress={onCloseCamera}
          style={styles.buttonCloseCamera}
        />
      </RNCamera>
    </Modal>
  );
};

const App = () => {
  const [isCameraVisible, setIsCameraVisible] = useState(false);

  const onCloseCamera = () => {
    setIsCameraVisible(false);
  };

  return (
    <View style={styles.container}>
      <Image source={logo} style={styles.logo} />
      <View style={styles.photo}></View>
      <View style={styles.buttons}>
        <TouchableOpacity
          style={styles.button}
          onPress={() => {
            setIsCameraVisible(true);
          }}
        >
          <Icon name="camera-alt" size={40} color={"#f37272"} />
        </TouchableOpacity>
        <TouchableOpacity style={styles.button} onPress={() => {}}>
          <Icon name="delete" size={40} color={"#f37272"} />
        </TouchableOpacity>
      </View>
      <Camera isVisible={isCameraVisible} onCloseCamera={onCloseCamera} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#f37272"
  },
  logo: {
    alignSelf: "center",
    marginTop: 60
  },
  photo: {
    width: 300,
    height: 200,
    backgroundColor: "#fff",
    alignSelf: "center",
    marginTop: 80
  },
  buttons: {
    marginTop: 10,
    flexDirection: "row",
    justifyContent: "center"
  },
  button: {
    backgroundColor: "#fff",
    margin: 20,
    borderRadius: 150,
    width: 80,
    height: 80,
    alignItems: "center",
    justifyContent: "center"
  },
  buttonTakePicture: {
    flex: 0,
    alignSelf: "center",
    position: "absolute",
    bottom: 20
  },
  buttonCloseCamera: {
    flex: 0,
    position: "absolute",
    top: 20,
    right: 20
  }
});

export default App;

Ao clicar no botão para exibir a câmera então:

Camera no Aplicativo SamuraiCam

Perceba que definimos um valor no state do componente Camera para usar como referência para o componente RNCamera.

No RNCamera pudemos notar algumas configurações:

  • type: Tipo da câmera (front/back)
  • autoFocus: Uso o foco da câmera automaticamente.
  • flashMode: Uso do flash.
  • androidCameraPermissionOptions: Configuração da caixinha de mensagem para pedir permissão de uso da câmera do celular.
  • captureAudio: Captura de áudio (usado em gravações de video).

Agora precisamos implementar a função que vai capturar a foto, vamos chamá-lo de onTakePicture e ele irá usar a valor de camera que está referenciando o RNCamera chamando o método takePictureAsync.

Essa função irá chamar o método takePictureAsync e colocar o valor dele que no caso é a foto capturada em um valor photo que vamos declarar lá no componente App para poder usar no quadrinho de foto.

Fica assim então:

import React, { useState } from "react";
import {
  Alert,
  Modal,
  View,
  Image,
  TouchableOpacity,
  StyleSheet
} from "react-native";

import Icon from "react-native-vector-icons/MaterialIcons";
import { RNCamera } from "react-native-camera";

import logo from "./assets/logo.png";

const Camera = ({ isVisible, onChangePhoto, onCloseCamera }) => {
  const [camera, setCamera] = useState();

  const onTakePicture = async () => {
    try {
      const { uri } = await camera.takePictureAsync({
        quality: 0.5,
        forceUpOrientation: true,
        fixOrientation: true,
        skipProcessing: true
      });
      onChangePhoto(uri);
    } catch (error) {
      Alert.alert("Erro", "Houve um erro ao tirar a foto.");
    }
  };

  return (
    <Modal animationType="slide" transparent={false} visible={isVisible}>
      <RNCamera
        ref={ref => setCamera(ref)}
        style={{ flex: 1 }}
        type={RNCamera.Constants.Type.back}
        autoFocus={RNCamera.Constants.AutoFocus.on}
        flashMode={RNCamera.Constants.FlashMode.off}
        androidCameraPermissionOptions={{
          title: "Permissão para usar a câmera",
          message: "Precisamos da sua permissão para usar a câmera.",
          buttonPositive: "Ok",
          buttonNegative: "Cancelar"
        }}
        captureAudio={false}
      >
        <Icon
          name="photo-camera"
          size={40}
          color={"#fff"}
          onPress={onTakePicture}
          style={styles.buttonTakePicture}
        />
        <Icon
          name="close"
          size={50}
          color={"#fff"}
          onPress={onCloseCamera}
          style={styles.buttonCloseCamera}
        />
      </RNCamera>
    </Modal>
  );
};

const App = () => {
  const [isCameraVisible, setIsCameraVisible] = useState(false);
  const [photo, setPhoto] = useState(null);

  const onChangePhoto = newPhoto => {
    setPhoto(newPhoto);
    setIsCameraVisible(false);
  };

  const onCloseCamera = () => {
    setIsCameraVisible(false);
  };

  return (
    <View style={styles.container}>
      <Image source={logo} style={styles.logo} />
      <View style={styles.photo}></View>
      <View style={styles.buttons}>
        <TouchableOpacity
          style={styles.button}
          onPress={() => {
            setIsCameraVisible(true);
          }}
        >
          <Icon name="camera-alt" size={40} color={"#f37272"} />
        </TouchableOpacity>
        <TouchableOpacity style={styles.button} onPress={() => {}}>
          <Icon name="delete" size={40} color={"#f37272"} />
        </TouchableOpacity>
      </View>
      <Camera
        isVisible={isCameraVisible}
        onChangePhoto={onChangePhoto}
        onCloseCamera={onCloseCamera}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#f37272"
  },
  logo: {
    alignSelf: "center",
    marginTop: 60
  },
  photo: {
    width: 300,
    height: 200,
    backgroundColor: "#fff",
    alignSelf: "center",
    marginTop: 80
  },
  buttons: {
    marginTop: 10,
    flexDirection: "row",
    justifyContent: "center"
  },
  button: {
    backgroundColor: "#fff",
    margin: 20,
    borderRadius: 150,
    width: 80,
    height: 80,
    alignItems: "center",
    justifyContent: "center"
  },
  buttonTakePicture: {
    flex: 0,
    alignSelf: "center",
    position: "absolute",
    bottom: 20
  },
  buttonCloseCamera: {
    flex: 0,
    position: "absolute",
    top: 20,
    right: 20
  }
});

export default App;

O método onChangePhoto está atualizando o estado de photo com a foto capturada pela câmera e setada la no componente Camera.

Assim o valor de photo é a foto atual capturada, com isso, podemos exibir ela no nosso componente quadrinho.

Para isso vamos atualizar o componente ImageBackground para exibir a foto, esse componente é importado da API do React Native da mesma forma que o Text, View, etc.

E para o botão de deletar a imagem, só precisamos setar o valor de photo para null assim limpamos o quadrinho.

O código final, fica assim então:

import React, { useState } from "react";
import {
  Alert,
  Modal,
  View,
  Image,
  ImageBackground,
  TouchableOpacity,
  StyleSheet
} from "react-native";

import Icon from "react-native-vector-icons/MaterialIcons";
import { RNCamera } from "react-native-camera";

import logo from "./assets/logo.png";

const Camera = ({ isVisible, onChangePhoto, onCloseCamera }) => {
  const [camera, setCamera] = useState();

  const onTakePicture = async () => {
    try {
      const { uri } = await camera.takePictureAsync({
        quality: 0.5,
        forceUpOrientation: true,
        fixOrientation: true,
        skipProcessing: true
      });
      onChangePhoto(uri);
    } catch (error) {
      Alert.alert("Erro", "Houve um erro ao tirar a foto.");
    }
  };

  return (
    <Modal animationType="slide" transparent={false} visible={isVisible}>
      <RNCamera
        ref={ref => setCamera(ref)}
        style={{ flex: 1 }}
        type={RNCamera.Constants.Type.back}
        autoFocus={RNCamera.Constants.AutoFocus.on}
        flashMode={RNCamera.Constants.FlashMode.off}
        androidCameraPermissionOptions={{
          title: "Permissão para usar a câmera",
          message: "Precisamos da sua permissão para usar a câmera.",
          buttonPositive: "Ok",
          buttonNegative: "Cancelar"
        }}
        captureAudio={false}
      >
        <Icon
          name="photo-camera"
          size={40}
          color={"#fff"}
          onPress={onTakePicture}
          style={styles.buttonTakePicture}
        />
        <Icon
          name="close"
          size={50}
          color={"#fff"}
          onPress={onCloseCamera}
          style={styles.buttonCloseCamera}
        />
      </RNCamera>
    </Modal>
  );
};

const App = () => {
  const [isCameraVisible, setIsCameraVisible] = useState(false);
  const [photo, setPhoto] = useState(null);

  const onChangePhoto = newPhoto => {
    setPhoto(newPhoto);
    setIsCameraVisible(false);
  };

  const onCloseCamera = () => {
    setIsCameraVisible(false);
  };

  return (
    <View style={styles.container}>
      <Image source={logo} style={styles.logo} />
      <View style={styles.photo}>
        <ImageBackground
          style={{ width: "100%", height: "100%" }}
          source={{ uri: photo }}
        />
      </View>
      <View style={styles.buttons}>
        <TouchableOpacity
          style={styles.button}
          onPress={() => {
            setIsCameraVisible(true);
          }}
        >
          <Icon name="camera-alt" size={40} color={"#f37272"} />
        </TouchableOpacity>
        <TouchableOpacity
          style={styles.button}
          onPress={() => {
            setPhoto(null);
          }}
        >
          <Icon name="delete" size={40} color={"#f37272"} />
        </TouchableOpacity>
      </View>
      <Camera
        isVisible={isCameraVisible}
        onChangePhoto={onChangePhoto}
        onCloseCamera={onCloseCamera}
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#f37272"
  },
  logo: {
    alignSelf: "center",
    marginTop: 60
  },
  photo: {
    width: 300,
    height: 200,
    backgroundColor: "#fff",
    alignSelf: "center",
    marginTop: 80
  },
  buttons: {
    marginTop: 10,
    flexDirection: "row",
    justifyContent: "center"
  },
  button: {
    backgroundColor: "#fff",
    margin: 20,
    borderRadius: 150,
    width: 80,
    height: 80,
    alignItems: "center",
    justifyContent: "center"
  },
  buttonTakePicture: {
    flex: 0,
    alignSelf: "center",
    position: "absolute",
    bottom: 20
  },
  buttonCloseCamera: {
    flex: 0,
    position: "absolute",
    top: 20,
    right: 20
  }
});

export default App;

Conclusão

Utilizar a câmera do celular é uma tarefa bastante comum e requisitada para os desenvolvedores de aplicativos. Então aqui pudemos ter uma base de como fazer isso em React Native, utilizando um exemplo bem simples.

Agora deixa agente saber o que acho do conteúdo, por isso deixa aqui seu comentário, suas dúvidas e se deseja que façamos um exemplo mais elaborado como uma parte 2 desse tutorial onde poderemos incrementar a aplicação para gravar as fotos na pasta de imagens e acessar depois para listar a imagens capturadas, etc.

Não deixe de participar da nossa comunidade no Discord, lá podemos discutir sobre diversos assuntos relacionados a programação e os cursos do Dev Samurai, você vai poder encontrar com o pessoal da equipe Dev Samurai e também toda comunidade que participa e esta engajada em aprender a fazer aplicativos e discutir sobre programação, então segue o link:

Comunidade Dev Samurai no Discord.

Segue o link do repositório para você baixar o código: https://mautic.devsamurai.com.br/asset/11:tutorial-samuraicam

Valeu e até a próxima!