# Flutter và Firebase năm 2026: Xác thực, Firestore và Câu hỏi Phỏng vấn > Tìm hiểu chuyên sâu Flutter Firebase: xác thực với firebase_auth, CRUD Firestore và stream thời gian thực, quy tắc bảo mật, cùng các câu hỏi phỏng vấn thường gặp kèm ví dụ mã. - Published: 2026-06-10 - Updated: 2026-06-17 - Author: SharpSkill - Tags: flutter, firebase, authentication, firestore, dart, mobile development - Reading time: 12 min --- Tích hợp Flutter Firebase vẫn là lựa chọn backend phổ biến nhất cho các ứng dụng Flutter vào năm 2026, hỗ trợ xác thực, cơ sở dữ liệu thời gian thực và cloud functions trên hàng triệu ứng dụng. Với FlutterFire SDK v4.15, bộ công cụ đã trưởng thành đáng kể — từ thiết lập gọn gàng qua `flutterfire configure` đến hỗ trợ hạng nhất cho Firestore Pipelines và Firebase Admin Dart SDK. > **FlutterFire SDK v4.15 (2026)** > > Bản phát hành FlutterFire mới nhất đi kèm firebase_auth v6.1.2 và cloud_firestore v6.4.1, hỗ trợ Firestore Pipelines, xác thực TOTP trên macOS, và API validatePassword để áp dụng chính sách mật khẩu. ## Thiết Lập Firebase Trong Dự Án Flutter Trước khi viết bất kỳ mã Firebase nào, dự án cần được khởi tạo đúng cách. FlutterFire CLI tự động đăng ký nền tảng và tạo các tệp cấu hình cho Android, iOS, web và macOS. ```dart // main.dart import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; import 'firebase_options.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); // Initialize Firebase with platform-specific config await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); runApp(const MyApp()); } ``` Tệp `firebase_options.dart` được tạo tự động khi chạy `flutterfire configure`. Nó chứa khóa API, ID dự án và các định danh riêng cho từng nền tảng — không cần sao chép thủ công từ Firebase console. ## Firebase Authentication: Email, Google và Đa Yếu Tố Firebase Authentication xử lý danh tính người dùng với mã soạn sẵn tối thiểu. Gói `firebase_auth` hỗ trợ email/mật khẩu, các nhà cung cấp OAuth (Google, Apple, GitHub), xác minh SMS qua điện thoại và xác thực đa yếu tố. ### Đăng Ký Bằng Email và Mật Khẩu Luồng phổ biến nhất bắt đầu với email/mật khẩu. Phương thức `createUserWithEmailAndPassword` trả về một `UserCredential` chứa đối tượng `User` đã được xác thực. ```dart // auth_service.dart import 'package:firebase_auth/firebase_auth.dart'; class AuthService { final FirebaseAuth _auth = FirebaseAuth.instance; // Register with email and password Future register(String email, String password) async { try { final credential = await _auth.createUserWithEmailAndPassword( email: email, password: password, ); return credential.user; } on FirebaseAuthException catch (e) { // Handle specific error codes switch (e.code) { case 'email-already-in-use': throw Exception('This email is already registered'); case 'weak-password': throw Exception('Password must be at least 6 characters'); default: throw Exception('Registration failed: ${e.message}'); } } } // Sign in with existing credentials Future signIn(String email, String password) async { final credential = await _auth.signInWithEmailAndPassword( email: email, password: password, ); return credential.user; } // Reactive auth state stream Stream get authStateChanges => _auth.authStateChanges(); } ``` Stream `authStateChanges()` là cách được khuyến nghị để theo dõi trạng thái đăng nhập trên toàn ứng dụng. Nó phát ra `User` hiện tại khi đăng ký lắng nghe và kích hoạt lại trên mỗi sự kiện đăng nhập hoặc đăng xuất. ### Tích Hợp Google Sign-In Các nhà cung cấp OAuth cần thêm một gói riêng. Google Sign-In kích hoạt bộ chọn tài khoản native, sau đó trao đổi token với Firebase. ```dart // google_auth.dart import 'package:firebase_auth/firebase_auth.dart'; import 'package:google_sign_in/google_sign_in.dart'; Future signInWithGoogle() async { // Trigger the native Google Sign-In flow final googleUser = await GoogleSignIn().signIn(); if (googleUser == null) throw Exception('Sign-in cancelled'); // Obtain auth details from the Google account final googleAuth = await googleUser.authentication; // Create a Firebase credential from the Google tokens final credential = GoogleAuthProvider.credential( accessToken: googleAuth.accessToken, idToken: googleAuth.idToken, ); // Sign in to Firebase with the Google credential return FirebaseAuth.instance.signInWithCredential(credential); } ``` Mẫu này áp dụng cho tất cả nhà cung cấp OAuth: lấy token của nhà cung cấp, bọc nó trong một credential Firebase, rồi gọi `signInWithCredential`. > **Xác Thực Chính Sách Mật Khẩu** > > Firebase Auth v6.1+ bao gồm API `validatePassword`, cho phép áp dụng chính sách mật khẩu phía máy chủ. Chính sách có thể yêu cầu độ dài tối thiểu, chữ hoa, chữ thường, chữ số và ký tự đặc biệt — tất cả đều có thể cấu hình từ [Firebase console](https://console.firebase.google.com/). ## Các Thao Tác CRUD Firestore và Mô Hình Hóa Dữ Liệu Cloud Firestore tổ chức dữ liệu thành các collection và document. Không giống cơ sở dữ liệu SQL, Firestore không có schema — mỗi document có thể chứa các trường khác nhau. Sự linh hoạt này phù hợp với ứng dụng Flutter, nhưng đòi hỏi mô hình hóa dữ liệu kỷ luật để tránh các vấn đề về hiệu năng. ### Ghi và Đọc Document Các thao tác Firestore rất đơn giản với gói `cloud_firestore`. Mỗi lần đọc và ghi đều nhắm đến một đường dẫn document cụ thể. ```dart // firestore_service.dart import 'package:cloud_firestore/cloud_firestore.dart'; class TaskService { final _db = FirebaseFirestore.instance; final String _collection = 'tasks'; // Create a new document with auto-generated ID Future createTask(String userId, String title) async { final doc = await _db.collection(_collection).add({ 'userId': userId, 'title': title, 'completed': false, 'createdAt': FieldValue.serverTimestamp(), }); return doc.id; } // Read a single document by ID Future?> getTask(String taskId) async { final snapshot = await _db.collection(_collection).doc(taskId).get(); return snapshot.data(); } // Update specific fields without overwriting the entire document Future toggleComplete(String taskId, bool completed) async { await _db.collection(_collection).doc(taskId).update({ 'completed': completed, 'updatedAt': FieldValue.serverTimestamp(), }); } // Delete a document Future deleteTask(String taskId) async { await _db.collection(_collection).doc(taskId).delete(); } } ``` `FieldValue.serverTimestamp()` đảm bảo timestamp nhất quán trên mọi thiết bị bằng cách dùng đồng hồ máy chủ Firestore thay vì đồng hồ máy khách. ### Stream Thời Gian Thực Với snapshots() Firestore tỏa sáng với đồng bộ hóa thời gian thực. Phương thức `snapshots()` trả về một `Stream` phát ra dữ liệu mới mỗi khi các document bên dưới thay đổi — không cần polling. ```dart // real_time_tasks.dart import 'package:cloud_firestore/cloud_firestore.dart'; class TaskStream { final _db = FirebaseFirestore.instance; // Stream all tasks for a specific user, ordered by creation date Stream>> userTasks(String userId) { return _db .collection('tasks') .where('userId', isEqualTo: userId) .orderBy('createdAt', descending: true) .snapshots() .map((snapshot) => snapshot.docs.map((doc) { final data = doc.data(); data['id'] = doc.id; // Include document ID return data; }).toList()); } } ``` Trong một widget Flutter, stream này tích hợp trực tiếp với `StreamBuilder` để dựng lại UI trên mỗi thay đổi cơ sở dữ liệu. Firestore tự động xử lý quản lý kết nối, caching và lưu trữ ngoại tuyến. ## Quy Tắc Bảo Mật Firestore Cho Ứng Dụng Flutter Quy tắc bảo mật chạy trên máy chủ Firestore và kiểm soát quyền đọc/ghi ở cấp độ document. Nếu không có quy tắc phù hợp, bất kỳ người dùng nào đã xác thực đều có thể đọc hoặc sửa đổi bất kỳ document nào. ```javascript // firestore.rules rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // Users can only access their own profile match /users/{userId} { allow read, update: if request.auth != null && request.auth.uid == userId; allow create: if request.auth != null; allow delete: if false; // Prevent self-deletion } // Tasks belong to the user who created them match /tasks/{taskId} { allow read, write: if request.auth != null && resource.data.userId == request.auth.uid; allow create: if request.auth != null && request.resource.data.userId == request.auth.uid; } } } ``` Biến `request.auth.uid` chứa ID của người dùng đã xác thực từ Firebase Auth. Các quy tắc tham chiếu `resource.data` kiểm tra các trường document hiện có, trong khi `request.resource.data` xác thực dữ liệu ghi đang đến. > **Quy Tắc Bảo Mật Không Phải Tùy Chọn** > > Firestore mặc định từ chối mọi quyền truy cập ở chế độ production. Mỗi collection cần quy tắc rõ ràng. Một lỗi phổ biến trong các dự án Flutter: phát triển với quy tắc chế độ thử nghiệm (`allow read, write: if true`) và quên khóa lại trước khi triển khai production. ## Lưu Trữ Ngoại Tuyến và Chiến Lược Caching Firestore bật lưu trữ ngoại tuyến theo mặc định trên các nền tảng di động. Khi thiết bị mất kết nối, các thao tác đọc trả về dữ liệu đã được cache và các thao tác ghi xếp hàng cục bộ cho đến khi kết nối được khôi phục. Hành vi này trong suốt đối với mã Flutter — vẫn cùng các lệnh gọi `get()` và `snapshots()` hoạt động khi ngoại tuyến. Tuy nhiên, hai điểm cần lưu ý cho ứng dụng production: - **Kích thước cache**: Firestore cache tất cả document mà client đã đọc. Với các ứng dụng nhiều dữ liệu, hãy cấu hình `Settings(cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED)` hoặc đặt một giới hạn cụ thể. Bản phát hành v6.4.1 đã sửa một lỗi khi cache không giới hạn không được áp dụng đúng trên iOS. - **Chỉ báo ghi đang chờ**: Dùng `SnapshotMetadata.hasPendingWrites` để cho người dùng biết khi nào các thay đổi cục bộ chưa được đồng bộ lên máy chủ. ```dart // offline_aware_widget.dart StreamBuilder( stream: FirebaseFirestore.instance .collection('tasks') .doc(taskId) .snapshots(), builder: (context, snapshot) { if (!snapshot.hasData) return const CircularProgressIndicator(); final data = snapshot.data!; final isPending = data.metadata.hasPendingWrites; return Row( children: [ Text(data['title']), if (isPending) const Icon(Icons.cloud_upload, size: 16), ], ); }, ) ``` ## Cấu Trúc Một Ứng Dụng Flutter Firebase Cho Production Một kiến trúc có khả năng mở rộng tách biệt logic Firebase khỏi mã UI. Mẫu repository cung cấp một lớp trừu tượng rõ ràng giúp đơn giản hóa việc kiểm thử và di chuyển backend trong tương lai. ```dart // task_repository.dart abstract class TaskRepository { Future create(String userId, String title); Stream> watchAll(String userId); Future update(String id, Map fields); Future delete(String id); } // firebase_task_repository.dart class FirebaseTaskRepository implements TaskRepository { final _db = FirebaseFirestore.instance; @override Future create(String userId, String title) async { final doc = await _db.collection('tasks').add({ 'userId': userId, 'title': title, 'completed': false, 'createdAt': FieldValue.serverTimestamp(), }); return doc.id; } @override Stream> watchAll(String userId) { return _db .collection('tasks') .where('userId', isEqualTo: userId) .orderBy('createdAt', descending: true) .snapshots() .map((s) => s.docs.map(Task.fromFirestore).toList()); } // ... update and delete implementations } ``` Với mẫu này, các giải pháp quản lý state như [Riverpod hoặc BLoC](/blog/flutter/flutter-state-management-riverpod-vs-bloc) sử dụng giao diện repository mà không cần biết về Firestore. Các bài kiểm thử đơn vị có thể thay thế bằng một mock repository trả về dữ liệu định trước. ## Các Câu Hỏi Phỏng Vấn Flutter Firebase Thường Gặp Các buổi phỏng vấn thường kiểm tra cả hiểu biết khái niệm lẫn triển khai thực tế các dịch vụ Firebase. Những câu hỏi sau xuất hiện thường xuyên trong các buổi phỏng vấn lập trình viên Flutter năm 2026. **`authStateChanges()` khác với `idTokenChanges()` và `userChanges()` như thế nào?** `authStateChanges()` chỉ kích hoạt trên các sự kiện đăng nhập và đăng xuất. `idTokenChanges()` còn kích hoạt khi ID token được làm mới (khoảng mỗi 60 phút). `userChanges()` ghi nhận tất cả những điều trên cộng với cập nhật hồ sơ như `updateDisplayName()`. Với hầu hết navigation guard, `authStateChanges()` là đủ. **Điều gì xảy ra khi một thao tác ghi Firestore diễn ra lúc thiết bị ngoại tuyến?** Thao tác ghi được lưu trong cache cục bộ và một hàng đợi ghi đang chờ. Các listener `snapshots()` ngay lập tức nhận dữ liệu cập nhật với `metadata.hasPendingWrites == true`. Khi kết nối được khôi phục, Firestore tự động đồng bộ các thao tác ghi đang chờ bằng cách giải quyết xung đột last-writer-wins ở cấp độ trường. **Nên mô hình hóa dữ liệu Firestore như thế nào cho một ứng dụng chat?** Một cách tiếp cận phổ biến: một collection `chats` trong đó mỗi document chứa metadata (người tham gia, timestamp tin nhắn cuối), và một subcollection `messages` dưới mỗi document chat. Cấu trúc này cho phép truy vấn danh sách chat của người dùng một cách hiệu quả đồng thời phân trang tin nhắn trong từng cuộc trò chuyện. Hãy phi chuẩn hóa tin nhắn cuối vào document cha để tránh đọc subcollection cho khung nhìn danh sách. **Tại sao nên ưu tiên `FieldValue.serverTimestamp()` hơn `DateTime.now()`?** `DateTime.now()` dùng đồng hồ thiết bị, vốn có thể không chính xác hoặc bị thao túng. `FieldValue.serverTimestamp()` dùng đồng hồ máy chủ Firestore, đảm bảo thứ tự nhất quán trên mọi client. Điều này quan trọng với các tính năng như sắp xếp tin nhắn, bảng tin hoạt động và nhật ký kiểm toán. **Các truy vấn phức hợp được xử lý như thế nào trong Firestore, và yêu cầu về chỉ mục là gì?** Firestore yêu cầu một [chỉ mục phức hợp](https://firebase.google.com/docs/firestore/query-data/indexing) cho các truy vấn kết hợp nhiều trường với mệnh đề `where` và `orderBy`. Chỉ mục một trường được tạo tự động, nhưng chỉ mục phức hợp phải được định nghĩa thủ công trong `firestore.indexes.json` hoặc qua Firebase console. Firestore ghi lại một liên kết trực tiếp để tạo chỉ mục còn thiếu khi một truy vấn thất bại. Để [chuẩn bị phỏng vấn Flutter](/technologies/flutter/interview-questions/firebase-integration) sâu hơn, các module luyện tập chuyên biệt bao quát các chủ đề này với câu hỏi tương tác và thử thách tính giờ. ## Kết Luận - FlutterFire SDK v4.15 đi kèm các gói ổn định cho xác thực (v6.1.2) và Firestore (v6.4.1), với các tính năng mới như Firestore Pipelines và xác thực chính sách mật khẩu - `authStateChanges()` cung cấp một stream phản ứng để quản lý trạng thái đăng nhập trên toàn ứng dụng mà không cần kiểm tra thủ công - Các thao tác CRUD Firestore tuân theo một mẫu đơn giản: `add`, `get`, `update`, `delete` trên các tham chiếu collection và document - Đồng bộ hóa thời gian thực qua `snapshots()` loại bỏ polling và tích hợp trực tiếp với `StreamBuilder` để cập nhật UI tự động - Quy tắc bảo mật là bắt buộc cho production — luôn xác thực `request.auth.uid` đối chiếu với các trường quyền sở hữu document - Lưu trữ ngoại tuyến hoạt động trong suốt trên di động; dùng `hasPendingWrites` để truyền đạt trạng thái đồng bộ cho người dùng - Mẫu repository tách Firebase khỏi logic nghiệp vụ, giúp [kiểm thử](/blog/flutter/flutter-testing-widget-integration-interview-2026) và di chuyển trong tương lai trở nên đơn giản - Việc chuẩn bị phỏng vấn nên bao quát sự khác biệt giữa các stream auth, hành vi ngoại tuyến, các mẫu mô hình hóa dữ liệu và yêu cầu chỉ mục phức hợp --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/flutter/flutter-firebase-authentication-firestore-2026