Flutter: Firebase統合
FirebaseはFlutterのスーパーアシスタント — Auth、データベース、ストレージ、アナリティクス、プッシュ通知がオールインワンですぐに使えます。
📋 前提条件: 以下に既に慣れている必要があります
- レッスン11:ネットワークとREST API
- レッスン13:ローカルストレージ
1. このレッスンで学ぶこと
- Firebaseプロジェクト設定:FlutterFire CLI + firebase_options.dartマルチプラットフォーム設定
- Authentication:Email/Google/Appleサインイン + 匿名ログイン + 電話番号認証
- Cloud Firestore:データモデリング、snapshots()によるリアルタイムリスニング、複合クエリとセキュリティルール
- Cloud Storage:商品画像のアップロード/ダウンロード + キャッシュ戦略
- Analytics + Cloud Messaging:ユーザー行動トラッキング + プッシュ通知マーケティング
2. 自前バックエンド崩壊のリアルなストーリー
(1) 悩み:自前バックエンドの高い運用コスト
BobのShopApp自前バックエンドはNode.js + PostgreSQL + S3を使用し、3人の運用チームで月額2万ドルのコスト。あるデータベースマイグレーションで4時間のダウンタイムが発生し、5万ドル分の注文を損失。リアルタイムの商品在庫同期にWebSocketが必要でしたが、自前構成では複雑で不安定でした。プッシュ通知はサードパーティサービスを使用し、頻繁に遅延が発生していました。
(2) Firebaseスイートソリューション
FirebaseはAuth + Firestore + Storage + Analytics + FCMをワンストップバックエンドとして提供 — サーバー運用不要、リアルタイムデータが自動同期。
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
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
(3) 成果:運用ゼロ + リアルタイム同期 + コスト半減
BobがFirebaseに移行後、バックエンド運用チームは2人削減され、月額コストは2万ドルから8千ドルに低下、リアルタイムデータ遅延は秒単位からミリ秒に改善しました。
3. Firebaseアーキテクチャ概要
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
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
(1) Firebaseサービスマトリックス
| サービス | 用途 | 料金 |
|---|---|---|
| Authentication | ユーザー認証 | 寛容な無料枠 |
| Cloud Firestore | NoSQLリアルタイムデータベース | 無料枠:5万読み取り/日 |
| Cloud Storage | ファイルストレージ | 無料枠:5GB |
| Analytics | ユーザー行動分析 | 無料 |
| Cloud Messaging | プッシュ通知 | 無料 |
| Crashlytics | クラッシュレポート | 無料 |
4. Firebaseプロジェクト設定
▶ サンプル
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
: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
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
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
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
5. Authentication
(1) サインイン方式比較
| 方式 | 複雑さ | ユーザー体験 | ユースケース |
|---|---|---|---|
| Email/Password | 低い | 中程度 | 汎用 |
| Googleサインイン | 中程度 | 高い | Android推奨 |
| Appleサインイン | 中程度 | 高い | iOS必須 |
| 匿名ログイン | 低い | 高い(摩擦なし) | 登録前のお試し |
| 電話番号認証 | 高い | 中程度 | 身元確認 |
▶ サンプル
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
: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
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
▶ サンプル
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
: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
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
6. Cloud Firestore
(1) データモデリング
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
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
▶ サンプル
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
: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
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
▶ サンプル
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
: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
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
7. Cloud Storage
▶ サンプル
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
:商品画像のアップロードとダウンロード
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
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
8. Analytics + Cloud Messaging
▶ サンプル
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
: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
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
▶ サンプル
TEXT
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
: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
> 出力: ローカルのFlutter SDKで実行してください(Flutter 3.x / Dart 3.x)。PistonサーバーにはFlutterがインストールされていません — 手元のマシンで`flutter run`して動作確認してください。実際のUI/状態はプラットフォームにより多少異なる場合があります。
❓ よくある質問
Q Firebase無料枠は十分ですか?
A Firestoreは無料で5万読み取り/2万書き込み/日 — 小規模アプリには十分です。100万DAUレベルでは有料プランが必要です。
Q FirestoreとRealtime Databaseのどちらを使うべきですか?
A 新規プロジェクトではFirestoreを使用してください(より強力なクエリ、より良いデータモデル)。Realtime Databaseは非常に低遅延のリアルタイムが必要なシナリオのみです。
Q Firebase AuthトークンをバックエンドAPIとどう連携しますか?
A
await user.getIdToken()でJWTを取得し、バックエンドに送信して検証します。バックエンドはFirebase Admin SDKでトークンを検証します。Q Firestoreクエリにインデックス制限はありますか?
A 複合クエリには複合インデックスが必要です。最初の実行はインデックス作成リンクを含むエラーで失敗します。
Q FCMプッシュは中国本土で動作しますか?
A いいえ。中国本土ではベンダー固有プッシュ(Huawei/Xiaomi/OPPO/VIVO)またはJPushのようなサードパーティサービスの統合が必要です。
Q Firestoreセキュリティルールはどうデバッグしますか?
A FirebaseコンソールのRules Playgroundで読み取り/書き込みリクエストをシミュレートしてルールをテストできます。本番では必ず厳格なルールを設定してください。
📖 まとめ
- Firebaseはワンストップバックエンド:Auth + Firestore + Storage + Analytics + FCM
- FlutterFire CLIがマルチプラットフォーム設定を自動生成
- Authは複数のサインイン方式をサポート、authStateChangesでリアルタイムログイン状態監視
- Firestoreのsnapshots()でリアルタイムデータ同期、セキュリティルールでデータを保護
- Analyticsがユーザー行動をトラッキング、FCMがプッシュ通知を配信
📝 練習問題
- 基本 (⭐):Firebaseプロジェクトを設定し、Email/Passwordの登録とログインを実装し、authStateChangesでユーザー状態を表示してください。
- 中級 (⭐⭐):Firestore商品CRUD + リアルタイムリスニングを実装し、商品在庫が変更されたときにUIが自動更新されるようにしてください。
- チャレンジ (⭐⭐⭐):完全なFirebase統合を実装してください:Googleサインイン + Firestoreリアルタイム商品 + Storage画像アップロード(進捗付き)+ Analyticsイベントトラッキング + FCMプッシュ通知。