# Flutter และ Firebase ในปี 2026: การยืนยันตัวตน, Firestore และคำถามสัมภาษณ์ > เจาะลึก Flutter Firebase: การยืนยันตัวตนด้วย firebase_auth, CRUD ของ Firestore และสตรีมแบบเรียลไทม์, กฎความปลอดภัย และคำถามสัมภาษณ์ที่พบบ่อยพร้อมตัวอย่างโค้ด - Published: 2026-06-10 - Updated: 2026-06-17 - Author: SharpSkill - Tags: flutter, firebase, authentication, firestore, dart, mobile development - Reading time: 12 min --- การผสานรวม Flutter กับ Firebase ยังคงเป็นตัวเลือกแบ็กเอนด์ที่ได้รับความนิยมมากที่สุดสำหรับแอปพลิเคชัน Flutter ในปี 2026 โดยขับเคลื่อนการยืนยันตัวตน ฐานข้อมูลแบบเรียลไทม์ และ cloud functions ในแอปนับล้าน ด้วย FlutterFire SDK v4.15 เครื่องมือต่าง ๆ ได้พัฒนาขึ้นอย่างมาก ตั้งแต่การตั้งค่าที่กระชับผ่าน `flutterfire configure` ไปจนถึงการรองรับระดับชั้นนำสำหรับ Firestore Pipelines และ Firebase Admin Dart SDK > **FlutterFire SDK v4.15 (2026)** > > FlutterFire รุ่นล่าสุดมาพร้อมกับ firebase_auth v6.1.2 และ cloud_firestore v6.4.1 รองรับ Firestore Pipelines การยืนยันตัวตนแบบ TOTP บน macOS และ API validatePassword สำหรับการบังคับใช้นโยบายรหัสผ่าน ## การตั้งค่า Firebase ในโปรเจกต์ Flutter ก่อนเขียนโค้ด Firebase ใด ๆ โปรเจกต์จำเป็นต้องได้รับการกำหนดค่าเริ่มต้นอย่างถูกต้อง FlutterFire CLI จะทำการลงทะเบียนแพลตฟอร์มโดยอัตโนมัติและสร้างไฟล์การกำหนดค่าสำหรับ Android, iOS, เว็บ และ 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()); } ``` ไฟล์ `firebase_options.dart` ถูกสร้างขึ้นโดยอัตโนมัติเมื่อรัน `flutterfire configure` ไฟล์นี้มีคีย์ API, รหัสโปรเจกต์ และตัวระบุเฉพาะแพลตฟอร์ม โดยไม่ต้องคัดลอกด้วยตนเองจาก Firebase console ## Firebase Authentication: อีเมล, Google และหลายปัจจัย Firebase Authentication จัดการตัวตนของผู้ใช้ด้วยโค้ดสำเร็จรูปที่น้อยที่สุด แพ็กเกจ `firebase_auth` รองรับอีเมล/รหัสผ่าน, ผู้ให้บริการ OAuth (Google, Apple, GitHub), การยืนยันผ่าน SMS ทางโทรศัพท์ และการยืนยันตัวตนแบบหลายปัจจัย ### การลงทะเบียนด้วยอีเมลและรหัสผ่าน ขั้นตอนที่พบบ่อยที่สุดเริ่มต้นด้วยอีเมล/รหัสผ่าน เมธอด `createUserWithEmailAndPassword` จะคืนค่า `UserCredential` ที่มีอ็อบเจกต์ `User` ที่ยืนยันตัวตนแล้ว ```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(); } ``` สตรีม `authStateChanges()` เป็นวิธีที่แนะนำในการติดตามสถานะการเข้าสู่ระบบทั่วทั้งแอป โดยจะส่งค่า `User` ปัจจุบันเมื่อมีการสมัครรับข้อมูล และทำงานอีกครั้งในทุกเหตุการณ์การเข้าสู่ระบบหรือออกจากระบบ ### การผสานรวม Google Sign-In ผู้ให้บริการ OAuth ต้องใช้แพ็กเกจเพิ่มเติม Google Sign-In จะเรียกตัวเลือกบัญชีแบบเนทีฟ จากนั้นแลกเปลี่ยนโทเค็นกับ 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); } ``` รูปแบบนี้ใช้ได้กับผู้ให้บริการ OAuth ทั้งหมด: รับโทเค็นของผู้ให้บริการ ห่อหุ้มไว้ใน credential ของ Firebase แล้วเรียก `signInWithCredential` > **การตรวจสอบนโยบายรหัสผ่าน** > > Firebase Auth v6.1+ มี API `validatePassword` ซึ่งช่วยให้บังคับใช้นโยบายรหัสผ่านฝั่งเซิร์ฟเวอร์ได้ นโยบายสามารถกำหนดความยาวขั้นต่ำ ตัวพิมพ์ใหญ่ ตัวพิมพ์เล็ก ตัวเลข และอักขระพิเศษ ซึ่งทั้งหมดสามารถกำหนดค่าได้จาก [Firebase console](https://console.firebase.google.com/) ## การดำเนินการ CRUD ของ Firestore และการสร้างแบบจำลองข้อมูล Cloud Firestore จัดระเบียบข้อมูลเป็น collection และ document ต่างจากฐานข้อมูล SQL ตรงที่ Firestore ไม่มี schema เอกสารแต่ละฉบับสามารถมีฟิลด์ที่แตกต่างกันได้ ความยืดหยุ่นนี้เหมาะกับแอป Flutter แต่ต้องการการสร้างแบบจำลองข้อมูลอย่างมีวินัยเพื่อหลีกเลี่ยงปัญหาด้านประสิทธิภาพ ### การเขียนและการอ่าน Document การดำเนินการของ Firestore ทำได้อย่างตรงไปตรงมาด้วยแพ็กเกจ `cloud_firestore` ทุกการอ่านและการเขียนจะมุ่งเป้าไปที่เส้นทางของ document ที่เฉพาะเจาะจง ```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()` รับประกัน timestamp ที่สอดคล้องกันในทุกอุปกรณ์ โดยใช้นาฬิกาของเซิร์ฟเวอร์ Firestore แทนนาฬิกาของไคลเอนต์ ### สตรีมแบบเรียลไทม์ด้วย snapshots() Firestore โดดเด่นด้วยการซิงโครไนซ์แบบเรียลไทม์ เมธอด `snapshots()` คืนค่า `Stream` ที่ส่งข้อมูลใหม่ทุกครั้งที่ document ที่เกี่ยวข้องมีการเปลี่ยนแปลง โดยไม่ต้อง 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()); } } ``` ภายใน widget ของ Flutter สตรีมนี้ผสานรวมโดยตรงกับ `StreamBuilder` เพื่อสร้าง UI ใหม่ในทุกการเปลี่ยนแปลงของฐานข้อมูล Firestore จัดการการเชื่อมต่อ การแคช และการคงอยู่แบบออฟไลน์โดยอัตโนมัติ ## กฎความปลอดภัยของ Firestore สำหรับแอป Flutter กฎความปลอดภัยทำงานบนเซิร์ฟเวอร์ Firestore และควบคุมสิทธิ์การอ่าน/เขียนในระดับ document หากไม่มีกฎที่เหมาะสม ผู้ใช้ที่ยืนยันตัวตนแล้วทุกคนสามารถอ่านหรือแก้ไข document ใดก็ได้ ```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; } } } ``` ตัวแปร `request.auth.uid` มี ID ของผู้ใช้ที่ยืนยันตัวตนแล้วจาก Firebase Auth กฎที่อ้างอิง `resource.data` จะตรวจสอบฟิลด์ของ document ที่มีอยู่ ในขณะที่ `request.resource.data` จะตรวจสอบข้อมูลการเขียนที่กำลังเข้ามา > **กฎความปลอดภัยไม่ใช่ทางเลือก** > > Firestore จะปฏิเสธการเข้าถึงทั้งหมดเป็นค่าเริ่มต้นในโหมด production ทุก collection ต้องมีกฎที่ชัดเจน ข้อผิดพลาดที่พบบ่อยในโปรเจกต์ Flutter: การพัฒนาด้วยกฎโหมดทดสอบ (`allow read, write: if true`) แล้วลืมล็อกก่อนนำขึ้น production ## การคงอยู่แบบออฟไลน์และกลยุทธ์การแคช Firestore เปิดใช้งานการคงอยู่แบบออฟไลน์เป็นค่าเริ่มต้นบนแพลตฟอร์มมือถือ เมื่ออุปกรณ์สูญเสียการเชื่อมต่อ การดำเนินการอ่านจะคืนค่าข้อมูลที่แคชไว้ และการดำเนินการเขียนจะเข้าคิวภายในเครื่องจนกว่าการเชื่อมต่อจะกลับมา พฤติกรรมนี้โปร่งใสต่อโค้ด Flutter การเรียก `get()` และ `snapshots()` เดิมยังคงทำงานได้ขณะออฟไลน์ อย่างไรก็ตาม มีสองสิ่งที่ต้องพิจารณาสำหรับแอป production: - **ขนาดแคช**: Firestore แคช document ทั้งหมดที่ไคลเอนต์ได้อ่าน สำหรับแอปที่มีข้อมูลจำนวนมาก ให้กำหนดค่า `Settings(cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED)` หรือกำหนดขีดจำกัดที่เฉพาะเจาะจง รุ่น v6.4.1 ได้แก้ไขบั๊กที่แคชแบบไม่จำกัดไม่ถูกนำไปใช้อย่างถูกต้องบน iOS - **ตัวบ่งชี้การเขียนที่รอดำเนินการ**: ใช้ `SnapshotMetadata.hasPendingWrites` เพื่อแสดงให้ผู้ใช้ทราบว่าการเปลี่ยนแปลงในเครื่องยังไม่ได้ซิงค์ไปยังเซิร์ฟเวอร์ ```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), ], ); }, ) ``` ## การจัดโครงสร้างแอป Flutter Firebase สำหรับ Production สถาปัตยกรรมที่ขยายขนาดได้จะแยกตรรกะของ Firebase ออกจากโค้ด UI รูปแบบ repository มอบชั้นการนามธรรมที่ชัดเจนซึ่งช่วยให้การทดสอบและการย้ายแบ็กเอนด์ในอนาคตง่ายขึ้น ```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 } ``` ด้วยรูปแบบนี้ โซลูชันการจัดการ state อย่าง [Riverpod หรือ BLoC](/blog/flutter/flutter-state-management-riverpod-vs-bloc) จะใช้อินเทอร์เฟซ repository โดยไม่ต้องรู้เกี่ยวกับ Firestore การทดสอบหน่วยสามารถแทนที่ด้วย mock repository ที่คืนค่าข้อมูลที่กำหนดไว้ล่วงหน้าได้ ## คำถามสัมภาษณ์ Flutter Firebase ที่พบบ่อย การสัมภาษณ์มักทดสอบทั้งความเข้าใจเชิงแนวคิดและการนำบริการ Firebase ไปใช้จริง คำถามต่อไปนี้ปรากฏเป็นประจำในการสัมภาษณ์นักพัฒนา Flutter ในปี 2026 **`authStateChanges()` แตกต่างจาก `idTokenChanges()` และ `userChanges()` อย่างไร?** `authStateChanges()` ทำงานเฉพาะในเหตุการณ์การเข้าสู่ระบบและออกจากระบบเท่านั้น `idTokenChanges()` จะทำงานเพิ่มเติมเมื่อ ID token ถูกรีเฟรช (ทุก ๆ ประมาณ 60 นาที) `userChanges()` จับทุกอย่างข้างต้นรวมถึงการอัปเดตโปรไฟล์อย่าง `updateDisplayName()` สำหรับ navigation guard ส่วนใหญ่ `authStateChanges()` ก็เพียงพอแล้ว **เกิดอะไรขึ้นเมื่อมีการเขียน Firestore ขณะที่อุปกรณ์ออฟไลน์?** การเขียนจะถูกเก็บไว้ในแคชภายในเครื่องและคิวการเขียนที่รอดำเนินการ ตัวรับฟัง `snapshots()` จะได้รับข้อมูลที่อัปเดตทันทีพร้อม `metadata.hasPendingWrites == true` เมื่อการเชื่อมต่อกลับมา Firestore จะซิงค์การเขียนที่รอดำเนินการโดยอัตโนมัติด้วยการแก้ไขข้อขัดแย้งแบบ last-writer-wins ในระดับฟิลด์ **ควรสร้างแบบจำลองข้อมูล Firestore สำหรับแอปแชทอย่างไร?** แนวทางที่พบบ่อย: collection `chats` ที่แต่ละ document มี metadata (ผู้เข้าร่วม, timestamp ของข้อความล่าสุด) และ subcollection `messages` ภายใต้แต่ละ document ของแชท โครงสร้างนี้ช่วยให้สามารถ query รายการแชทของผู้ใช้ได้อย่างมีประสิทธิภาพ พร้อมทั้งแบ่งหน้าข้อความภายในแต่ละบทสนทนา ควร denormalize ข้อความล่าสุดเข้าไปใน document แม่ เพื่อหลีกเลี่ยงการอ่าน subcollection สำหรับมุมมองรายการ **ทำไมจึงควรใช้ `FieldValue.serverTimestamp()` มากกว่า `DateTime.now()`?** `DateTime.now()` ใช้นาฬิกาของอุปกรณ์ ซึ่งอาจไม่แม่นยำหรือถูกดัดแปลงได้ `FieldValue.serverTimestamp()` ใช้นาฬิกาของเซิร์ฟเวอร์ Firestore ซึ่งรับประกันลำดับที่สอดคล้องกันในทุกไคลเอนต์ สิ่งนี้สำคัญสำหรับฟีเจอร์อย่างการเรียงลำดับข้อความ ฟีดกิจกรรม และบันทึกการตรวจสอบ **การ query แบบ composite จัดการอย่างไรใน Firestore และมีข้อกำหนดเรื่อง index อย่างไร?** Firestore ต้องการ [composite index](https://firebase.google.com/docs/firestore/query-data/indexing) สำหรับ query ที่รวมหลายฟิลด์ด้วยอนุประโยค `where` และ `orderBy` index แบบฟิลด์เดียวจะถูกสร้างโดยอัตโนมัติ แต่ composite index ต้องถูกกำหนดด้วยตนเองใน `firestore.indexes.json` หรือผ่าน Firebase console Firestore จะบันทึกลิงก์โดยตรงเพื่อสร้าง index ที่ขาดหายไปเมื่อ query ล้มเหลว สำหรับ[การเตรียมตัวสัมภาษณ์ Flutter](/technologies/flutter/interview-questions/firebase-integration) เพิ่มเติม โมดูลฝึกฝนเฉพาะทางครอบคลุมหัวข้อเหล่านี้ด้วยแบบทดสอบเชิงโต้ตอบและความท้าทายแบบจับเวลา ## บทสรุป - FlutterFire SDK v4.15 มาพร้อมแพ็กเกจที่เสถียรสำหรับการยืนยันตัวตน (v6.1.2) และ Firestore (v6.4.1) พร้อมฟีเจอร์ใหม่อย่าง Firestore Pipelines และการตรวจสอบนโยบายรหัสผ่าน - `authStateChanges()` มอบสตรีมแบบ reactive สำหรับจัดการสถานะการเข้าสู่ระบบทั่วทั้งแอปโดยไม่ต้องตรวจสอบด้วยตนเอง - การดำเนินการ CRUD ของ Firestore เป็นไปตามรูปแบบที่เรียบง่าย: `add`, `get`, `update`, `delete` บนการอ้างอิง collection และ document - การซิงโครไนซ์แบบเรียลไทม์ผ่าน `snapshots()` ขจัดการ polling และผสานรวมโดยตรงกับ `StreamBuilder` เพื่อการอัปเดต UI โดยอัตโนมัติ - กฎความปลอดภัยเป็นสิ่งจำเป็นสำหรับ production ควรตรวจสอบ `request.auth.uid` เทียบกับฟิลด์ความเป็นเจ้าของของ document เสมอ - การคงอยู่แบบออฟไลน์ทำงานอย่างโปร่งใสบนมือถือ ใช้ `hasPendingWrites` เพื่อสื่อสารสถานะการซิงค์ให้ผู้ใช้ทราบ - รูปแบบ repository แยก Firebase ออกจากตรรกะทางธุรกิจ ทำให้[การทดสอบ](/blog/flutter/flutter-testing-widget-integration-interview-2026)และการย้ายในอนาคตเป็นเรื่องง่าย - การเตรียมตัวสัมภาษณ์ควรครอบคลุมความแตกต่างของสตรีม auth, พฤติกรรมออฟไลน์, รูปแบบการสร้างแบบจำลองข้อมูล และข้อกำหนดของ composite index --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/th/blog/flutter/flutter-firebase-authentication-firestore-2026