Flutter: Firebase Integration
Firebase is Flutter's super assistant — Auth, database, storage, analytics, push notifications, all-in-one out of the box.
📋 Prerequisites: You should already be familiar with
- Lesson 11: Networking & REST API
- Lesson 13: Local Storage
1. What You'll Learn
- Firebase project configuration: FlutterFire CLI + firebase_options.dart multi-platform setup
- Authentication: Email/Google/Apple Sign-In + anonymous login + phone verification
- Cloud Firestore: data modeling, real-time listening with snapshots(), compound queries and security rules
- Cloud Storage: product image upload/download + caching strategy
- Analytics + Cloud Messaging: user behavior tracking + push notification marketing
2. A Real Story of a Self-Built Backend Collapse
(1) The Pain Point: High Ops Cost of Self-Built Backend
Bob's ShopApp self-built backend uses Node.js + PostgreSQL + S3, with a 3-person ops team and a monthly cost of 20 thousand USD. One database migration caused 4 hours of downtime, losing 50 thousand USD in orders. Real-time product inventory sync required WebSocket, which was complex and unstable in the self-built setup. Push notifications used a third-party service with frequent delays.
(2) The Firebase Suite Solution
Firebase provides Auth + Firestore + Storage + Analytics + FCM as a one-stop backend — no server ops needed, real-time data syncs automatically.
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();
});
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
(3) The Payoff: Zero Ops + Real-Time Sync + Halved Costs
After Bob migrated to Firebase, the backend ops team shrank by 2 people, monthly costs dropped from 20 thousand USD to 8 thousand USD, and real-time data latency went from seconds to milliseconds.
3. Firebase Architecture Overview
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]
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
(1) Firebase Service Matrix
| Service | Usage | Pricing |
|---|---|---|
| Authentication | User authentication | Generous free tier |
| Cloud Firestore | NoSQL real-time database | Free tier: 50K reads/day |
| Cloud Storage | File storage | Free tier: 5GB |
| Analytics | User behavior analytics | Free |
| Cloud Messaging | Push notifications | Free |
| Crashlytics | Crash reporting | Free |
4. Firebase Project Configuration
▶ Example
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
: FlutterFire initialization
# 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
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
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());
}
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
5. Authentication
(1) Sign-In Method Comparison
| Method | Complexity | User Experience | Use Case |
|---|---|---|---|
| Email/Password | Low | Medium | General purpose |
| Google Sign-In | Medium | High | Android preferred |
| Apple Sign-In | Medium | High | iOS required |
| Anonymous Login | Low | High (frictionless) | Try before registering |
| Phone Verification | High | Medium | Identity verification |
▶ Example
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
: AuthService wrapper
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;
}
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
▶ Example
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
: Riverpod Auth Notifier + Firebase
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();
}
}
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
6. Cloud Firestore
(1) Data Modeling
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!"
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
▶ Example
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
: Firestore CRUD
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});
}
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
▶ Example
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
: Firestore security rules
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;
}
}
}
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
7. Cloud Storage
▶ Example
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
: Product image upload and download
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;
});
}
}
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
8. Analytics + Cloud Messaging
▶ Example
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
: Analytics event tracking
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);
}
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
▶ Example
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
: FCM push notifications
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
}
});
}
}
> Output: Run on a local Flutter SDK (Flutter 3.x / Dart 3.x). The Piston server does not have Flutter installed; use `flutter run` on your local machine for comparison. Actual UI/state may vary slightly by platform.
❓ FAQ
await user.getIdToken() and send it to the backend for verification. The backend uses Firebase Admin SDK to verify the token.📖 Summary
- Firebase is a one-stop backend: Auth + Firestore + Storage + Analytics + FCM
- FlutterFire CLI auto-generates multi-platform configuration
- Auth supports multiple sign-in methods, authStateChanges for real-time login state monitoring
- Firestore snapshots() enables real-time data sync, security rules protect data
- Analytics tracks user behavior, FCM delivers push notifications
📝 Exercises
- Basic (Difficulty ⭐): Configure a Firebase project, implement Email/Password registration and login, and monitor authStateChanges to display user status.
- Intermediate (Difficulty ⭐⭐): Implement Firestore product CRUD + real-time listening, with the UI auto-updating when product stock changes.
- Challenge (Difficulty ⭐⭐⭐): Implement full Firebase integration: Google Sign-In + Firestore real-time products + Storage image upload (with progress) + Analytics event tracking + FCM push notifications.