Firebase — это облачная платформа от Google, которая предоставляет полный бэкенд для мобильных и веб-приложений. В этом руководстве мы разберём полную интеграцию Firebase во Flutter приложение, от настройки проекта до продвинутых функций.
Что такое Firebase?
Firebase — это Backend-as-a-Service (BaaS) платформа, которая предоставляет готовые решения для common задач мобильной разработки:
- Authentication — готовая система аутентификации с множеством провайдеров
- Cloud Firestore — NoSQL база данных real-time
- Cloud Functions — serverless функции на Node.js
- Cloud Storage — хранилище файлов (изображения, видео, документы)
- Cloud Messaging — push-уведомления
- Analytics — аналитика пользователей
- Crashlytics — отчёты о крашах
Преимущества Firebase
- Быстрый старт — не нужно настраивать сервер
- Real-time — мгновенное обновление данных
- Масштабируемость — автоматическое масштабирование
- Бесплатный tier — generous free plan для небольших проектов
- Интеграция с Google —无缝 интеграция с Google сервисами
Настройка проекта
1. Создание проекта в Firebase Console
- Перейдите на Firebase Console
- Нажмите «Add project»
- Введите название проекта и включите Google Analytics (опционально)
2. Добавление Flutter приложений
Для Android:
- В Firebase Console выберите «Add app» → Android
- Введите package name (например,
com.example.myapp) - Скачайте
google-services.json - Положите файл в
android/app/
Для iOS:
- В Firebase Console выберите «Add app» → iOS
- Введите bundle ID (например,
com.example.myapp) - Скачайте
GoogleService-Info.plist - Положите файл в
ios/Runner/
3. Зависимости
dependencies:
firebase_core: ^3.0.0
firebase_auth: ^5.0.0
cloud_firestore: ^5.0.0
firebase_storage: ^12.0.0
firebase_messaging: ^15.0.0
firebase_analytics: ^11.0.0
google_sign_in: ^6.2.0
Выполните flutter pub get.
Инициализация Firebase
Перед использованием任何 Firebase сервиса нужно инициализировать Firebase в приложении:
import 'package:firebase_core/firebase_core.dart';
Future<void> main() async {
// Обязательно для Firebase
WidgetsFlutterBinding.ensureInitialized();
// Инициализация Firebase
await Firebase.initializeApp();
runApp(MyApp());
}
Если вы используете несколько Firebase проектов или специфическую конфигурацию:
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
Класс DefaultFirebaseOptions генерируется автоматически при добавлении Flutter приложений в Firebase Console через FlutterFire CLI.
Authentication
Firebase Authentication поддерживает множество провайдеров: email/password, Google, Facebook, Apple, GitHub, телефон и другие.
Email/Password аутентификация
Сначала включите этот провайдер в Firebase Console → Authentication → Sign-in method.
import 'package:firebase_auth/firebase_auth.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// Текущий пользователь
User? get currentUser => _auth.currentUser;
// Stream изменений аутентификации
Stream<User?> get authStateChanges => _auth.authStateChanges();
// Регистрация
Future<Result<UserCredential>> register(
String email,
String password,
) async {
try {
final credential = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
return Result.success(credential);
} on FirebaseAuthException catch (e) {
return Result.failure(e.message ?? 'Registration failed');
} catch (e) {
return Result.failure('An error occurred');
}
}
// Вход
Future<Result<UserCredential>> login(
String email,
String password,
) async {
try {
final credential = await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
return Result.success(credential);
} on FirebaseAuthException catch (e) {
switch (e.code) {
case 'user-not-found':
return Result.failure('User not found');
case 'wrong-password':
return Result.failure('Wrong password');
default:
return Result.failure(e.message ?? 'Login failed');
}
} catch (e) {
return Result.failure('An error occurred');
}
}
// Выход
Future<void> logout() async {
await _auth.signOut();
}
// Сброс пароля
Future<Result<void>> resetPassword(String email) async {
try {
await _auth.sendPasswordResetEmail(email);
return Result.success(null);
} catch (e) {
return Result.failure('Failed to send reset email');
}
}
}
// Helper класс для результата
class Result<T> {
final bool isSuccess;
final T? data;
final String? error;
Result.success(this.data)
: isSuccess = true,
error = null;
Result.failure(this.error)
: isSuccess = false,
data = null;
}
Google Sign-In
Google Sign-In — один из самых удобных способов аутентификации.
import 'package:google_sign_in/google_sign_in.dart';
class GoogleAuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn _googleSignIn = GoogleSignIn();
Future<Result<UserCredential>> signInWithGoogle() async {
try {
// 1. Запускаем Google Sign-In
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
if (googleUser == null) {
// Пользователь отменил вход
return Result.failure('Sign-in cancelled');
}
// 2. Получаем authentication данные
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
// 3. Создаём credential для Firebase
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
// 4. Входим через Firebase
final userCredential = await _auth.signInWithCredential(credential);
return Result.success(userCredential);
} catch (e) {
return Result.failure('Google sign-in failed: $e');
}
}
Future<void> signOut() async {
await _googleSignIn.signOut();
await _auth.signOut();
}
}
Отслеживание состояния аутентификации
Для автоматического перенаправления пользователя на нужный экран используйте StreamBuilder:
class AuthWrapper extends StatelessWidget {
const AuthWrapper({super.key});
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
// Загрузка
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
body: Center(child: CircularProgressIndicator()),
);
}
// Пользователь авторизован
if (snapshot.hasData) {
return HomeScreen();
}
// Пользователь не авторизован
return LoginScreen();
},
);
}
}
Cloud Firestore
Firestore — это NoSQL база данных real-time. Данные хранятся в виде коллекций документов.
Базовые операции CRUD
import 'package:cloud_firestore/cloud_firestore.dart';
class DatabaseService {
final FirebaseFirestore _db = FirebaseFirestore.instance;
// CREATE: Создать документ
Future<Result<void>> createUser(User user) async {
try {
await _db.collection('users').doc(user.id).set(user.toMap());
return Result.success(null);
} catch (e) {
return Result.failure('Failed to create user');
}
}
// READ: Прочитать документ
Future<Result<User>> getUser(String userId) async {
try {
final doc = await _db.collection('users').doc(userId).get();
if (!doc.exists) {
return Result.failure('User not found');
}
final user = User.fromMap(doc.data() as Map<String, dynamic>);
return Result.success(user);
} catch (e) {
return Result.failure('Failed to get user');
}
}
// UPDATE: Обновить документ
Future<Result<void>> updateUser(User user) async {
try {
await _db.collection('users').doc(user.id).update(user.toMap());
return Result.success(null);
} catch (e) {
return Result.failure('Failed to update user');
}
}
// DELETE: Удалить документ
Future<Result<void>> deleteUser(String userId) async {
try {
await _db.collection('users').doc(userId).delete();
return Result.success(null);
} catch (e) {
return Result.failure('Failed to delete user');
}
}
}
Real-time обновления
Один из главных преимуществ Firestore — real-time обновления. Используйте snapshots() вместо get():
class UserList extends StatelessWidget {
const UserList({super.key});
@override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.orderBy('createdAt', descending: true)
.snapshots(),
builder: (context, snapshot) {
// Обработка ошибок
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
}
// Загрузка
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
}
// Нет данных
if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
return Center(child: Text('No users found'));
}
// Отображение списка
final users = snapshot.data!.docs;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
final user = User.fromMap(
users[index].data() as Map<String, dynamic>,
);
return ListTile(
title: Text(user.name),
subtitle: Text(user.email),
trailing: Text(user.createdAt.toString()),
);
},
);
},
);
}
}
Запросы
Firestore поддерживает сложные запросы:
class QueryService {
final FirebaseFirestore _db = FirebaseFirestore.instance;
// Где (where)
Future<List<User>> getUsersOlderThan(int age) async {
final snapshot = await _db
.collection('users')
.where('age', isGreaterThan: age)
.get();
return snapshot.docs
.map((doc) => User.fromMap(doc.data() as Map<String, dynamic>))
.toList();
}
// Сортировка
Future<List<User>> getUsersSortedByAge() async {
final snapshot = await _db
.collection('users')
.orderBy('age', descending: true)
.get();
return snapshot.docs
.map((doc) => User.fromMap(doc.data() as Map<String, dynamic>))
.toList();
}
// Лимит
Future<List<User>> getFirst10Users() async {
final snapshot = await _db
.collection('users')
.limit(10)
.get();
return snapshot.docs
.map((doc) => User.fromMap(doc.data() as Map<String, dynamic>))
.toList();
}
// Сложные запросы
Future<List<Product>> searchProducts({
required String category,
required double maxPrice,
}) async {
final snapshot = await _db
.collection('products')
.where('category', isEqualTo: category)
.where('price', isLessThan: maxPrice)
.orderBy('rating', descending: true)
.limit(20)
.get();
return snapshot.docs
.map((doc) => Product.fromMap(doc.data() as Map<String, dynamic>))
.toList();
}
// Пагинация
Future<List<User>> getUsersPaginated({
required User? lastUser,
int limit = 10,
}) async {
Query query = _db
.collection('users')
.orderBy('name')
.limit(limit);
if (lastUser != null) {
query = query.startAfterDocument(lastUser);
}
final snapshot = await query.get();
return snapshot.docs
.map((doc) => User.fromMap(doc.data() as Map<String, dynamic>))
.toList();
}
}
Транзакции
Для атомарных операций используйте транзакции:
Future<Result<void>> transferMoney({
required String fromUserId,
required String toUserId,
required double amount,
}) async {
try {
await FirebaseFirestore.instance.runTransaction((transaction) async {
// 1. Читаем документы
final fromUserRef =
FirebaseFirestore.instance.collection('users').doc(fromUserId);
final toUserRef =
FirebaseFirestore.instance.collection('users').doc(toUserId);
final fromUserDoc = await transaction.get(fromUserRef);
final toUserDoc = await transaction.get(toUserRef);
if (!fromUserDoc.exists || !toUserDoc.exists) {
throw Exception('One or both users not found');
}
final fromBalance = fromUserDoc.data()!['balance'] as double;
final toBalance = toUserDoc.data()!['balance'] as double;
// 2. Проверяем баланс
if (fromBalance < amount) {
throw Exception('Insufficient balance');
}
// 3. Обновляем документы
transaction.update(fromUserRef, {'balance': fromBalance - amount});
transaction.update(toUserRef, {'balance': toBalance + amount});
});
return Result.success(null);
} catch (e) {
return Result.failure('Transaction failed: $e');
}
}
Batch операции
Для нескольких записей используйте batch операции:
Future<void> deleteAllMessages(String chatId) async {
final batch = FirebaseFirestore.instance.batch();
final messages = await FirebaseFirestore.instance
.collection('chats')
.doc(chatId)
.collection('messages')
.limit(500)
.get();
for (var doc in messages.docs) {
batch.delete(doc.reference);
}
await batch.commit();
}
Firebase Storage
Cloud Storage используется для хранения файлов: изображений, видео, документов и других бинарных данных.
Загрузка файлов
import 'package:firebase_storage/firebase_storage.dart';
class StorageService {
final FirebaseStorage _storage = FirebaseStorage.instance;
// Загрузить файл
Future<Result<String>> uploadFile({
required File file,
required String path,
}) async {
try {
final ref = _storage.ref().child(path);
final uploadTask = ref.putFile(file);
// Отслеживание прогресса
uploadTask.snapshotEvents.listen((TaskSnapshot snapshot) {
final progress = snapshot.bytesTransferred / snapshot.totalBytes;
print('Upload progress: ${(progress * 100).toStringAsFixed(0)}%');
});
// Ожидание завершения
await uploadTask;
// Получение URL
final url = await ref.getDownloadURL();
return Result.success(url);
} catch (e) {
return Result.failure('Upload failed');
}
}
// Загрузить изображение с metadata
Future<Result<String>> uploadImage(File imageFile) async {
try {
final fileName = '${DateTime.now().millisecondsSinceEpoch}.jpg';
final ref = _storage.ref().child('images/$fileName');
final metadata = SettableMetadata(
contentType: 'image/jpeg',
customMetadata: {
'uploaded_by': 'user_id',
'description': 'Profile picture',
},
);
await ref.putFile(imageFile, metadata);
final url = await ref.getDownloadURL();
return Result.success(url);
} catch (e) {
return Result.failure('Image upload failed');
}
}
// Удалить файл
Future<Result<void>> deleteFile(String path) async {
try {
await _storage.ref().child(path).delete();
return Result.success(null);
} catch (e) {
return Result.failure('Delete failed');
}
}
// Получить URL файла
Future<Result<String>> getFileUrl(String path) async {
try {
final url = await _storage.ref().child(path).getDownloadURL();
return Result.success(url);
} catch (e) {
return Result.failure('Failed to get URL');
}
}
}
Cloud Functions
Cloud Functions позволяют выполнять serverless код на backend. Это полезно для: — Отправки email уведомлений — Обработки платежей — Агрегации данных — Интеграции с внешними API
Вызов функций из Flutter
dependencies:
cloud_functions: ^5.0.0
import 'package:cloud_functions/cloud_functions.dart';
class FunctionsService {
final FirebaseFunctions _functions = FirebaseFunctions.instance;
Future<Result<void>> sendWelcomeEmail(String userId) async {
try {
final result = await _functions.httpsCallable('sendWelcomeEmail').call({
'userId': userId,
});
return Result.success(result.data);
} on FirebaseFunctionsException catch (e) {
return Result.failure(e.message);
} catch (e) {
return Result.failure('Function call failed');
}
}
Future<Result<String>> processPayment(PaymentRequest request) async {
try {
final result =
await _functions.httpsCallable('processPayment').call(request.toMap());
final transactionId = result.data['transactionId'] as String;
return Result.success(transactionId);
} catch (e) {
return Result.failure('Payment failed');
}
}
}
Push Notifications
Firebase Cloud Messaging (FCM) позволяет отправлять push-уведомления на устройства пользователей.
Настройка FCM
import 'package:firebase_messaging/firebase_messaging.dart';
class NotificationService {
final FirebaseMessaging _messaging = FirebaseMessaging.instance;
Future<void> initialize() async {
// 1. Запрос permissions на iOS
NotificationSettings settings = await _messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
print('User granted permission');
}
// 2. Получить FCM token
final token = await _messaging.getToken();
if (token != null) {
print('FCM Token: $token');
// Сохранить token в Firestore для отправки уведомлений
await _saveTokenToServer(token);
}
// 3. Слушать сообщения в foreground
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got message in foreground: ${message.notification?.title}');
// Показать local notification
_showLocalNotification(message);
});
// 4. Сообщения при открытии приложения из notification
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('App opened from notification');
_handleNotificationTap(message);
});
}
Future<void> _saveTokenToServer(String token) async {
// Сохранить token в Firestore
final userId = FirebaseAuth.instance.currentUser?.uid;
if (userId != null) {
await FirebaseFirestore.instance
.collection('users')
.doc(userId)
.update({'fcmToken': token});
}
}
void _showLocalNotification(RemoteMessage message) {
// Используйте flutter_local_notifications для показа
}
void _handleNotificationTap(RemoteMessage message) {
// Навигация к нужному экрану
}
}
Обработка фоновых сообщений
Для обработки сообщений в свернутом приложении:
// Обязательно top-level функция
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print('Background message: ${message.messageId}');
// Обработка сообщения
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// Регистрируем background handler
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
Analytics
Firebase Analytics автоматически собирает данные о пользователях. Для custom events:
import 'package:firebase_analytics/firebase_analytics.dart';
class AnalyticsService {
final FirebaseAnalytics _analytics = FirebaseAnalytics.instance;
// Логировать custom событие
Future<void> logEvent({
required String name,
Map<String, Object?>? parameters,
}) async {
await _analytics.logEvent(
name: name,
parameters: parameters,
);
}
// Просмотр экрана
Future<void> logScreenView(String screenName) async {
await _analytics.logScreenView(
screenName: screenName,
screenClass: screenName,
);
}
// Покупка
Future<void> logPurchase({
required String itemId,
required double price,
required String currency,
}) async {
await _analytics.logPurchase(
transactionId: DateTime.now().millisecondsSinceEpoch.toString(),
itemId: itemId,
value: price,
currency: currency,
);
}
// Поиск
Future<void> logSearch(String searchTerm) async {
await _analytics.logSearch(searchTerm: searchTerm);
}
// Выбор контента
Future<void> logSelectContent({
required String contentType,
required String itemId,
}) async {
await _analytics.logSelectContent(
contentType: contentType,
itemId: itemId,
);
}
}
Заключение
Firebase предоставляет мощный и простой бэкенд для Flutter приложений. В этом руководстве мыcovered:
- Authentication — email/password и Google sign-in
- Cloud Firestore — CRUD операции, real-time обновления, транзакции
- Cloud Storage — загрузка файлов
- Cloud Functions — serverless функции
- FCM — push-уведомления
- Analytics — tracking событий
Используйте Firebase для быстрого прототипирования и MVP. Для больших проектов с complex requirements может потребоваться custom backend.