Flutter: Integração com Firebase

Firebase é o super assistente do Flutter — Auth, banco de dados, armazenamento, analytics, notificações push, tudo pronto para uso.

📋 Pré-requisitos: Você já deve estar familiarizado com

1. O Que Você Vai Aprender


2. Uma História Real de Colapso de Backend Próprio

(1) O Problema: Alto Custo Operacional de Backend Próprio

O backend próprio do ShopApp do Bob usa Node.js + PostgreSQL + S3, com uma equipe de ops de 3 pessoas e custo mensal de 20 mil USD. Uma migração de banco de dados causou 4 horas de inatividade, perdendo 50 mil USD em pedidos. A sincronização de inventário de produtos em tempo real exigia WebSocket, que era complexo e instável na infraestrutura própria. Notificações push usavam um serviço de terceiros com atrasos frequentes.

(2) A Solução com o Pacote Firebase

Firebase fornece Auth + Firestore + Storage + Analytics + FCM como backend completo — sem necessidade de ops de servidor, sincronização de dados em tempo real automática.

DART
import 'package:cloud_firestore/cloud_firestore.dart';

// ⚙️ Install dependencies: flutter pub add cloud_firestore

// Custom class definition source:
// - Product: see Lesson 11 json_serializable model (add fromFirestore factory constructor)

// Firestore real-time product updates
FirebaseFirestore.instance
  .collection('products')
  .where('stock', isGreaterThan: 0)
  .snapshots()
  .listen((snapshot) {
    // UI auto-updates when stock changes
    products = snapshot.docs.map((d) => Product.fromFirestore(d)).toList();
  });
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

(3) Benefício: Zero Ops + Sincronização em Tempo Real + Custos Reduzidos pela Metade

Após Bob migrar para Firebase, a equipe de ops do backend reduziu 2 pessoas, os custos mensais caíram de 20 mil USD para 8 mil USD, e a latência de dados em tempo real passou de segundos para milissegundos.


3. Visão Geral da Arquitetura do Firebase

100%
graph TD
    FB[Firebase] --> AUTH[Authentication]
    FB --> FS[Cloud Firestore]
    FB --> ST[Cloud Storage]
    FB --> AN[Analytics]
    FB --> CM[Cloud Messaging]
    FS --> |snapshots| PL[ProductList Realtime]
    AUTH --> |authStateChanges| LP[LoginPage]
    CM --> |onMessage| NP[NotificationPopup]
    AN --> |logEvent| DA[Dashboard Analytics]
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

(1) Matriz de Serviços do Firebase

Serviço Uso Preço
Authentication Autenticação de usuários Tier gratuito generoso
Cloud Firestore Banco de dados NoSQL em tempo real Gratuito: 50K leituras/dia
Cloud Storage Armazenamento de arquivos Gratuito: 5GB
Analytics Analytics de comportamento do usuário Gratuito
Cloud Messaging Notificações push Gratuito
Crashlytics Relatórios de crash Gratuito

4. Configuração do Projeto Firebase

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Inicialização com FlutterFire

BASH
# Install FlutterFire CLI
dart pub global activate flutterfire_cli

# Configure Firebase project
flutterfire configure

# Add dependencies
flutter pub add firebase_core firebase_auth cloud_firestore firebase_storage firebase_analytics firebase_messaging
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.
DART
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

// ⚙️ Install dependencies: flutter pub add firebase_core
// ⚙️ Project configuration: dart pub global activate flutterfire_cli && flutterfire configure

// lib/main.dart
import 'firebase_options.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  runApp(const ShopApp());
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

5. Authentication

(1) Comparação de Métodos de Sign-In

Método Complexidade Experiência do Usuário Caso de Uso
Email/Password Baixa Média Uso geral
Google Sign-In Média Alta Preferido no Android
Apple Sign-In Média Alta Obrigatório no iOS
Login Anônimo Baixa Alta (sem atrito) Testar antes de registrar
Verificação por Telefone Alta Média Verificação de identidade

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Wrapper AuthService

DART
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';

// ⚙️ Install dependencies: flutter pub add firebase_auth google_sign_in

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  Stream<User?> get authStateChanges => _auth.authStateChanges();

  Future<UserCredential> signInWithEmail(String email, String password) =>
      _auth.signInWithEmailAndPassword(email: email, password: password);

  Future<UserCredential> signUpWithEmail(String email, String password) =>
      _auth.createUserWithEmailAndPassword(email: email, password: password);

  Future<UserCredential> signInWithGoogle() async {
    final googleUser = await GoogleSignIn().signIn();
    final googleAuth = await googleUser?.authentication;
    final credential = GoogleAuthProvider.credential(
      accessToken: googleAuth?.accessToken,
      idToken: googleAuth?.idToken,
    );
    return _auth.signInWithCredential(credential);
  }

  Future<UserCredential> signInAnonymously() => _auth.signInAnonymously();

  Future<void> signOut() => _auth.signOut();

  User? get currentUser => _auth.currentUser;
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Riverpod Auth Notifier + Firebase

DART
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

// ⚙️ Install dependencies: flutter pub add firebase_auth flutter_riverpod riverpod_annotation
// ⚙️ Dev dependencies: flutter pub add --dev riverpod_generator build_runner

// Custom class definition source:
// - AuthService: see Section 5 AuthService wrapper in this lesson

@riverpod
class FirebaseAuth extends _$FirebaseAuth {
  @override
  AsyncValue<User?> build() {
    final service = AuthService();
    // Listen to auth state changes
    service.authStateChanges.listen((user) {
      state = AsyncData(user);
    });
    return AsyncData(service.currentUser);
  }

  Future<void> login(String email, String password) async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(() => AuthService().signInWithEmail(email, password));
  }

  Future<void> loginWithGoogle() async {
    state = const AsyncLoading();
    state = await AsyncValue.guard(() => AuthService().signInWithGoogle());
  }

  Future<void> logout() async {
    await AuthService().signOut();
  }
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

6. Cloud Firestore

(1) Modelagem de Dados

TEXT
firestore/
├── products/{productId}
│   ├── name: "Pro Laptop"
│   ├── price: 1299.99
│   ├── stock: 150
│   ├── category: "Electronics"
│   └── imageUrl: "gs://shopapp.appspot.com/products/laptop.jpg"
├── users/{userId}
│   ├── name: "Bob"
│   ├── email: "alice@shopapp.com"
│   ├── addresses: [{...}]
│   └── preferences: {theme: "dark", currency: "USD"}
├── orders/{orderId}
│   ├── userId: "alice_123"
│   ├── items: [{productId, quantity, price}]
│   ├── total: 99.99
│   ├── status: "shipped"
│   └── createdAt: Timestamp
└── reviews/{reviewId}
    ├── productId: "laptop_001"
    ├── userId: "bob_456"
    ├── rating: 4.5
    └── comment: "Great product!"
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Firestore CRUD

DART
import 'package:cloud_firestore/cloud_firestore.dart';

// ⚙️ Install dependencies: flutter pub add cloud_firestore

// Custom class definition source:
// - Product: see Lesson 11 json_serializable model (add fromFirestore factory constructor)

class ProductFirestore {
  final _db = FirebaseFirestore.instance;

  Future<List<Product>> getProducts({String? category, int limit = 20}) async {
    Query query = _db.collection('products').orderBy('rating', descending: true).limit(limit);
    if (category != null) query = query.where('category', isEqualTo: category);
    final snapshot = await query.get();
    return snapshot.docs.map((d) => Product.fromFirestore(d)).toList();
  }

  Future<Product> getProduct(String id) async {
    final doc = await _db.collection('products').doc(id).get();
    return Product.fromFirestore(doc);
  }

  // Real-time listener
  Stream<List<Product>> watchProducts({String? category}) {
    Query query = _db.collection('products').where('stock', isGreaterThan: 0);
    if (category != null) query = query.where('category', isEqualTo: category);
    return query.snapshots().map((s) => s.docs.map((d) => Product.fromFirestore(d)).toList());
  }

  Future<void> updateStock(String productId, int newStock) =>
      _db.collection('products').doc(productId).update({'stock': newStock});
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Regras de segurança do Firestore

JAVASCRIPT
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Products: anyone can read, only admins can write
    match /products/{productId} {
      allow read: if true;
      allow write: if isAdmin();
    }
    // Orders: users can read/write own orders
    match /orders/{orderId} {
      allow read, write: if request.auth != null && request.auth.uid == resource.data.userId;
      allow create: if request.auth != null;
    }
    // Reviews: anyone can read, authenticated users can create
    match /reviews/{reviewId} {
      allow read: if true;
      allow create: if request.auth != null;
    }
  }
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

7. Cloud Storage

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Upload e download de imagem de produto

DART
import 'package:firebase_storage/firebase_storage.dart';
import 'dart:io';

// ⚙️ Install dependencies: flutter pub add firebase_storage

class StorageService {
  final _storage = FirebaseStorage.instance;

  Future<String> uploadProductImage(String filePath, String productId) async {
    final ref = _storage.ref().child('products/$productId/${DateTime.now().millisecondsSinceEpoch}.jpg');
    final uploadTask = ref.putFile(File(filePath));
    final snapshot = await uploadTask;
    return await snapshot.ref.getDownloadURL();
  }

  Future<String> getDownloadUrl(String path) =>
      _storage.ref().child(path).getDownloadURL();

  // Upload with progress
  Stream<double> uploadWithProgress(String filePath, String productId) {
    final ref = _storage.ref().child('products/$productId/main.jpg');
    return ref.putFile(File(filePath)).snapshotEvents.map((s) {
      return s.bytesTransferred / s.totalBytes;
    });
  }
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

8. Analytics + Cloud Messaging

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Rastreamento de eventos do Analytics

DART
import 'package:firebase_analytics/firebase_analytics.dart';

// ⚙️ Install dependencies: flutter pub add firebase_analytics

// Custom class definition source:
// - Product: see Lesson 11 json_serializable model
// - Order: see Lesson 14 Order model

class ShopAnalytics {
  static final _analytics = FirebaseAnalytics.instance;

  static Future<void> logViewProduct(Product product) =>
      _analytics.logEvent(name: 'view_product', parameters: {
        'product_id': product.id,
        'product_name': product.name,
        'price': product.price,
        'category': product.category,
      });

  static Future<void> logAddToCart(Product product, int quantity) =>
      _analytics.logEvent(name: 'add_to_cart', parameters: {
        'product_id': product.id,
        'quantity': quantity,
        'price': product.price,
        'currency': 'USD',
      });

  static Future<void> logPurchase(Order order) =>
      _analytics.logEvent(name: 'purchase', parameters: {
        'order_id': order.id,
        'value': order.total,
        'currency': 'USD',
        'item_count': order.items.length,
      });

  static Future<void> setUserProperty(String name, String value) =>
      _analytics.setUserProperty(name: name, value: value);
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

▶ Exemplo

TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

: Notificações push com FCM

DART
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';

// ⚙️ Install dependencies: flutter pub add firebase_messaging

class NotificationService {
  static final _messaging = FirebaseMessaging.instance;

  static Future<void> init() async {
    // Request permission
    await _messaging.requestPermission();

    // Get FCM token
    final token = await _messaging.getToken();
    debugPrint('FCM Token: $token');

    // Foreground messages
    FirebaseMessaging.onMessage.listen((message) {
      debugPrint('Foreground message: ${message.notification?.title}');
    });

    // Background messages
    FirebaseMessaging.onMessageOpenedApp.listen((message) {
      // Handle notification tap
      final productId = message.data['product_id'];
      if (productId != null) {
        // Navigate to product detail
      }
    });
  }
}
TEXT
> Saída: Execute localmente com o Flutter SDK (Flutter 3.x / Dart 3.x). O servidor Piston não possui o Flutter instalado; use `flutter run` na sua máquina local para comparação. A UI/estado real pode variar ligeiramente entre plataformas.

❓ Perguntas Frequentes

P: O tier gratuito do Firebase é suficiente? R: O Firestore oferece 50K leituras/20K escritas/dia gratuitamente — suficiente para apps de pequena escala. DAUs na casa dos milhões exigem um plano pago.

P: Firestore ou Realtime Database? R: Use Firestore para novos projetos (consultas mais poderosas, modelo de dados melhor). Realtime Database é apenas para cenários que exigem latência em tempo real extremamente baixa.

P: Como integrar tokens do Firebase Auth com uma API de backend? R: Obtenha um JWT com await user.getIdToken() e envie-o ao backend para verificação. O backend usa o Firebase Admin SDK para verificar o token.

P: As consultas do Firestore têm limites de índice? R: Consultas compostas exigem índices compostos. A primeira execução falhará com um erro contendo um link para criar o índice.

P: O push do FCM funciona na China continental? R: Não. A China continental exige integração de push específico de fabricante (Huawei/Xiaomi/OPPO/VIVO) ou serviços de terceiros como JPush.

P: Como depurar regras de segurança do Firestore? R: O Rules Playground do Firebase Console pode simular requisições de leitura/escrita para testar regras. Sempre defina regras estritas em produção.


📖 Resumo


📝 Exercícios

  1. Básico (dificuldade ⭐): Configure um projeto Firebase, implemente registro e login com Email/Password, e monitore authStateChanges para exibir o status do usuário.
  2. Intermediário (dificuldade ⭐⭐): Implemente CRUD de produtos com Firestore + escuta em tempo real, com a UI atualizando automaticamente quando o estoque muda.
  3. Desafio (dificuldade ⭐⭐⭐): Implemente integração completa com Firebase: Google Sign-In + produtos em tempo real com Firestore + upload de imagem no Storage (com progresso) + rastreamento de eventos com Analytics + notificações push com FCM.

← Aula Anterior | Próxima Aula →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%