Flutter: تكامل Firebase
Firebase هو المساعد الخارق لـ Flutter — Auth وقاعدة بيانات وتخزين وتحليلات وإشعارات فورية، كل شيء جاهز من الصندوق.
📋 المتطلبات السابقة: يجب أن تكون ملمًا بـ
- الدرس 11: الشبكات وREST API
- الدرس 13: التخزين المحلي
1. ما ستتعلمه
- إعدادات مشروع Firebase: FlutterFire CLI + إعدادات متعددة المنصات بـ firebase_options.dart
- المصادقة: بريد إلكتروني/Google/Apple Sign-In + تسجيل دخول مجهول + التحقق من الهاتف
- Cloud Firestore: نمذجة البيانات، الاستماع الفوري بـ snapshots()، الاستعلامات المركبة وقواعد الأمان
- Cloud Storage: رفع/تنزيل صور المنتجات + استراتيجية التخزين المؤقت
- Analytics + Cloud Messaging: تتبع سلوك المستخدم + تسويق الإشعارات الفورية
2. قصة حقيقية عن انهيار خادم ذاتي البناء
(1) المشكلة: تكلفة تشغيل عالية للخادم الذاتي
خادم ShopApp لبوب الذاتي يستخدم Node.js + PostgreSQL + S3، بفريق تشغيل من 3 أشهر وتكلفة شهرية 20 ألف دولار. ترحيل قاعدة بيانات واحد سبب 4 ساعات توقف، بخسارة 50 ألف دولار في الطلبات. مزامنة مخزون المنتجات الفورية تحتاج WebSocket، الذي كان معقدًا وغير مستقر في البنية الذاتية. الإشعارات الفورية استخدمت خدمة خارجية بتأخيرات متكررة.
(2) حل حزمة Firebase
يوفر Firebase Auth + Firestore + Storage + Analytics + FCM كخادم شامل — لا حاجة لتشغيل خوادم، البيانات الفورية تتزامن تلقائيًا.
import 'package:cloud_firestore/cloud_firestore.dart';
// ⚙️ تثبيت التبعيات: 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();
});
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
(3) الفائدة: تشغيل صفري + مزامنة فورية + تكاليف مخفضة للنصف
بعد انتقال بوب لـ Firebase، تقلص فريق التشغيل بشخصين، وانخفضت التكلفة الشهرية من 20 ألف دولار إلى 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]
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
(1) مصفوفة خدمات Firebase
| الخدمة | الاستخدام | التسعير |
|---|---|---|
| Authentication | مصادقة المستخدم | طبقة مجانية سخية |
| Cloud Firestore | قاعدة بيانات NoSQL فورية | طبقة مجانية: 50 ألف قراءة/يوم |
| Cloud Storage | تخزين الملفات | طبقة مجانية: 5 غيغابايت |
| Analytics | تحليلات سلوك المستخدم | مجاني |
| Cloud Messaging | إشعارات فورية | مجاني |
| Crashlytics | تقارير الأعطال | مجاني |
4. إعدادات مشروع Firebase
▶ مثال
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
: تهيئة FlutterFire
# 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
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
// ⚙️ تثبيت التبعيات: flutter pub add firebase_core
// ⚙️ إعدادات المشروع: 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());
}
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
5. المصادقة
(1) مقارنة طرق تسجيل الدخول
| الطريقة | التعقيد | تجربة المستخدم | حالة الاستخدام |
|---|---|---|---|
| بريد إلكتروني/كلمة مرور | منخفض | متوسطة | عام |
| Google Sign-In | متوسط | عالية | مفضلة على Android |
| Apple Sign-In | متوسط | عالية | مطلوبة على iOS |
| تسجيل دخول مجهول | منخفض | عالية (بدون احتكاك) | تجربة قبل التسجيل |
| التحقق من الهاتف | مرتفع | متوسطة | التحقق من الهوية |
▶ مثال
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
: غلاف AuthService
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
// ⚙️ تثبيت التبعيات: 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;
}
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
▶ مثال
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
: 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';
// ⚙️ تثبيت التبعيات: flutter pub add firebase_auth flutter_riverpod riverpod_annotation
// ⚙️ تبعيات تطوير: 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();
}
}
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
6. Cloud Firestore
(1) نمذجة البيانات
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!"
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
▶ مثال
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
: CRUD في Firestore
import 'package:cloud_firestore/cloud_firestore.dart';
// ⚙️ تثبيت التبعيات: 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});
}
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
▶ مثال
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
: قواعد أمان Firestore
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;
}
}
}
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
7. Cloud Storage
▶ مثال
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
: رفع وتنزيل صور المنتجات
import 'package:firebase_storage/firebase_storage.dart';
import 'dart:io';
// ⚙️ تثبيت التبعيات: 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;
});
}
}
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
8. Analytics + Cloud Messaging
▶ مثال
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
: تتبع أحداث Analytics
import 'package:firebase_analytics/firebase_analytics.dart';
// ⚙️ تثبيت التبعيات: 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);
}
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
▶ مثال
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
: إشعارات FCM الفورية
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
// ⚙️ تثبيت التبعيات: 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
}
});
}
}
> الإخراج: شغّل محليًا باستخدام Flutter SDK (Flutter 3.x / Dart 3.x). خادم Piston لا يحتوي على Flutter؛ استخدم `flutter run` على جهازك المحلي للمقارنة. قد تختلف واجهة/حالة المستخدم الفعلية قليلاً بين المنصات.
❓ أسئلة شائعة
await user.getIdToken() وأرسله للخادم للتحقق. الخادم يستخدم Firebase Admin SDK للتحقق من الرمز.📖 ملخص
- Firebase خادم شامل: Auth + Firestore + Storage + Analytics + FCM
- FlutterFire CLI يُولد تلقائيًا إعدادات متعددة المنصات
- Auth يدعم طرق تسجيل دخول متعددة، authStateChanges لمراقبة حالة تسجيل الدخول الفورية
- Firestore snapshots() يُمكن مزامنة البيانات الفورية، قواعد الأمان تحمي البيانات
- Analytics يتتبع سلوك المستخدم، FCM يُسلم الإشعارات الفورية
📝 تمارين
- أساسي (الصعوبة ⭐): أعد مشروع Firebase، نفذ تسجيل ودخول بالبريد الإلكتروني/كلمة المرور، وراقب authStateChanges لعرض حالة المستخدم.
- متوسط (الصعوبة ⭐⭐): نفذ CRUD لمنتجات Firestore + استماع فوري، مع تحديث UI تلقائي عند تغير مخزون المنتج.
- متقدم (الصعوبة ⭐⭐⭐): نفذ تكامل Firebase كامل: Google Sign-In + منتجات Firestore الفورية + رفع صور Storage (مع تقدم) + تتبع أحداث Analytics + إشعارات FCM الفورية.