Fala Samurai, beleza?

Aqui vamos nós com mais um tutorial para React Native.

Dessa vez nós iremos destrinchar outro caso de uso bastante requisitado para desenvolvimento mobile que é o uso de mapas e geolocalização.

Considerações iniciais

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

react-native init SamuraiMap

Para esse tutorial, a ideia é trazer uma tela com um mapa com uma View para nos informar a latitude e longitude atual e um botão que ao clicar fará uso da geolocalização para pegar a localização atual e marcar no mapa.

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

Imagem prévia da aplicação React Native/SamuraiMap - Dev Samurai

Preparando o layout

Nesse caso vamos trabalhar somente no arquivo App.js.

Como iremos precisar de um ícone para nosso layout, já vamos instalar 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

Para exibir o mapa, vamos utilizar a lib react-native-maps , então rode o comando:

yarn add react-native-maps

No caso do IOS essa lib pode utilizar o mapa providenciado nativamente.

Mas para o Android temos que utilizar o Google Maps.

Então caso esteja no IOS e queira utilizar o mapa nativo, pode pular essa próxima etapa Gerando uma API-KEY, mas se quiser utilizar o Google Maps no IOS, pode acompanhar.

Gerando uma API-KEY

Acesse o site https://cloud.google.com/console/google/maps-apis/overview, caso não tenha uma conta no Google Cloud Platform, vai precisar criar.

Depois, é preciso criar um novo projeto, pode chamar como quiser, ai dentro do projeto, no menu que pode ser acessado na lateral esquerda, procure por APIS e serviços, deverá chegar em uma tela parecida com essa:

Imagem exemplo painel do google cloud platform - Dev Samurai

Na opção Credentials, crie uma nova chave de api para todos os serviços e não precisa de por com restrições, COPIE ESSA API-KEY GERADA e cole em algum lugar para usar daqui a pouco.

Depois volte para a opção Painel.

Agora podemos clicar em + ATIVAR APIS E SERVIÇOS e depois ativar o Maps SDK for Android se estiver no Android ou Maps SDK for IOS caso esteja no IOS.

Utilizando o MapView

Voltando no projeto React Native.

Caso esteja em Android, vá em andoroid/app/src/main/AndroidManifest.xml e adicione isso dentro da tag application:

<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="SUA_API_KEY"/>

Vamos adicionar em nosso App.js um MapView com um Marker , ambos da lib react-native-maps :

import React, {useState} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import MapView, {Marker} from 'react-native-maps';
const App = () => {
const [position, setPosition] = useState({
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
});
return (
<View style={styles.container}>
<MapView
style={styles.map}
region={position}
onPress={e =>
setPosition({
...position,
latitude: e.nativeEvent.coordinate.latitude,
longitude: e.nativeEvent.coordinate.longitude,
})
}>
<Marker
coordinate={position}
title={'Marcador'}
description={'Testando o marcador no mapa'}
/>
</MapView>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
map: {
height: '100%',
width: '100%',
},
});
export default App;

O MapView tem suas props e eventos que estão documentados aqui: https://github.com/react-native-community/react-native-maps/blob/master/docs/mapview.md

No nosso app, utilizando a props region e o evento onPress para manipular um valor de state que chamamos de position , onde vai armazenar as coordenadas atuais do mapa, sendo os valores atuais, a região de San Francisco, apenas para nossa demonstração.

OBS: Se estiver utilizando o Google Maps no IOS, adicionar mais uma props no MapView , chamada provider com o valor de google

Rode o comando react-native run-android ou react-native run-ios .

OBS: Se o build falhar, acusando erro nessa linha:

dev_samurai/SamuraiMap/node_modules/react-native-maps/lib/android/build.gradle' line: 20

Acesse esse arquivo descrito e exatamente na linha acusada (no caso a 20), adicione:

def supportLibVersion = safeExtGet('supportLibVersion', '28.0.0')

A aplicação deve apresentar o mapa e o marcador:

Imagem mapa na aplicação React Native/SamuraiMap - Dev Samurai

Adicionando o logo, painel e botão

Agora que já exibimos o mapa, vamos adicionar os outros detalhes do layout.

import React, {useState} from 'react';
import {View, Text, TouchableOpacity, StyleSheet} from 'react-native';
import MapView, {Marker} from 'react-native-maps';
import Icon from 'react-native-vector-icons/MaterialIcons';
const App = () => {
const [position, setPosition] = useState({
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
});
return (
<View style={styles.container}>
<MapView
style={styles.map}
region={position}
onPress={e =>
setPosition({
...position,
latitude: e.nativeEvent.coordinate.latitude,
longitude: e.nativeEvent.coordinate.longitude,
})
}>
<Marker
coordinate={position}
title={'Marcador'}
description={'Testando o marcador no mapa'}
/>
</MapView>
<View style={styles.positonBox}>
<Text style={styles.positonBoxTitle}>Sua Localização</Text>
<View style={styles.positonBoxLatLon}>
<Text style={{fontSize: 18}}>Lat.</Text>
<Text style={{fontSize: 18}}>{position.latitude}</Text>
</View>
<View style={styles.positonBoxLatLon}>
<Text style={{fontSize: 18}}>Lon.</Text>
<Text style={{fontSize: 18}}>{position.longitude}</Text>
</View>
</View>
<TouchableOpacity style={styles.locationButton} onPress={() => {}}>
<Icon name="my-location" color={'#fff'} size={30} />
</TouchableOpacity>
<View style={styles.logo}>
<Text style={styles.logoText}>Samurai</Text>
<Text style={[styles.logoText, {color: '#e74c3c'}]}>Map</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
map: {
height: '100%',
width: '100%',
},
logo: {
backgroundColor: '#fff',
borderRadius: 15,
paddingHorizontal: 15,
elevation: 5,
marginTop: -730,
alignSelf: 'center',
marginRight: 10,
flexDirection: 'row',
},
logoText: {
fontWeight: 'bold',
fontSize: 22,
},
positonBox: {
backgroundColor: '#fff',
borderRadius: 20,
opacity: 0.75,
marginTop: -170,
marginHorizontal: 40,
padding: 25,
shadowColor: '#000',
elevation: 5,
},
positonBoxTitle: {
textAlign: 'center',
fontSize: 22,
fontWeight: 'bold',
color: '#e74c3c',
},
positonBoxLatLon: {flexDirection: 'row', justifyContent: 'space-between'},
locationButton: {
backgroundColor: '#e74c3c',
borderRadius: 150,
marginTop: -25,
width: 50,
height: 50,
alignSelf: 'center',
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#000',
elevation: 8,
},
});
export default App;

Com isso sua aplicação já deve estar com o layout igual a da prévia.

Agora falta adicionar a Geolocalização e colocar a função no botão.

Adicionando a Geolocalização

O sentido da geolocalização no nosso exemplo é para ao clicar a position do mapa receber a localização atual em que estamos.

Para isso vamos utilizar uma lib chamada react-native-geolocation-service , ela proverá funções para pegar a localização do GPS do celular, por isso, é preciso dar permissão para acessar a nossa localização.

yarn add react-native-geolocation-service

Se estiver no emulador, terá que habilitar o GPS do emulador e definir uma coordenada especifica para simular sua posição atual, no meu caso, como estou utilizando o emulador de Android no Genymotion, a tela de configuração do GPS, é assim:

GPS config no emulador do Genymotion - Dev Samurai

Caso esteja em Android, vá em andoroid/app/src/main/AndroidManifest.xml e adicione isso dentro da tag manifest:

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

Agora vamos importar o Geolocation da lib react-native-geolocation-service e utilizaremos o método getCurrentPosition para acessar nossa localização atual.

O método getCurrentPosition é assíncrono e podemos usar nesse formato:

Geolocation.getCurrentPosition(
pos => {
setPosition({
...position,
latitude: pos.coords.latitude,
longitude: pos.coords.longitude,
});
},
error => {
console.log(error);
Alert.alert('Houve um erro ao pegar a latitude e longitude.');
},
);

Iremos utilizar esse método no clique no botão para pegar nossa localização, mas precisamos verificar se o usuário deu permissão para acessar o GPS do celular, por isso, vamos criar um método que fará essa verificação e depois pega a posição atual:

import React, {useState} from 'react';
import {
PermissionsAndroid,
Alert,
View,
Text,
TouchableOpacity,
StyleSheet,
} from 'react-native';
import MapView, {Marker} from 'react-native-maps';
import Geolocation from 'react-native-geolocation-service';
import Icon from 'react-native-vector-icons/MaterialIcons';
const App = () => {
const [position, setPosition] = useState({
latitude: 37.78825,
longitude: -122.4324,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
});
const request_location_runtime_permission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'Permissão de Localização',
message: 'A aplicação precisa da permissão de localização.',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
Geolocation.getCurrentPosition(
pos => {
setPosition({
...position,
latitude: pos.coords.latitude,
longitude: pos.coords.longitude,
});
},
error => {
console.log(error);
Alert.alert('Houve um erro ao pegar a latitude e longitude.');
},
);
} else {
Alert.alert('Permissão de localização não concedida');
}
} catch (err) {
console.log(err);
}
};
return (
<View style={styles.container}>
<MapView
style={styles.map}
region={position}
onPress={e =>
setPosition({
...position,
latitude: e.nativeEvent.coordinate.latitude,
longitude: e.nativeEvent.coordinate.longitude,
})
}>
<Marker
coordinate={position}
title={'Marcador'}
description={'Testando o marcador no mapa'}
/>
</MapView>
<View style={styles.positonBox}>
<Text style={styles.positonBoxTitle}>Sua Localização</Text>
<View style={styles.positonBoxLatLon}>
<Text style={{fontSize: 18}}>Lat.</Text>
<Text style={{fontSize: 18}}>{position.latitude}</Text>
</View>
<View style={styles.positonBoxLatLon}>
<Text style={{fontSize: 18}}>Lon.</Text>
<Text style={{fontSize: 18}}>{position.longitude}</Text>
</View>
</View>
<TouchableOpacity
style={styles.locationButton}
onPress={() => {
request_location_runtime_permission();
}}>
<Icon name="my-location" color={'#fff'} size={30} />
</TouchableOpacity>
<View style={styles.logo}>
<Text style={styles.logoText}>Samurai</Text>
<Text style={[styles.logoText, {color: '#e74c3c'}]}>Map</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
map: {
height: '100%',
width: '100%',
},
logo: {
backgroundColor: '#fff',
borderRadius: 15,
paddingHorizontal: 15,
elevation: 5,
marginTop: -730,
alignSelf: 'center',
marginRight: 10,
flexDirection: 'row',
},
logoText: {
fontWeight: 'bold',
fontSize: 22,
},
positonBox: {
backgroundColor: '#fff',
borderRadius: 20,
opacity: 0.75,
marginTop: -170,
marginHorizontal: 40,
padding: 25,
shadowColor: '#000',
elevation: 5,
},
positonBoxTitle: {
textAlign: 'center',
fontSize: 22,
fontWeight: 'bold',
color: '#e74c3c',
},
positonBoxLatLon: {flexDirection: 'row', justifyContent: 'space-between'},
locationButton: {
backgroundColor: '#e74c3c',
borderRadius: 150,
marginTop: -25,
width: 50,
height: 50,
alignSelf: 'center',
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#000',
elevation: 8,
},
});
export default App;

Rode o comando react-native run-android ou react-native run-ios .

OBS: Pode ser que o build venha a falhar novamente, acusando erro nessa linha:

dev_samurai/SamuraiMap/node_modules/react-native-maps/lib/android/build.gradle' line: 20

Acesse esse arquivo descrito e exatamente na linha acusada (no caso a 20), adicione:

def supportLibVersion = safeExtGet('supportLibVersion', '28.0.0')

Depois só rodar o build novamente.

Agora ao clicar no botão para pegar a localização atual, a primeira vez deverá pedir o acesso e depois irá levar o marcador do mapa para a localização atual \O. Aplicação final, pegando minha localização atual:

Imagem final da aplicação React Native/SamuraiMap - Dev Samurai

Conclusão

Chegamos ao final de mais um tutorial de libs, ferramentas em React Native, nesse tutorial tivemos algumas configurações especiais e um problema de build que foi necessário lidar.

Essas nuances e problemas são perfeitamente normais na vida de um programador e principalmente quando lidamos com ferramentas open-source e que estão em constante atualização.

E como a intenção desses tutoriais e fazer o passo a passo definitivo, exatamente do jeito que nós aprendemos a utilizar as ferramentas aqui na Dev Samurai, também trazemos os problemas que tivemos.

A partir desses labs (como gosto de chamar esses testes de ferramentas) que aprendemos e é assim que depois saímos utilizando e criando aplicações mais complexas e robustas.

Espero que tenham gostado do tutorial e caso não tenham visto sobre utilização da câmera do celular com React Native, acessem aqui: React Native Tutorials: Usando a Câmera do Celular

E 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/12:tutorial-samuraimap

Valeu e até a próxima!