React native e firebase
O Firebase vai funcionar como back-end da aplicação. Vamos começar criando um projeto através do console do Firebase.
Insira um nome de projeto e pressione para continuar.
Agora você será perguntado se deseja habilitar o Google Analytics para seu projeto. Não habilite e continue.
Clique em Criar Projeto
. Agora o Firebase alocará recursos para o projeto.
Quando terminar, pressione Continuar
para prosseguir para o painel do projeto. Agora, vamos configurar a autenticação.
Na barra de navegação lateral clique em Criação/Autenticação.
e será apresentada a seguinte tela e você deverá clicar em Primeiros passos
. Isso habilitará o módulo de autenticação:
Vamos utilizar a autenticação do Google. Clique em Google
e forneça o seu email. Veja abaixo como ficará a tela:
Você configurou com sucesso a autenticação do Google em seu projeto do Firebase.
Em uma etapa posterior vamos precisar da configuração associada a um app criado no console do Firebase. Clique no ícone de engrenagem na barra lateral e vá para as configurações do projeto.
Role a tela até aparecer o seguinte:
Clique no segundo ícone, que representa uma aplicação Android. Forneça um nome para o aplicativo e clique em Registrar aplicativo.
Após você deverá fazer o download do arquivo de configuração:
O seu arquivo google-services.json
será parecido com este:
{
"project_info": {
"project_number": "184594998406",
"project_id": "lista-de-tarefas-d0615",
"storage_bucket": "lista-de-tarefas-d0615.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:184594998406:android:9df3ac0ca5d0ff788a9d07",
"android_client_info": {
"package_name": "com.professorangoti.listadetarefas"
}
},
"oauth_client": [
{
"client_id": "184594998406-d9fmffc8tj8dgcev6qlm29q5e9issrtc.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyBCsJYBXCRdm35_JpeKFHAUdP5yGIKcVJ8"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "184594998406-d9fmffc8tj8dgcev6qlm29q5e9issrtc.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}
Ignore todas as outras etapas, faremos isso manualmente.
Vamos criar uma aplicação React native. Primeiro abra um terminal e use os seguintes comandos:
npx react-native init <nome da aplicação>
Precisamos renomear o pacote dentro da aplicação Android. Use o mesmo nome do pacote do Android informado no passo 2. Execute o seguinte comando dentro da pasta raiz do projeto:
cd <nome da aplicação>
npx react-native-rename "nome da aplicação" -b <nome do pacote>
O último comando abrirá o Visual Studio Code nesse diretório. Vamos limpar o código do arquivo App.js
, que ficará assim:
import React from 'react';
import {Header} from 'react-native/Libraries/NewAppScreen';
function App() {
return <Header />;
}
export default App;
Vamos criar agora um arquivo na raiz do projeto com o nome jsconfig.json
, com o seguinte código:
{
"compilerOptions": {
"checkJs": true,
"jsx": "react-native",
},
"exclude": ["node_modules", "**/node_modules/*"]
}
Em seguida, execute o seguinte comando para iniciar o aplicativo:
npx react-native run-android
O seu aplicativo exibirá a tela abaixo. Observe no canto superior direito da tela a mensagem de ativação do Hermes.
google-services.json
que obtivemos no passo 4 deve ser copiado para dentro de seu projeto no seguinte local: /android/app/./android/app/build.gradle
e adicione o seguinte código:dependencies {
...
// For animated GIF support
implementation 'com.facebook.fresco:animated-gif:2.6.0'
...
}
// Adicione esta linha no fim do arquivo
apply plugin: 'com.google.gms.google-services' // <--- this should be the last line
/android/build.gradle
:buildscript {
ext {
buildToolsVersion = "..."
minSdkVersion = ...
compileSdkVersion = ...
targetSdkVersion = ...
supportLibVersion = "..."
googlePlayServicesAuthVersion = "19.2.0" // <--- use this version or newer
}
dependencies {
...
classpath('com.google.gms:google-services:4.3.10') // <--- use this version or newer
...
}
cd android && ./gradlew signingReport
Isso gera duas chaves. Você precisa copiar as chaves 'SHA1' e 'SHA-256' que pertencem à variante 'debugAndroidTest
' e ‘Store
' indicando o diretório de sua aplicação.
Em seguida, você pode adicionar essas chaves às "impressões digitais do certificado SHA" em seu aplicativo no Firebase console. Veja sequencia de imagens a seguir:
Role a página até aparecer o seu aplicativo Android
Copie as chaves geradas no campo Impressão digital do certificado
.
yarn add @react-native-firebase/app @react-native-firebase/auth @react-native-firebase/firestore @react-native-firebase/storage @react-native-firebase/messaging
yarn add @react-native-google-signin/google-signin
yarn add @react-navigation/native react-native-screens react-native-safe-area-context @react-navigation/native-stack @react-navigation/bottom-tabs
yarn add react-native-paper
yarn add react-native-vector-icons
Edite o arquivo android/app/build.gradle
( Não é android/build.gradle ) e adicione o seguinte:
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
yarn add react-native-encrypted-storage
Vamos crias as seguintes pastas a partir da raiz do projeto:
E depois teremos a seguinte estrutura no nosso projeto
Normalmente o fluxo segue a sequência:
Vamos usar a renderização condicional para organizar o fluxo de autenticação:
<NavigationContainer> <Stack.Navigator> {state.isLoading ? ( <Stack.Screen name="Splash"> {props => <SplashScreen {...props} loaded={loaded} />} </Stack.Screen> ) : state.userToken == null ? ( // Usuário não autenticado <Stack.Screen name="SignIn"> {props => <SignInScreen {...props} sigIn={signIn} />} </Stack.Screen> ) : ( // Usuário autenticado <Stack.Screen name="Home"> {props => <HomeScreen {...props} signOut={signOut} />} </Stack.Screen> )} </Stack.Navigator> </NavigationContainer>
Vamos usar duas variáveis para controlar a autenticação:
isLoading
indica que o sistema está carregando o token de usuário armazenado no dispositivo ou recuperando o usuário.
userToken
armazena o token do usuário. Se é nulo indica que o usuário não está autenticado.
Estas variáveis serão parte do estado que a aplicação controla e temos também as funções que fazem a transição entre os estados possíveis. Abaixo p código que define o estado e as funções correspondentes.
const initialState = { isLoading: true, userToken: null, }; const [state, setState] = useState(initialState); const loaded = data => { if (data === '') { setState(prev => { return {...prev, isLoading: false}; }); } else { setState(prev => { return {isLoading: false, userToken: data}; }); } }; const signIn = data => { setState(prev => { return {...prev, userToken: data}; }); }; const signOut = () => { setState(prev => { return {isLoading: true, userToken: null}; }); };
O código final do arquivo App.js fica assim:
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createContext, useState } from 'react';
import SplashScreen from './src/screens/SplashScreen';
import SignInScreen from './src/screens/SignInScreen';
import HomeScreen from './src/screens/HomeScreen';
// @ts-ignore
export const AuthContext = createContext();
const Stack = createNativeStackNavigator();
export default function App() {
const initialState = {
isLoading: true,
userToken: null,
};
const [state, setState] = useState(initialState);
const loaded = data => {
if (data === '') {
setState(prev => {
return { ...prev, isLoading: false };
});
} else {
setState(prev => {
return { isLoading: false, userToken: data };
});
}
};
const signIn = data => {
setState(prev => {
return { ...prev, userToken: data };
});
};
const signOut = () => {
setState(prev => {
return { isLoading: true, userToken: null };
});
};
return (
<AuthContext.Provider value={{ state }}>
<NavigationContainer>
<Stack.Navigator>
{state.isLoading ? (
<Stack.Screen name="Splash">
{props => <SplashScreen {...props} loaded={loaded} />}
</Stack.Screen>
) : state.userToken == null ? (
// Usuário não autenticado
<Stack.Screen name="SignIn">
{props => <SignInScreen {...props} signIn={signIn} />}
</Stack.Screen>
) : (
// Usuário autenticado
<Stack.Screen name="Home">
{props => <HomeScreen {...props} signOut={signOut} />}
</Stack.Screen>
)}
</Stack.Navigator>
</NavigationContainer>
</AuthContext.Provider>
);
}
O valor a ser usado na configuração GoogleSignin.configure
você vai encontrar no console do Firebase:
import auth from '@react-native-firebase/auth';
import {
GoogleSignin,
GoogleSigninButton,
} from '@react-native-google-signin/google-signin';
import {useState} from 'react';
import {View} from 'react-native';
import {styles} from '../styles/styles';
GoogleSignin.configure({
webClientId:
'829208944695-43u8nvtkbtnufrm6pto7r7pk4do2u4a4.apps.googleusercontent.com',
});
export const logOut = async () => {
try {
await GoogleSignin.signOut();
} catch (error) {
console.error(error);
}
};
export const getCurrentUserInfo = async () => {
try {
const userInfo = await GoogleSignin.signInSilently();
return userInfo;
} catch (error) {
console.log(error);
}
};
function SignInScreen({signIn}) {
const [isSigninInProgress, setIsSigninInProgress] = useState(false);
async function onGoogleButtonPress() {
setIsSigninInProgress(true);
// Get the users ID token
const {idToken} = await GoogleSignin.signIn();
// Create a Google credential with the token
const googleCredential = auth.GoogleAuthProvider.credential(idToken);
// Sign-in the user with the credential
return auth().signInWithCredential(googleCredential);
}
return (
<View style={styles.container}>
<GoogleSigninButton
style={styles.button}
size={GoogleSigninButton.Size.Icon}
color={GoogleSigninButton.Color.Light}
onPress={() => onGoogleButtonPress().then(user => signIn(user.user))}
disabled={isSigninInProgress}
/>
</View>
);
}
export default SignInScreen;
Estrutura do objeto do usuário:
{"displayName": "Edson Angoti Junior", "email": "angoti@iftm.edu.br", "emailVerified": true, "isAnonymous": false, "metadata": {"creationTime": 1668626580750, "lastSignInTime": 1668626775914}, "multiFactor": {"enrolledFactors": [Array]}, "phoneNumber": null, "photoURL": "https://lh3.googleusercontent.com/a/ALm5wu0IJSCEz1Jb5RobScLVVfG3W-le4_WaoFL0xrhnUg=s96-c", "providerData": [[Object]], "providerId": "firebase", "tenantId": null, "uid": "wcwLaOIek1Tpk2xYWfxzVcyP07F3"}
import {useContext} from 'react'; import {AuthContext} from '../../App'; const {View, Text, Button, Image} = require('react-native'); const {styles} = require('../styles/styles'); const HomeScreen = ({signOut}) => { const {user} = useContext(AuthContext); return ( <View style={styles.container}> <Text style={styles.texto}>Tela principal</Text> <Text style={styles.texto}>{user.displayName}</Text> <Image source={{uri: user.photoURL}} style={{width: 200, height: 350}} resizeMode="contain" /> <Button title="Sair" onPress={() => signOut()} /> </View> ); }; export default HomeScreen;
import {ActivityIndicator, View} from 'react-native'; import {styles} from '../styles/styles'; import {getCurrentUserInfo} from './SignInScreen'; const SplashScreen = ({loaded}) => { getCurrentUserInfo().then(user => { loaded(user); }); return ( <View style={styles.container}> <ActivityIndicator size="large" /> </View> ); }; export default SplashScreen;