A autenticação é a porta de entrada para a maioria dos aplicativos modernos. Proteger rotas, personalizar a experiência do usuário e garantir a segurança dos dados são tarefas fundamentais. Felizmente, o Firebase Authentication simplifica drasticamente esse processo.
Neste guia, vamos construir um aplicativo Flutter simples com uma tela de login (usando email e senha) e uma tela inicial que só pode ser acessada após a autenticação. O foco é criar uma base sólida e fácil de entender para que você possa implementar em seus próprios projetos.
Pré-requisitos
O ambiente necessário é muito parecido com o de outros projetos Flutter/Firebase:
- O SDK do Flutter instalado e configurado no seu PATH.
- Suporte para a plataforma desejada ativado (Android, iOS, Web, Windows, etc.). Verifique com o comando
flutter doctor
. - Uma conta Google para acessar o Firebase.
- Node.js e npm instalados (necessários para a CLI do Firebase).
Passo 1: Preparando o Ambiente Firebase
Se você já usa a CLI do Firebase, pode pular esta etapa.
Instale a CLI globalmente via npm:
npm install -g firebase-tools
Faça login na sua conta Google para autenticar sua máquina:
firebase login
Isso abrirá uma janela no seu navegador para você completar o login.
Passo 2: Configurar o Projeto no Console do Firebase
- Acesse o Console do Firebase.
- Clique em “Adicionar projeto” e dê um nome a ele (ex:
flutter-auth-example
). - No menu à esquerda, vá em Build > Authentication. Esta é a seção do Firebase dedicada ao gerenciamento de usuários. Ela oferece um serviço de backend completo, SDKs fáceis de usar e bibliotecas de UI prontas para autenticar usuários no seu aplicativo. Com isso, você não precisa se preocupar em criar e manter seu próprio servidor de autenticação.
- Clique no botão “Get started”.
- Na aba “Sign-in method”, você verá uma lista de provedores de autenticação. O Firebase é extremamente flexível, oferecendo métodos como login com contas Google, Facebook, Apple, GitHub, além de opções como login anônimo ou por número de telefone. Cada um serve a um propósito diferente. Para manter este guia focado e simples, selecione “E-mail/senha”, que é o método de login mais tradicional e uma ótima base para começar.
- Ative a opção e clique em “Salvar”.
- Ainda na aba de Autenticação, vá para a aba “Users” e clique em “Add user”. Crie um usuário de teste com um email e senha para que possamos testar nosso login mais tarde.
Passo 3: Criando o Projeto Flutter
Vamos criar um novo projeto Flutter do zero.
Abra seu terminal e execute o comando:
flutter create flutter_auth_example
Navegue para dentro da pasta do projeto:
cd flutter_auth_example
Passo 4: Adicionar as Dependências no Flutter
Precisamos de dois pacotes principais para este projeto: o firebase_core
para inicializar a conexão e o firebase_auth
para cuidar da autenticação.
- Na raiz do seu projeto, execute o comando:
flutter pub add firebase_core firebase_auth
Passo 5: Conectar o App ao Firebase com FlutterFire
A CLI do FlutterFire é a ferramenta que conecta seu código Flutter ao projeto que criamos no console do Firebase.
Se ainda não tiver, instale a CLI do FlutterFire:
dart pub global activate flutterfire_cli
Atenção: Se o terminal avisar que o diretório
Pub\Cache\bin
não está no seu “Path”, siga as instruções do aviso para adicioná-lo às suas variáveis de ambiente e reinicie o terminal.Na raiz do seu projeto, execute o comando de configuração:
flutterfire configure
A ferramenta irá listar seus projetos Firebase. Selecione o
flutter-auth-example
que criamos. Em seguida, escolha as plataformas para as quais você quer configurar o app (ex: android, ios, web). Ao final, o arquivolib/firebase_options.dart
será gerado automaticamente.
Passo 6: O Código do Aplicativo
Agora, vamos ao código! Para uma melhor organização, vamos dividir nossa lógica em três arquivos. Crie uma nova pasta pages
dentro da sua pasta lib
.
1. Crie o arquivo lib/pages/login_page.dart
Este arquivo conterá exclusivamente o widget da nossa tela de login. O StatefulWidget
é usado aqui porque precisamos gerenciar o estado do formulário (o que o usuário digita) e o estado de carregamento (_isLoading
).
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
// Tela de Login
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
bool _isLoading = false;
// Função para lidar com o processo de login
Future<void> _login() async {
setState(() {
_isLoading = true;
});
try {
// Tenta fazer login com email e senha
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text.trim(),
);
// Se o login for bem-sucedido, o StreamBuilder no AuthWrapper
// irá reconstruir e levar o usuário para a HomePage.
} on FirebaseAuthException catch (e) {
// Mostra um erro para o usuário se o login falhar
final snackBar = SnackBar(
content: Text('Erro ao fazer login: ${e.message}'),
backgroundColor: Colors.red,
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
} finally {
// Garante que o estado de loading seja desativado
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Login')),
body: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.lock, size: 80, color: Colors.blue),
const SizedBox(height: 20),
TextField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.email),
),
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 16),
TextField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Senha',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.vpn_key),
),
obscureText: true,
),
const SizedBox(height: 24),
// Mostra o botão ou o loader
_isLoading
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: _login,
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 50,
vertical: 15,
),
),
child: const Text('Entrar'),
),
],
),
),
),
);
}
}
2. Crie o arquivo lib/pages/home_page.dart
Este arquivo conterá o widget da tela principal. Ele é um StatelessWidget
porque seu único trabalho é exibir informações (o email do usuário) que ele recebe de fora, sem gerenciar nenhum estado interno complexo.
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
// Tela Home, exibida após o login
class HomePage extends StatelessWidget {
final User user;
const HomePage({super.key, required this.user});
// Função para fazer logout
Future<void> _logout() async {
await FirebaseAuth.instance.signOut();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Página Inicial'),
actions: [
IconButton(
icon: const Icon(Icons.logout),
onPressed: _logout,
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Bem-vindo!',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
Text(
user.email ?? 'Email não disponível',
style: const TextStyle(fontSize: 18),
),
],
),
),
);
}
}
3. Substitua o conteúdo do lib/main.dart
Agora, nosso arquivo principal fica mais limpo. Sua principal responsabilidade é inicializar o Firebase e usar o AuthWrapper
para decidir qual tela (Login ou Home) deve ser mostrada ao usuário com base em seu status de autenticação.
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'firebase_options.dart'; // Importa as configurações
import 'pages/home_page.dart'; // Importa a HomePage
import 'pages/login_page.dart'; // Importa a LoginPage
// Ponto de entrada da aplicação
void main() async {
// Garante que os widgets do Flutter estão prontos
WidgetsFlutterBinding.ensureInitialized();
// Inicializa o Firebase
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Firebase Auth',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
// O AuthWrapper decide qual tela mostrar
home: const AuthWrapper(),
debugShowCheckedModeBanner: false,
);
}
}
// O AuthWrapper ouve as mudanças de estado de autenticação
class AuthWrapper extends StatelessWidget {
const AuthWrapper({super.key});
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
// Ouve o stream de estado de autenticação do Firebase
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
// Enquanto está conectando, mostra um loader
if (snapshot.connectionState == ConnectionState.waiting) {
return const Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
// Se o snapshot tem dados, significa que o usuário está logado
if (snapshot.hasData) {
return HomePage(user: snapshot.data!);
}
// Se não tem dados, mostra a tela de login
return const LoginPage();
},
);
}
}
Passo 7: Testando a Aplicação
Com o código no lugar e o usuário de teste criado no console do Firebase, é hora de testar.
Execute o aplicativo na sua plataforma de preferência (Android, iOS, Web, etc.):
flutter run
A tela de login deve aparecer.
Use o email e a senha do usuário que você cadastrou no Passo 2.
Ao clicar em “Entrar”, você deve ser redirecionado para a tela inicial, que exibirá seu email.
Clique no ícone de “logout” na barra de aplicativos. Você será levado de volta para a tela de login.
Tente fazer login com uma senha errada para ver a mensagem de erro aparecer na parte inferior da tela.
Conclusão
Parabéns! Você implementou um fluxo de autenticação completo e seguro com Flutter e Firebase. A estrutura que criamos, usando um StreamBuilder
para “ouvir” o estado de login e separando as telas em arquivos diferentes, é uma das formas mais robustas e eficientes de gerenciar sessões de usuário em um aplicativo Flutter. A partir daqui, você pode expandir facilmente para incluir uma tela de registro, funcionalidade de “esqueci minha senha” ou login com provedores sociais como Google e Facebook.