Chat com Flutter e Firebase | Parte 2 — Conectando nossa aplicação ao Firebase

Douglas Possas
5 min readJun 3, 2023

--

Nesse artigo, iremos conectar o app ao Firebase (acesse aqui a Parte 1). A idéia principal é não manter nossas credenciais no repositório. Para isso usaremos o pacote envied (documentação oficial) e variáveis de ambiente para manter as informações sensíveis seguras. Vamos lá!

Primeiro, é necessário que você crie seu projeto no Firebase e baixe os arquivos de configuração para cada Sistema Operacional. No nosso caso, estamos trabalhando apenas iOS (GoogleService-Info.plist) e Android (google-services.json). Não iremos adicionar esses arquivos no projeto e também não iremos usar a CLI do firebase para conectar o projeto e gerar arquivos de configuração (como demonstrado na documentação oficial).

Basicamente a gente precisa das seguintes informações:

  • API_KEY: Chave da nossa aplicação (essa informação muda para cada S.O.);
  • APP_ID: Identificador único de cada projeto (essa informação muda para cada S.O.);
  • SENDER_ID: Identificador do mensageiro que está conectando ao Firebase (informação idêntica para cada S.O.);
  • PROJECT_ID: Identificador do projeto Firebase (informação idêntica para cada S.O.).

Como as 2 primeiras propriedades mudam para cada S.O., teremos uma chave para cada sistema operacional em nosso arquivo de variáveis de ambiente. Vamos criá-lo agora na raiz do projeto. Ele deve ficar conforme a imagem a seguir:

arquivo .env
firebaseiOSApiKey=<API_KEY>
firebaseAndroidApiKey=client.api_key.current_key
firebaseAndroidAppId=client.client_info.mobilesdk_app_id
firebaseiOSAppId=<GOOGLE_APP_ID>
firebaseMessagingSenderId=<GCM_SENDER_ID> or project_info.project_number
firebaseProjectId=<PROJECT_ID> or project_info.project_info

Será necessário que cada propriedade seja preenchida conforme cada valor encontrado nos arquivos baixados anteriormente. Após preencher os valores, vamos criar a classe que representará os valores e será utilizada pela aplicação.

Crie o arquivo env.dart. Eu irei criá-lo em lib/core/utils/env.dart. Conforme a documentação oficial do pacote envied, iremos gerar uma propriedade estática para cada uma das informações que criamos em nosso arquivo de variaáveis de ambiente. Além dessas propriedades, iremos gerar 2 getters para buscar as propriedades de cada sistema operacional de forma dinâmica. Nossa classe terá a seguinte estrutura:

import 'dart:io';

import 'package:envied/envied.dart';

part 'env.g.dart';

@Envied()
abstract class Env {
/// On iOS this is represented as API_KEY within the GoogleService-Info.plist file
@EnviedField(varName: 'firebaseiOSApiKey')
static const String firebaseiOSApiKey = _Env.firebaseiOSApiKey;

/// On Android this is represened as client.client_info.api_key.current_key
/// within the google-services.json file
@EnviedField(varName: 'firebaseAndroidApiKey')
static const String firebaseAndroidApiKey = _Env.firebaseAndroidApiKey;

/// On iOS this is represented as API_KEY within the GoogleService-Info.plist file
@EnviedField(varName: 'firebaseiOSAppId')
static const String firebaseiOSAppId = _Env.firebaseiOSAppId;

/// On Android this is represened as client.client_info.mobilesdk_app_id
/// within the google-services.json file
@EnviedField(varName: 'firebaseAndroidAppId')
static const String firebaseAndroidAppId = _Env.firebaseAndroidAppId;

/// On iOS this is represented as GCM_SENDER_ID within the GoogleService-Info.plist file
/// On Android this is represened as project_info.project_number
/// within the google-services.json file
@EnviedField(varName: 'firebaseMessagingSenderId')
static const String firebaseMessagingSenderId =
_Env.firebaseMessagingSenderId;

/// On iOS this is represented as PROJECT_ID within the GoogleService-Info.plist file
/// On Android this is represened as project_info.project_info
/// within the google-services.json file
@EnviedField(varName: 'firebaseProjectId')
static const String firebaseProjectId = _Env.firebaseProjectId;

/// This information is different boetween OS
/// On iOS this is represented as API_KEY within the GoogleService-Info.plist file
/// On Android this is represened as client.client_info.mobilesdk_app_id
/// within the google-services.json file
static String get firebaseAppId =>
Platform.isIOS ? _Env.firebaseiOSAppId : _Env.firebaseAndroidAppId;

/// This information is different boetween OS
/// On iOS this is represented as API_KEY within the GoogleService-Info.plist file
/// On Android this is represened as client.client_info.api_key.current_key
/// within the google-services.json file
static String get firebaseApiKey =>
Platform.isIOS ? _Env.firebaseiOSApiKey : _Env.firebaseAndroidApiKey;
}

Para cada propriedade, deixei um comentário no código explicando onde conseguir cada valor (que deve ser preenchido no arquivo .env). A classe estará apresentando erros, mas não se preocupe, iremos resolver a seguir.

Como nossa classe criada eos valores preenchidos no arquivo de variáveis de ambiente, iremos rodar o build_runner para gerar o arquivo parcial env.g.dart ao qual nossa classe precisa para obter cada valor corretamente. Abra o terminal e navegue até a pasta do projeto e rode o comando a seguir:

flutter packages pub run build_runner build --delete-conflicting-outputs

O arquivo gerado terá a mesma estrutura a seguir, porém com cada propriedade devidamente preenchida.

Arquivo env.g.dart gerado através do build_runner e envied_generator

Agora podemos conectar a aplicação com o Firebase. Para isso vamos alterar o arquivo main.dart. É necessário nesse caso:

  • Tornar o método main assíncrono (através da assinatura async);
  • Garantir que nosso aplicativo só rode após nossas dependências terem sido inicializadas (através do comando WidgetsFlutterBinding.ensureInitialized());
  • Chamar o método de inicialização do Firebase passando as opções esperadas. Usaremos nossa class _ENV criada anteriormente.

Nosso método mais deve ficar da seguinte forma:

void main() async {
WidgetsFlutterBinding.ensureInitialized();

await Firebase.initializeApp(
options: FirebaseOptions(
apiKey: Env.firebaseApiKey,
appId: Env.firebaseAppId,
messagingSenderId: Env.firebaseMessagingSenderId,
projectId: Env.firebaseProjectId,
),
);

runApp(const MainApp());
}

Agora precisamos de uma classe que será responsável por escrever algo no Firestore (banco de dados) e que a gente possa validar o funcionamento destas configurações. Vamos criar uma classe de serviço chamada FirebaseService. Eu criei o arquivo no caminho lib/services/firebase_service.dart.

Ela terá apenas um método onde escreveremos uma entrada de testes. Então ela ficará assim:

import 'package:cloud_firestore/cloud_firestore.dart';

class FirebaseService {
final FirebaseFirestore firestore;

FirebaseService({required this.firestore});

void writeTestMessage() {
firestore
.collection('test_collection')
.add({'message': 'test successfully'});
}
}

Para testarmos se ocorreu tudo conforme o esperado, iremos alterar a classe MainApp transformando ela em um Stateful e chamando nossa classe de serviço no initState para que ela consiga escrever em nosso banco de dados. A classe MainApp deve ficar assim:

class _MainAppState extends State<MainApp> {
final _firebaseService =
FirebaseService(firestore: FirebaseFirestore.instance);

@override
void initState() {
_firebaseService.writeTestMessage();

super.initState();
}

@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Text('Hello World!'),
),
),
);
}
}

Acesse o Firebase Firestore e verifique se existe a collection 'test_collection' e se ela possui a entrada esperada.

Collection de testes criada e registro de sucesso escrito

Antes de enviar suas alterações para o repositório, garanta que os arquivos gerados via build_runner e também o arquivo de variáveis de ambiente não sejam enviados. Para isso, adicione-os ao .gitignore.

Arquivos .g.dart e .evn ignorados

Dessa forma garantimos que nossas credenciais não sejam expostas.

Nesse artigo a gente viu:

  • Como usar o pacote envied;
  • Criar o arquivo de variáveis de ambiente necessário para conectar ao Firebase (iOS e Android);
  • Manter nossas credenciais protegidas.

No próximo artigo iremos ciar a UI simples do Chat e começar a brincadeira =)

Me segue aí, compartilha e me ajude com sugestões de melhorias! Até a próxima!

--

--

Douglas Possas

Desenvolvedor desde 2005, amante de tecnologia desde os 5 (cinco) anos de idade quando ajudava a copiar um livro para compilar o Snake Game em um CP 400 color!