Flutter และ Firebase ในปี 2026: การยืนยันตัวตน, Firestore และคำถามสัมภาษณ์

เจาะลึก Flutter Firebase: การยืนยันตัวตนด้วย firebase_auth, CRUD ของ Firestore และสตรีมแบบเรียลไทม์, กฎความปลอดภัย และคำถามสัมภาษณ์ที่พบบ่อยพร้อมตัวอย่างโค้ด

การผสานรวม Flutter และ Firebase แสดงสถาปัตยกรรมการยืนยันตัวตนและฐานข้อมูล Firestore

การผสานรวม 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

main.dartdart
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 ที่ยืนยันตัวตนแล้ว

auth_service.dartdart
import 'package:firebase_auth/firebase_auth.dart';

class AuthService {
  final FirebaseAuth _auth = FirebaseAuth.instance;

  // Register with email and password
  Future<User?> 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<User?> signIn(String email, String password) async {
    final credential = await _auth.signInWithEmailAndPassword(
      email: email,
      password: password,
    );
    return credential.user;
  }

  // Reactive auth state stream
  Stream<User?> get authStateChanges => _auth.authStateChanges();
}

สตรีม authStateChanges() เป็นวิธีที่แนะนำในการติดตามสถานะการเข้าสู่ระบบทั่วทั้งแอป โดยจะส่งค่า User ปัจจุบันเมื่อมีการสมัครรับข้อมูล และทำงานอีกครั้งในทุกเหตุการณ์การเข้าสู่ระบบหรือออกจากระบบ

การผสานรวม Google Sign-In

ผู้ให้บริการ OAuth ต้องใช้แพ็กเกจเพิ่มเติม Google Sign-In จะเรียกตัวเลือกบัญชีแบบเนทีฟ จากนั้นแลกเปลี่ยนโทเค็นกับ Firebase

google_auth.dartdart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';

Future<UserCredential> 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

การดำเนินการ CRUD ของ Firestore และการสร้างแบบจำลองข้อมูล

Cloud Firestore จัดระเบียบข้อมูลเป็น collection และ document ต่างจากฐานข้อมูล SQL ตรงที่ Firestore ไม่มี schema เอกสารแต่ละฉบับสามารถมีฟิลด์ที่แตกต่างกันได้ ความยืดหยุ่นนี้เหมาะกับแอป Flutter แต่ต้องการการสร้างแบบจำลองข้อมูลอย่างมีวินัยเพื่อหลีกเลี่ยงปัญหาด้านประสิทธิภาพ

การเขียนและการอ่าน Document

การดำเนินการของ Firestore ทำได้อย่างตรงไปตรงมาด้วยแพ็กเกจ cloud_firestore ทุกการอ่านและการเขียนจะมุ่งเป้าไปที่เส้นทางของ document ที่เฉพาะเจาะจง

firestore_service.dartdart
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<String> 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<Map<String, dynamic>?> 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<void> toggleComplete(String taskId, bool completed) async {
    await _db.collection(_collection).doc(taskId).update({
      'completed': completed,
      'updatedAt': FieldValue.serverTimestamp(),
    });
  }

  // Delete a document
  Future<void> deleteTask(String taskId) async {
    await _db.collection(_collection).doc(taskId).delete();
  }
}

FieldValue.serverTimestamp() รับประกัน timestamp ที่สอดคล้องกันในทุกอุปกรณ์ โดยใช้นาฬิกาของเซิร์ฟเวอร์ Firestore แทนนาฬิกาของไคลเอนต์

สตรีมแบบเรียลไทม์ด้วย snapshots()

Firestore โดดเด่นด้วยการซิงโครไนซ์แบบเรียลไทม์ เมธอด snapshots() คืนค่า Stream ที่ส่งข้อมูลใหม่ทุกครั้งที่ document ที่เกี่ยวข้องมีการเปลี่ยนแปลง โดยไม่ต้อง polling

real_time_tasks.dartdart
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<List<Map<String, dynamic>>> 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 จัดการการเชื่อมต่อ การแคช และการคงอยู่แบบออฟไลน์โดยอัตโนมัติ

พร้อมที่จะพิชิตการสัมภาษณ์ Flutter แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

กฎความปลอดภัยของ Firestore สำหรับแอป Flutter

กฎความปลอดภัยทำงานบนเซิร์ฟเวอร์ Firestore และควบคุมสิทธิ์การอ่าน/เขียนในระดับ document หากไม่มีกฎที่เหมาะสม ผู้ใช้ที่ยืนยันตัวตนแล้วทุกคนสามารถอ่านหรือแก้ไข document ใดก็ได้

firestore.rulesjavascript
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 เพื่อแสดงให้ผู้ใช้ทราบว่าการเปลี่ยนแปลงในเครื่องยังไม่ได้ซิงค์ไปยังเซิร์ฟเวอร์
offline_aware_widget.dartdart
StreamBuilder<DocumentSnapshot>(
  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 มอบชั้นการนามธรรมที่ชัดเจนซึ่งช่วยให้การทดสอบและการย้ายแบ็กเอนด์ในอนาคตง่ายขึ้น

task_repository.dartdart
abstract class TaskRepository {
  Future<String> create(String userId, String title);
  Stream<List<Task>> watchAll(String userId);
  Future<void> update(String id, Map<String, dynamic> fields);
  Future<void> delete(String id);
}

// firebase_task_repository.dart
class FirebaseTaskRepository implements TaskRepository {
  final _db = FirebaseFirestore.instance;

  
  Future<String> 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;
  }

  
  Stream<List<Task>> 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 จะใช้อินเทอร์เฟซ 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 สำหรับ query ที่รวมหลายฟิลด์ด้วยอนุประโยค where และ orderBy index แบบฟิลด์เดียวจะถูกสร้างโดยอัตโนมัติ แต่ composite index ต้องถูกกำหนดด้วยตนเองใน firestore.indexes.json หรือผ่าน Firebase console Firestore จะบันทึกลิงก์โดยตรงเพื่อสร้าง index ที่ขาดหายไปเมื่อ query ล้มเหลว

สำหรับการเตรียมตัวสัมภาษณ์ Flutter เพิ่มเติม โมดูลฝึกฝนเฉพาะทางครอบคลุมหัวข้อเหล่านี้ด้วยแบบทดสอบเชิงโต้ตอบและความท้าทายแบบจับเวลา

เริ่มฝึกซ้อมเลย!

ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ

บทสรุป

  • 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 ออกจากตรรกะทางธุรกิจ ทำให้การทดสอบและการย้ายในอนาคตเป็นเรื่องง่าย
  • การเตรียมตัวสัมภาษณ์ควรครอบคลุมความแตกต่างของสตรีม auth, พฤติกรรมออฟไลน์, รูปแบบการสร้างแบบจำลองข้อมูล และข้อกำหนดของ composite index

เริ่มฝึกซ้อมเลย!

ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ

ชาเลนจ์ประจำวัน

คุณหาบั๊กใน Flutter เจอไหม

โค้ดจริงหนึ่งชิ้น บั๊กที่ซ่อนอยู่หนึ่งจุด วันละหนึ่งครั้ง ลองได้โดยไม่ต้องมีบัญชี

Anthony Fillion-Maillet

เขียนโดย

Anthony Fillion-Maillet

ผู้ก่อตั้ง SharpSkill

เป็นนักพัฒนาฟูลสแตกมากว่า 10 ปี ดูแล SharpSkill และรับผิดชอบทุกสิ่งที่เผยแพร่ที่นี่

อัปเดตเมื่อ 17 มิถุนายน 2569

แท็ก

#flutter
#firebase
#authentication
#firestore
#dart
#mobile development

แชร์

บทความที่เกี่ยวข้อง

คำถามสัมภาษณ์งาน Flutter สำหรับนักพัฒนาแอปมือถือ

20 คำถามสัมภาษณ์งาน Flutter ที่พบบ่อยที่สุดสำหรับนักพัฒนาแอปมือถือ

เตรียมตัวสัมภาษณ์งาน Flutter ด้วยคำถาม 20 ข้อที่ถูกถามบ่อยที่สุด ครอบคลุม Widget, State Management, Dart, สถาปัตยกรรม และแนวทางปฏิบัติที่ดีที่สุด พร้อมคำอธิบายอย่างละเอียดและตัวอย่างโค้ด

คู่มือการสร้างแอปพลิเคชันมือถือตัวแรกด้วย Flutter และ Dart

Flutter: สร้างแอปพลิเคชันข้ามแพลตฟอร์มตัวแรก

คู่มือฉบับสมบูรณ์สำหรับการสร้างแอปพลิเคชันมือถือข้ามแพลตฟอร์มด้วย Flutter และ Dart ครอบคลุม Widget การจัดการสถานะ การนำทาง และแนวทางปฏิบัติที่ดีสำหรับผู้เริ่มต้น

Dart Isolates และ Concurrency ใน Flutter 2026

Dart Isolates และ Concurrency 2026: compute, Async และคำถามสัมภาษณ์

คู่มือฉบับสมบูรณ์เกี่ยวกับ Dart isolates, ฟังก์ชัน compute ใน Flutter และ pattern ของ concurrency สำหรับการพัฒนาแอปที่ตอบสนองรวดเร็วในปี 2026