การพัฒนาแอป Flutter ในเครื่องโดยใช้ Firebase Emulator Suite

1. ก่อนเริ่มต้น

ใน Codelab นี้ คุณจะได้เรียนรู้วิธีใช้ชุดโปรแกรมจำลอง Firebase กับ Flutter ระหว่างการพัฒนาในเครื่อง คุณจะได้ดูวิธีใช้การตรวจสอบสิทธิ์อีเมลด้วยรหัสผ่านผ่านชุดโปรแกรมจำลอง และวิธีอ่านและเขียนข้อมูลลงในโปรแกรมจำลอง Firestore สุดท้าย ให้ใช้การนำเข้าและส่งออกข้อมูลจากโปรแกรมจำลอง เพื่อทำงานกับข้อมูลปลอมเดียวกันทุกครั้งที่กลับไปที่การพัฒนาซอฟต์แวร์

ข้อกำหนดเบื้องต้น

Codelab นี้จะสมมติว่าคุณมีประสบการณ์การใช้งาน Flutter อยู่บ้าง หากไม่ คุณอาจต้องเรียนรู้ข้อมูลเบื้องต้นก่อน ลิงก์ต่อไปนี้มีประโยชน์

คุณควรมีประสบการณ์การใช้งาน Firebase อยู่บ้าง แต่ไม่เคยเพิ่ม Firebase ในโปรเจ็กต์ Flutter เลย หากคุณไม่คุ้นเคยกับคอนโซล Firebase หรือเพิ่งเริ่มใช้ Firebase เลย โปรดดูลิงก์ต่อไปนี้ก่อน

สิ่งที่คุณจะสร้าง

Codelab นี้จะแนะนำคุณตลอดการสร้างแอปพลิเคชันรายการบันทึกที่ใช้งานง่าย แอปพลิเคชันจะมีหน้าจอการเข้าสู่ระบบและหน้าจอที่ช่วยให้คุณอ่านรายการบันทึกที่ผ่านมา และสร้างรายการใหม่ได้

cd5c4753bbee8af.png 8cb4d21f656540bf.png

สิ่งที่คุณจะได้เรียนรู้

คุณจะได้ดูวิธีเริ่มใช้ Firebase รวมถึงวิธีผสานรวมและใช้ชุดโปรแกรมจำลอง Firebase ในเวิร์กโฟลว์การพัฒนา Flutter เราจะกล่าวถึงหัวข้อ Firebase ต่อไปนี้

โปรดทราบว่าหัวข้อเหล่านี้จะครอบคลุมเท่าที่จำเป็นต้องครอบคลุมชุดโปรแกรมจำลอง Firebase Codelab นี้มุ่งเน้นที่การเพิ่มโปรเจ็กต์ Firebase ลงในแอป Flutter และการพัฒนาโดยใช้ Firebase Emulator Suite จะไม่มีการพูดคุยเชิงลึกเกี่ยวกับการตรวจสอบสิทธิ์ Firebase หรือ Firestore หากคุณไม่คุ้นเคยกับหัวข้อเหล่านี้ เราขอแนะนำให้เริ่มต้นด้วยหัวข้อทำความรู้จัก Firebase สำหรับ Flutter Codelab

สิ่งที่ต้องมี

  • ความรู้ในการใช้งาน Flutter และติดตั้ง SDK
  • Intellij JetBrains หรือตัวแก้ไขข้อความ VS Code
  • เบราว์เซอร์ Google Chrome (หรือเป้าหมายการพัฒนาอื่นๆ ที่ต้องการสำหรับ Flutter คำสั่งเทอร์มินัลบางคำสั่งใน Codelab จะถือว่าคุณกำลังเรียกใช้แอปใน Chrome)

2. สร้างและตั้งค่าโปรเจ็กต์ Firebase

งานแรกที่คุณต้องทำให้เสร็จคือสร้างโปรเจ็กต์ Firebase ในเว็บคอนโซลของ Firebase Codelab ส่วนใหญ่นี้จะมุ่งเน้นที่ชุดโปรแกรมจำลอง ซึ่งใช้ UI ที่ทำงานภายในเครื่อง แต่คุณต้องตั้งค่าโปรเจ็กต์ Firebase ที่สมบูรณ์ก่อน

สร้างโปรเจ็กต์ Firebase

  1. ลงชื่อเข้าใช้คอนโซล Firebase
  2. ในคอนโซล Firebase ให้คลิกเพิ่มโปรเจ็กต์ (หรือสร้างโปรเจ็กต์) แล้วป้อนชื่อโปรเจ็กต์ Firebase (เช่น "Firebase-Flutter-Codelab")

fe6aeab3b91965ed.png

  1. คลิกตัวเลือกการสร้างโปรเจ็กต์ ยอมรับข้อกำหนดของ Firebase หากได้รับข้อความแจ้ง ข้ามการตั้งค่า Google Analytics เนื่องจากคุณจะไม่ใช้ Analytics สําหรับแอปนี้

d1fcec48bf251eaa.png

ดูข้อมูลเพิ่มเติมเกี่ยวกับโปรเจ็กต์ Firebase ได้ที่ทำความเข้าใจโปรเจ็กต์ Firebase

แอปที่คุณกำลังสร้างใช้ผลิตภัณฑ์ Firebase 2 รายการที่พร้อมใช้งานสำหรับแอป Flutter ดังนี้

  • การตรวจสอบสิทธิ์ของ Firebase เพื่ออนุญาตให้ผู้ใช้ลงชื่อเข้าใช้แอปได้
  • Cloud Firestore เพื่อบันทึก Structured Data ในระบบคลาวด์และรับการแจ้งเตือนทันทีเมื่อข้อมูลมีการเปลี่ยนแปลง

ผลิตภัณฑ์ทั้ง 2 รายการนี้ต้องมีการกำหนดค่าพิเศษหรือต้องเปิดใช้โดยใช้คอนโซล Firebase

เปิดใช้ Cloud Firestore

แอป Flutter ใช้ Cloud Firestore เพื่อบันทึกรายการบันทึก

เปิดใช้ Cloud Firestore

  1. ในส่วนสร้างของคอนโซล Firebase ให้คลิก Cloud Firestore
  2. คลิกสร้างฐานข้อมูล 99e8429832d23fa3.png
  3. เลือกตัวเลือกเริ่มในโหมดทดสอบ อ่านข้อจำกัดความรับผิดเกี่ยวกับกฎความปลอดภัย โหมดทดสอบช่วยให้คุณเขียนไปยังฐานข้อมูลได้อย่างอิสระในระหว่างการพัฒนา คลิกถัดไป 6be00e26c72ea032.png
  4. เลือกตำแหน่งสำหรับฐานข้อมูล (คุณสามารถใช้ค่าเริ่มต้นได้) โปรดทราบว่าตำแหน่งนี้เปลี่ยนแปลงในภายหลังไม่ได้ 278656eefcfb0216.png
  5. คลิกเปิดใช้

3. ตั้งค่าแอป Flutter

คุณจะต้องดาวน์โหลดโค้ดเริ่มต้น และติดตั้ง Firebase CLI ก่อนที่เราจะเริ่มต้น

รับรหัสเริ่มต้น

โคลนที่เก็บ GitHub จากบรรทัดคำสั่งดังนี้

git clone https://github.com/flutter/codelabs.git flutter-codelabs

หรือหากคุณติดตั้งเครื่องมือ cli ของ GitHub ไว้ ให้ทำดังนี้

gh repo clone flutter/codelabs flutter-codelabs

ควรโคลนโค้ดตัวอย่างลงในไดเรกทอรี flutter-codelabs ซึ่งมีโค้ดสำหรับคอลเล็กชัน Codelab โค้ดสำหรับ Codelab นี้คือ flutter-codelabs/firebase-emulator-suite

โครงสร้างไดเรกทอรีภายใต้ flutter-codelabs/firebase-emulator-suite ประกอบด้วยโปรเจ็กต์ Flutter 2 โปรเจ็กต์ โค้ดแรกเรียกว่า complete ซึ่งคุณจะดูได้หากต้องการข้ามหรืออ้างอิงโค้ดของคุณเอง อีกโปรเจ็กต์หนึ่งมีชื่อว่า start

รหัสที่คุณต้องการใช้เริ่มต้นอยู่ในไดเรกทอรี flutter-codelabs/firebase-emulator-suite/start เปิดหรือนำเข้าไดเรกทอรีนั้นไปยัง IDE ที่ต้องการ

cd flutter-codelabs/firebase-emulator-suite/start

ติดตั้ง Firebase CLI

Firebase CLI มีเครื่องมือสำหรับการจัดการโปรเจ็กต์ Firebase ต้องมี CLI เพื่อใช้ชุดโปรแกรมจำลอง คุณจึงต้องติดตั้ง CLI

การติดตั้ง CLI ทำได้หลายวิธี วิธีที่ง่ายที่สุดในการใช้ MacOS หรือ Linux คือเรียกใช้คำสั่งนี้จากเทอร์มินัล

curl -sL https://firebase.tools | bash

หลังจากติดตั้ง CLI คุณต้องตรวจสอบสิทธิ์กับ Firebase

  1. เข้าสู่ระบบ Firebase โดยใช้บัญชี Google โดยเรียกใช้คำสั่งต่อไปนี้
firebase login
  1. คำสั่งนี้จะเชื่อมต่อเครื่องภายในของคุณกับ Firebase และให้สิทธิ์คุณเข้าถึงโปรเจ็กต์ Firebase
  1. ทดสอบว่า CLI ติดตั้งอย่างถูกต้องและมีสิทธิ์เข้าถึงบัญชีโดยแสดงรายการโปรเจ็กต์ Firebase เรียกใช้คำสั่งต่อไปนี้
firebase projects:list
  1. รายการที่แสดงควรเหมือนกับโปรเจ็กต์ Firebase ที่ระบุไว้ในคอนโซล Firebase อย่างน้อยคุณควรเห็น firebase-flutter-codelab

ติดตั้ง FlutterFire CLI

FlutterFire CLI สร้างขึ้นต่อยอดจาก Firebase CLI และทำให้การผสานรวมโปรเจ็กต์ Firebase กับแอป Flutter ง่ายขึ้น

ก่อนอื่น ให้ติดตั้ง CLI โดยทำดังนี้

dart pub global activate flutterfire_cli

ตรวจสอบว่าติดตั้ง CLI แล้ว เรียกใช้คำสั่งต่อไปนี้ภายในไดเรกทอรีโปรเจ็กต์ Flutter และตรวจสอบว่า CLI แสดงผลเมนูความช่วยเหลือ

flutterfire --help

ใช้ Firebase CLI และ FlutterFire CLI เพื่อเพิ่มโปรเจ็กต์ Firebase ลงในแอป Flutter

เมื่อติดตั้ง CLI ทั้ง 2 รายการแล้ว คุณสามารถตั้งค่าผลิตภัณฑ์ Firebase แต่ละรายการ (เช่น Firestore) ดาวน์โหลดโปรแกรมจำลอง และเพิ่ม Firebase ไปยังแอป Flutter ได้ด้วยคำสั่งเทอร์มินัลเพียง 2-3 คำสั่ง

ก่อนอื่น ให้ตั้งค่า Firebase ให้เสร็จโดยการเรียกใช้โค้ดต่อไปนี้

firebase init

คำสั่งนี้จะนำคุณไปยังชุดคำถามที่ต้องใช้ในการสร้างโปรเจ็กต์ ภาพหน้าจอต่อไปนี้จะแสดงขั้นตอน

  1. เมื่อมีข้อความแจ้งให้เลือกฟีเจอร์ ให้เลือก "Firestore" และ "โปรแกรมจำลอง" (ไม่มีตัวเลือกการตรวจสอบสิทธิ์ เนื่องจากไม่ได้ใช้การกำหนดค่าที่แก้ไขได้จากไฟล์โปรเจ็กต์ Flutter) fe6401d769be8f53.png
  2. ถัดไป ให้เลือก "ใช้โปรเจ็กต์ที่มีอยู่" เมื่อมีข้อความแจ้ง

f11dcab439e6ac1e.png

  1. ต่อไปให้เลือกโปรเจ็กต์ที่คุณสร้างในขั้นตอนก่อนหน้า: flutter-firebase-codelab

3bdc0c6934991c25.png

  1. จากนั้น เราจะถามคำถามชุดหนึ่งเกี่ยวกับการตั้งชื่อไฟล์ที่จะสร้างขึ้น ฉันขอแนะนำให้กด "Enter" สำหรับแต่ละคำถามเพื่อเลือกค่าเริ่มต้น 9bfa2d507e199c59.png
  2. และสุดท้าย คุณจะต้องกำหนดค่าโปรแกรมจำลอง เลือก Firestore และการตรวจสอบสิทธิ์จากรายการ แล้วกด "Enter" สำหรับคำถามแต่ละข้อเกี่ยวกับพอร์ตที่จะใช้สำหรับโปรแกรมจำลองแต่ละตัว คุณควรเลือกค่าเริ่มต้นเป็น ใช่ เมื่อระบบถามว่าคุณต้องการใช้ UI โปรแกรมจำลองหรือไม่

ในตอนท้ายของกระบวนการ คุณควรเห็นผลลัพธ์ที่คล้ายกับภาพหน้าจอต่อไปนี้

สำคัญ: ผลลัพธ์ของคุณอาจแตกต่างไปจากนี้เล็กน้อยตามที่แสดงในภาพหน้าจอด้านล่าง เนื่องจากคำถามสุดท้ายจะมีค่าเริ่มต้นเป็น "ไม่" หากคุณดาวน์โหลดโปรแกรมจำลองไว้แล้ว

8544e41037637b07.png

กำหนดค่า FlutterFire

ถัดไป คุณจะใช้ FlutterFire เพื่อสร้างโค้ดของ Dart ที่ต้องใช้ Firebase ในแอป Flutter ได้

flutterfire configure

เมื่อเรียกใช้คำสั่งนี้ คุณจะได้รับแจ้งให้เลือกโปรเจ็กต์ Firebase ที่ต้องการใช้และแพลตฟอร์มที่ต้องการตั้งค่า ตัวอย่างใน Codelab นี้ใช้ Flutter Web แต่คุณตั้งค่าโปรเจ็กต์ Firebase ให้ใช้ตัวเลือกทั้งหมดได้

ภาพหน้าจอต่อไปนี้แสดงข้อความแจ้งให้ตอบ

619b7aca6dc15472.png 301c9534f594f472.png

ภาพหน้าจอนี้แสดงเอาต์พุตในช่วงท้ายของกระบวนการ หากคุ้นเคยกับ Firebase อยู่แล้ว คุณจะสังเกตเห็นว่าคุณไม่จำเป็นต้องสร้างแอปพลิเคชันในคอนโซล ซึ่ง FlutterFire CLI สร้างให้คุณเอง

12199a85ade30459.png

เพิ่มแพ็กเกจ Firebase ลงในแอป Flutter

ขั้นตอนสุดท้ายคือการเพิ่มแพ็กเกจ Firebase ที่เกี่ยวข้องลงในโปรเจ็กต์ Flutter ของคุณ ในเทอร์มินัล ให้ตรวจสอบว่าคุณอยู่ในรูทของโปรเจ็กต์ Flutter ที่ flutter-codelabs/firebase-emulator-suite/start จากนั้นเรียกใช้คำสั่ง 3 คำสั่งต่อไปนี้

flutter pub add firebase_core
flutter pub add firebase_auth
flutter pub add cloud_firestore

แพ็กเกจเหล่านี้จะเป็นแพ็กเกจเดียวที่คุณจะใช้ในแอปพลิเคชันนี้

4. การเปิดใช้โปรแกรมจำลอง Firebase

ถึงตอนนี้แอป Flutter และโปรเจ็กต์ Firebase ได้รับการตั้งค่าให้ใช้โปรแกรมจำลองได้แล้ว แต่คุณยังต้องบอกให้โค้ด Flutter เปลี่ยนเส้นทางคำขอ Firebase ขาออกไปยังพอร์ตในเครื่อง

ขั้นแรก ให้เพิ่มโค้ดการเริ่มต้น Firebase และโค้ดการตั้งค่าโปรแกรมจำลองลงในฟังก์ชัน main ใน main.dart.

main.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

import 'app_state.dart';
import 'firebase_options.dart';
import 'logged_in_view.dart';
import 'logged_out_view.dart';


void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Firebase.initializeApp(
   options: DefaultFirebaseOptions.currentPlatform,
 );

 if (kDebugMode) {
   try {
     FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080);
     await FirebaseAuth.instance.useAuthEmulator('localhost', 9099);
   } catch (e) {
     // ignore: avoid_print
     print(e);
   }
 }

 runApp(MyApp());
}

โค้ด 2-3 บรรทัดแรกจะเริ่มต้น Firebase หากคุณใช้งาน Firebase ในแอป Flutter อยู่เกือบทั้งหมด คุณจะต้องเริ่มต้นด้วยการโทรหา WidgetsFlutterBinding.ensureInitialized และ Firebase.initializeApp

หลังจากนั้น โค้ดที่ขึ้นต้นด้วยบรรทัด if (kDebugMode) จะบอกแอปของคุณให้กำหนดเป้าหมายโปรแกรมจำลองแทนโปรเจ็กต์ Firebase ที่ใช้งานจริง kDebugMode ช่วยให้มั่นใจว่าการกำหนดเป้าหมายโปรแกรมจำลองจะเกิดขึ้นเฉพาะเมื่ออยู่ในสภาพแวดล้อมการพัฒนาซอฟต์แวร์เท่านั้น เนื่องจาก kDebugMode เป็นค่าคงที่ คอมไพเลอร์ของ Dart จะรู้ว่าต้องนำโค้ดบล็อกดังกล่าวออกทั้งหมดในโหมดเผยแพร่

เริ่มต้นโปรแกรมจำลอง

คุณควรเริ่มโปรแกรมจำลองก่อนเริ่มแอป Flutter ก่อนอื่น ให้เริ่มต้นโปรแกรมจำลองโดยการเรียกใช้โปรแกรมในเทอร์มินัล

firebase emulators:start

คำสั่งนี้จะเปิดเครื่องจำลองให้เริ่มทำงาน และแสดงพอร์ต localhost ที่เราสามารถโต้ตอบด้วยได้ เมื่อเรียกใช้คำสั่งดังกล่าว คุณควรเห็นเอาต์พุตในลักษณะนี้

bb7181eb70829606.png

เอาต์พุตนี้จะบอกคุณว่าโปรแกรมจำลองใดทำงานอยู่ และสามารถดูโปรแกรมจำลองได้จากที่ใด ขั้นแรก โปรดดู UI โปรแกรมจำลองที่ localhost:4000

11563f4c7216de81.png

นี่คือหน้าแรกของ UI ของโปรแกรมจำลองในเครื่อง ซึ่งจะแสดงรายการโปรแกรมจำลองทั้งหมดที่มี และแต่ละโปรแกรมจะมีป้ายกำกับสถานะเป็นเปิดหรือปิด

5. โปรแกรมจำลองการตรวจสอบสิทธิ์ Firebase

โปรแกรมจำลองแรกที่คุณจะใช้คือโปรแกรมจำลองการตรวจสอบสิทธิ์ เริ่มด้วยโปรแกรมจำลองการตรวจสอบสิทธิ์โดยคลิก "ไปที่โปรแกรมจำลอง" บนการ์ดการตรวจสอบสิทธิ์ใน UI และคุณจะเห็นหน้าเว็บดังนี้

3c1bfded40733189.png

หน้านี้มีความคล้ายคลึงกับหน้าเว็บคอนโซลการตรวจสอบสิทธิ์ โดยมีตารางแสดงรายการผู้ใช้ เช่น คอนโซลออนไลน์ และอนุญาตให้คุณเพิ่มผู้ใช้ด้วยตนเอง ความแตกต่างที่สำคัญอย่างหนึ่งคือ ตัวเลือกวิธีการตรวจสอบสิทธิ์เดียวที่มีอยู่ในโปรแกรมจำลองคือผ่านอีเมลและรหัสผ่าน ซึ่งเพียงพอสำหรับการพัฒนาในท้องถิ่น

ต่อไปเป็นขั้นตอนการเพิ่มผู้ใช้ลงในโปรแกรมจำลองการตรวจสอบสิทธิ์ Firebase จากนั้นจึงเข้าสู่ระบบให้ผู้ใช้ดังกล่าวผ่าน UI ของ Flutter

เพิ่มผู้ใช้

คลิก "เพิ่มผู้ใช้" แล้วกรอกแบบฟอร์มพร้อมข้อมูลต่อไปนี้

  • ชื่อที่แสดง: ขีดกลาง
  • อีเมล: dash@email.com
  • รหัสผ่าน: เครื่องหมายขีดกลาง

ส่งแบบฟอร์ม แล้วคุณจะเห็นตารางที่มีผู้ใช้ ตอนนี้คุณจะอัปเดตโค้ดเพื่อเข้าสู่ระบบด้วยผู้ใช้รายนั้นได้

loggle_out_view.dart

รหัสเดียวในวิดเจ็ต LoggedOutView ที่ต้องอัปเดตจะอยู่ใน Callback ที่ทริกเกอร์เมื่อผู้ใช้กดปุ่มเข้าสู่ระบบ อัปเดตโค้ดให้มีลักษณะดังนี้

class LoggedOutView extends StatelessWidget {
 final AppState state;
 const LoggedOutView({super.key, required this.state});
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: const Text('Firebase Emulator Suite Codelab'),
     ),
     body: Center(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: [
          Text(
           'Please log in',
            style: Theme.of(context).textTheme.displaySmall,
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: ElevatedButton(
             onPressed: () async {
              await state.logIn('dash@email.com', 'dashword').then((_) {
                if (state.user != null) {
                 context.go('/');
                }
              });
              },
              child: const Text('Log In'),
          ),
        ),
      ],
    ),
   ),
  );
 }
}

โค้ดที่อัปเดตจะแทนที่สตริง TODO ด้วยอีเมลและรหัสผ่านที่คุณสร้างไว้ในโปรแกรมจำลองการตรวจสอบสิทธิ์ และในบรรทัดถัดไป บรรทัด if(true) จะถูกแทนที่ด้วยโค้ดที่ตรวจสอบว่า state.user เป็นค่าว่างหรือไม่ โค้ดใน AppClass ช่วยให้เข้าใจเรื่องนี้มากขึ้น

app_state.dart

ต้องอัปเดตโค้ด 2 ส่วนใน AppState ขั้นแรก ให้สมาชิกคลาส AppState.user ประเภท User จากแพ็กเกจ firebase_auth แทนประเภท Object

ขั้นที่ 2 กรอกเมธอด AppState.login ตามที่แสดงด้านล่าง

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'entry.dart';

class AppState {
 AppState() {
   _entriesStreamController = StreamController.broadcast(onListen: () {
     _entriesStreamController.add([
       Entry(
         date: '10/09/2022',
         text: lorem,
         title: '[Example] My Journal Entry',
       )
     ]);
   });
 }

 User? user; // <-- changed variable type
 Stream<List<Entry>> get entries => _entriesStreamController.stream;
 late final StreamController<List<Entry>> _entriesStreamController;

 Future<void> logIn(String email, String password) async {
   final credential = await FirebaseAuth.instance
       .signInWithEmailAndPassword(email: email, password: password);
   if (credential.user != null) {
     user = credential.user!;
     _listenForEntries();
   } else {
     print('no user!');
   }
 } 
 // ...
}

คำจำกัดความประเภทสำหรับผู้ใช้ตอนนี้คือ User? คลาส User นั้นมาจาก Firebase Auth และให้ข้อมูลที่จำเป็น เช่น User.displayName ซึ่งจะกล่าวถึงในอีกไม่ช้า

โค้ดนี้เป็นโค้ดพื้นฐานที่จำเป็นในการเข้าสู่ระบบผู้ใช้ด้วยอีเมลและรหัสผ่านใน Firebase Auth โดยจะเรียกใช้ FirebaseAuth เพื่อลงชื่อเข้าใช้ ซึ่งจะแสดงออบเจ็กต์ Future<UserCredential> เมื่อการดำเนินการในอนาคตเสร็จสมบูรณ์ โค้ดนี้จะตรวจสอบว่ามี User แนบอยู่กับ UserCredential หรือไม่ หากมีผู้ใช้อยู่ในออบเจ็กต์ข้อมูลเข้าสู่ระบบ แสดงว่าผู้ใช้เข้าสู่ระบบสำเร็จแล้วและตั้งค่าพร็อพเพอร์ตี้ AppState.user ได้ หากไม่มี แสดงว่ามีข้อผิดพลาดและพิมพ์อยู่

โปรดทราบว่าโค้ดบรรทัดเดียวในเมธอดนี้ที่ใช้กับแอปนี้โดยเฉพาะ (ไม่ใช่โค้ด FirebaseAuth ทั่วไป) คือการเรียกใช้เมธอด _listenForEntries ซึ่งจะกล่าวถึงในขั้นตอนถัดไป

สิ่งที่ต้องทำ: ไอคอนการทำงาน – โหลดแอปของคุณซ้ำ แล้วกดปุ่มเข้าสู่ระบบเมื่อแสดงผล ซึ่งจะทำให้แอปไปยังหน้าที่ระบุว่า "ยินดีต้อนรับกลับมานะ คน!" ที่ด้านบน การตรวจสอบสิทธิ์นั้นต้องได้ผลเพราะอนุญาตให้คุณไปที่หน้านี้ได้ แต่จำเป็นต้องอัปเดต logged_in_view.dart เล็กน้อยเพื่อให้แสดงชื่อจริงของผู้ใช้ได้

loggle_in_view.dart

เปลี่ยนบรรทัดแรกในเมธอด LoggedInView.build ดังนี้

class LoggedInView extends StatelessWidget {
 final AppState state;
 LoggedInView({super.key, required this.state});

 final PageController _controller = PageController(initialPage: 1);

 @override
 Widget build(BuildContext context) {
   final name = state.user!.displayName ?? 'No Name';

   return Scaffold(
 // ...

ในตอนนี้ บรรทัดนี้จะดึง displayName จากพร็อพเพอร์ตี้ User บนออบเจ็กต์ AppState มีการตั้งค่า displayName นี้ในโปรแกรมจำลองเมื่อคุณกำหนดผู้ใช้รายแรก ตอนนี้แอปของคุณควรแสดง "ยินดีต้อนรับกลับมานะ Dash!" เมื่อคุณลงชื่อเข้าสู่ระบบ แทนที่จะเป็น TODO

6. อ่านและเขียนข้อมูลไปยังโปรแกรมจำลอง Firestore

ขั้นแรก ลองดูโปรแกรมจำลอง Firestore ในหน้าแรกของ UI โปรแกรมจำลอง (localhost:4000) ให้คลิก "ไปที่โปรแกรมจำลอง" ในการ์ด Firestore ซึ่งควรมีลักษณะดังนี้

โปรแกรมจำลอง:

791fce7dc137910a.png

คอนโซล Firebase

e0dde9aea34af050.png

หากคุณเคยใช้ Firestore คุณจะพบว่าหน้านี้ดูคล้ายกับหน้า Firestore ในคอนโซล Firebase แต่ก็มีข้อแตกต่างที่สำคัญอยู่บ้าง

  1. คุณสามารถล้างข้อมูลทั้งหมดได้โดยแตะเพียงปุ่มเดียว กระบวนการนี้อาจเป็นอันตรายต่อข้อมูลการผลิต แต่ก็มีประโยชน์สําหรับการทำซ้ำอย่างรวดเร็ว หากคุณกำลังทำงานในโปรเจ็กต์ใหม่และรูปแบบข้อมูลมีการเปลี่ยนแปลง คุณสามารถล้างออกได้
  2. มี "คำขอ" แท็บนี้ช่วยให้คุณดูคำขอที่เข้ามาใหม่ซึ่งส่งไปยังโปรแกรมจำลองนี้ได้ เราจะพูดถึงแท็บนี้อย่างละเอียดยิ่งขึ้นในอีกสักครู่
  3. ไม่มีแท็บสำหรับกฎ ดัชนี หรือการใช้งาน มีเครื่องมือ (ที่จะอธิบายในส่วนถัดไป) ที่ช่วยในการเขียนกฎความปลอดภัย แต่คุณตั้งกฎความปลอดภัยสำหรับโปรแกรมจำลองในเครื่องไม่ได้

สรุปง่ายๆ ก็คือ Firestore เวอร์ชันนี้มีเครื่องมือที่เป็นประโยชน์มากกว่าในระหว่างการพัฒนา รวมทั้งนำเครื่องมือที่ต้องมีในเวอร์ชันที่ใช้งานจริงออกด้วย

เขียนไปยัง Firestore

ก่อนกล่าวถึง "คำขอ" ในโปรแกรมจำลอง ให้ส่งคำขอก่อน การดำเนินการนี้ต้องมีการอัปเดตโค้ด เริ่มต้นด้วยการต่อแบบฟอร์มในแอปเพื่อเขียนบันทึกประจำวันใหม่ Entry ไปยัง Firestore

ขั้นตอนระดับสูงในการส่ง Entry มีดังนี้

  1. ผู้ใช้กรอกแบบฟอร์มและกดปุ่ม Submit
  2. UI เรียกใช้ AppState.writeEntryToFirebase
  3. AppState.writeEntryToFirebase เพิ่มรายการไปยัง Firebase

โค้ดที่เกี่ยวข้องในขั้นตอนที่ 1 หรือ 2 ไม่จำเป็นต้องเปลี่ยนแปลง ระบบจะเพิ่มรหัสเดียวที่ต้องเพิ่มในขั้นตอนที่ 3 ลงในชั้นเรียน AppState ทำการเปลี่ยนแปลงต่อไปนี้เป็น AppState.writeEntryToFirebase

app_state.dart

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'entry.dart';

class AppState {
 AppState() {
   _entriesStreamController = StreamController.broadcast(onListen: () {
     _entriesStreamController.add([
       Entry(
         date: '10/09/2022',
         text: lorem,
         title: '[Example] My Journal Entry',
       )
     ]);
   });
 }

 User? user;
 Stream<List<Entry>> get entries => _entriesStreamController.stream;
 late final StreamController<List<Entry>> _entriesStreamController;

 Future<void> logIn(String email, String password) async {
   final credential = await FirebaseAuth.instance
       .signInWithEmailAndPassword(email: email, password: password);
   if (credential.user != null) {
     user = credential.user!;
     _listenForEntries();
   } else {
     print('no user!');
   }
 }

 void writeEntryToFirebase(Entry entry) {
   FirebaseFirestore.instance.collection('Entries').add(<String, String>{
     'title': entry.title,
     'date': entry.date.toString(),
     'text': entry.text,
   });
 }
 // ...
}

โค้ดในเมธอด WriteEntryToFirebase จะใช้การอ้างอิงคอลเล็กชันที่เรียกว่า "Entries" ใน Firestore จากนั้นเพิ่มรายการใหม่ ซึ่งต้องเป็นประเภท Map<String, String>

ในกรณีนี้ "รายการ" ไม่มีคอลเล็กชันใน Firestore จึงสร้างคอลเล็กชันขึ้น

เมื่อเพิ่มโค้ดแล้ว ให้ Hot โหลดซ้ำหรือรีสตาร์ทแอป ลงชื่อเข้าสู่ระบบ และไปที่มุมมองEntryForm คุณสามารถกรอกข้อมูลในแบบฟอร์มด้วย Strings ได้ตามต้องการ (ฟิลด์วันที่จะใช้สตริงใดก็ได้ เนื่องจากโค้ดแล็บนี้เรียบง่ายขึ้น เนื่องจากไม่มีการตรวจสอบที่หนักแน่นหรือให้ความสำคัญกับออบเจ็กต์ DateTime แต่อย่างใด)

กดส่งในแบบฟอร์ม จะไม่มีสิ่งใดเกิดขึ้นในแอป แต่คุณสามารถดูรายการใหม่ได้ใน UI ของโปรแกรมจำลอง

แท็บคำขอในโปรแกรมจำลอง Firestore

ใน UI ให้ไปที่โปรแกรมจำลอง Firestore แล้วดูที่ "ข้อมูล" คุณจะเห็นว่าตอนนี้มีคอลเล็กชันที่รูทของฐานข้อมูลชื่อ "รายการ" ซึ่งควรมีเอกสารที่มีข้อมูลตรงกับที่คุณป้อนลงในแบบฟอร์ม

a978fb34fb8a83da.png

ข้อความนี้ยืนยันว่า AppState.writeEntryToFirestore ใช้งานได้แล้ว และขณะนี้คุณสามารถดูคำขอเพิ่มเติมได้ในแท็บคำขอ คลิกแท็บนั้นทันที

คำขอโปรแกรมจำลอง Firestore

ที่นี่คุณจะเห็นรายการที่คล้ายกับตัวอย่างนี้

F0b37f0341639035.png

คุณสามารถคลิกเข้าไปในรายการใดก็ได้เพื่อดูข้อมูลที่เป็นประโยชน์มากมาย คลิกที่รายการ CREATE ที่ตรงกับคำขอของคุณในการสร้างรายการบันทึกใหม่ คุณจะเห็นตารางใหม่ที่มีลักษณะดังนี้

385d62152e99aad4.png

ตามที่กล่าวไว้ โปรแกรมจำลอง Firestore มีเครื่องมือในการพัฒนากฎความปลอดภัยของแอป มุมมองนี้จะแสดงบรรทัดในกฎความปลอดภัยที่คำขอนี้ผ่าน (หรือล้มเหลวหากเป็นกรณีดังกล่าว) ในแอปที่มีประสิทธิภาพมากขึ้น กฎความปลอดภัยสามารถขยายตัวได้และต้องมีการตรวจสอบการให้สิทธิ์หลายรายการ มุมมองนี้ใช้เพื่อช่วยเขียนและแก้ไขข้อบกพร่องของกฎการให้สิทธิ์เหล่านั้น

นอกจากนี้ ยังเป็นวิธีง่ายๆ ในการตรวจสอบคำขอทุกรายการนี้ ซึ่งรวมถึงข้อมูลเมตาและข้อมูลการตรวจสอบสิทธิ์ ระบบจะใช้ข้อมูลนี้เพื่อเขียนกฎการให้สิทธิ์ที่ซับซ้อน

กำลังอ่านจาก Firestore

Firestore ใช้การซิงค์ข้อมูลเพื่อพุชข้อมูลที่อัปเดตไปยังอุปกรณ์ที่เชื่อมต่อ โดยคุณจะฟัง (หรือสมัครใช้บริการ) คอลเล็กชันและเอกสารของ Firestore ได้ในโค้ด Flutter และโค้ดจะได้รับการแจ้งเตือนทุกครั้งที่ข้อมูลมีการเปลี่ยนแปลง ในแอปนี้ ระบบจะฟังการอัปเดต Firestore ในเมธอด AppState._listenForEntries

โค้ดนี้จะทำงานร่วมกับ StreamController และ Stream ที่เรียกว่า AppState._entriesStreamController และ AppState.entries ตามลำดับ เราเขียนโค้ดดังกล่าวไว้แล้ว เช่นเดียวกับโค้ดทั้งหมดที่ต้องมีใน UI เพื่อแสดงข้อมูลจาก Firestore

อัปเดตเมธอด _listenForEntries ให้ตรงกับโค้ดด้านล่าง

app_state.dart

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'entry.dart';

class AppState {
 AppState() {
   _entriesStreamController = StreamController.broadcast(onListen: () {
     _entriesStreamController.add([
       Entry(
         date: '10/09/2022',
         text: lorem,
         title: '[Example] My Journal Entry',
       )
     ]);
   });
 }

 User? user;
 Stream<List<Entry>> get entries => _entriesStreamController.stream;
 late final StreamController<List<Entry>> _entriesStreamController;

 Future<void> logIn(String email, String password) async {
   final credential = await FirebaseAuth.instance
       .signInWithEmailAndPassword(email: email, password: password);
   if (credential.user != null) {
     user = credential.user!;
     _listenForEntries();
   } else {
     print('no user!');
   }
 }

 void writeEntryToFirebase(Entry entry) {
   FirebaseFirestore.instance.collection('Entries').add(<String, String>{
     'title': entry.title,
     'date': entry.date.toString(),
     'text': entry.text,
   });
 }

 void _listenForEntries() {
   FirebaseFirestore.instance
       .collection('Entries')
       .snapshots()
       .listen((event) {
     final entries = event.docs.map((doc) {
       final data = doc.data();
       return Entry(
         date: data['date'] as String,
         text: data['text'] as String,
         title: data['title'] as String,
       );
     }).toList();

     _entriesStreamController.add(entries);
   });
 }
 // ...
}

โค้ดนี้จะรับคำสั่ง "รายการ" ใน Firestore เมื่อ Firestore แจ้งไคลเอ็นต์นี้ว่ามีข้อมูลใหม่ ระบบจะส่งผ่านข้อมูลดังกล่าวและโค้ดใน _listenForEntries จะเปลี่ยนเอกสารย่อยทั้งหมดเป็นออบเจ็กต์ที่แอปใช้ได้ (Entry) จากนั้นจะเพิ่มรายการเหล่านี้ลงใน StreamController ที่ชื่อว่า _entriesStreamController (ซึ่ง UI กำลังฟังอยู่) รหัสนี้เป็นรหัสเดียวที่ต้องอัปเดต

สุดท้าย อย่าลืมว่าเมธอด AppState.logIn จะเรียกใช้ _listenForEntries ซึ่งจะเริ่มต้นกระบวนการฟังหลังจากที่ผู้ใช้เข้าสู่ระบบ

// ...
Future<void> logIn(String email, String password) async {
 final credential = await FirebaseAuth.instance
     .signInWithEmailAndPassword(email: email, password: password);
 if (credential.user != null) {
   user = credential.user!;
   _listenForEntries();
 } else {
   print('no user!');
 }
}
// ...

เรียกใช้แอปได้เลย ซึ่งควรมีลักษณะดังนี้

b8a31c7a8900331.gif

7. ส่งออกและนำเข้าข้อมูลไปยังโปรแกรมจำลอง

โปรแกรมจำลอง Firebase รองรับการนำเข้าและส่งออกข้อมูล การใช้การนำเข้าและส่งออกจะช่วยให้คุณพัฒนาต่อไปได้โดยใช้ข้อมูลเดิมเมื่อคุณหยุดพักจากการพัฒนาแล้วค่อยดำเนินการต่อ นอกจากนี้ คุณยังสามารถคอมมิตไฟล์ข้อมูลไปยัง git และนักพัฒนาซอฟต์แวร์รายอื่นๆ ที่ร่วมงานด้วยก็จะมีข้อมูลเดียวกันที่จะใช้งาน

ส่งออกข้อมูลโปรแกรมจำลอง

ขั้นแรก ให้ส่งออกข้อมูลโปรแกรมจำลองที่คุณมีอยู่แล้ว ขณะที่โปรแกรมจำลองยังคงทำงานอยู่ ให้เปิดหน้าต่างเทอร์มินัลใหม่ แล้วป้อนคำสั่งต่อไปนี้

firebase emulators:export ./emulators_data

.emulators_data คืออาร์กิวเมนต์ซึ่งบอก Firebase ว่าจะส่งออกข้อมูลที่ใด หากไม่มีไดเรกทอรี ระบบจะสร้างไดเรกทอรีดังกล่าวขึ้น คุณสามารถใช้ชื่อใดก็ได้ที่ต้องการสำหรับไดเรกทอรีนั้น

เมื่อเรียกใช้คำสั่งนี้ คุณจะเห็นเอาต์พุตนี้ในเทอร์มินัลที่คุณเรียกใช้คำสั่ง

i  Found running emulator hub for project flutter-firebase-codelab-d6b79 at http://localhost:4400
i  Creating export directory /Users/ewindmill/Repos/codelabs/firebase-emulator-suite/complete/emulators_data
i  Exporting data to: /Users/ewindmill/Repos/codelabs/firebase-emulator-suite/complete/emulators_data
✔  Export complete

และถ้าคุณสลับไปที่หน้าต่างเทอร์มินัลที่โปรแกรมจำลองทำงานอยู่ คุณจะเห็นเอาต์พุตนี้

i  emulators: Received export request. Exporting data to /Users/ewindmill/Repos/codelabs/firebase-emulator-suite/complete/emulators_data.
✔  emulators: Export complete.

และสุดท้าย หากดูในไดเรกทอรีโครงการ คุณจะเห็นไดเรกทอรีชื่อ ./emulators_data ซึ่งมีไฟล์ JSON ไฟล์ รวมถึงไฟล์ข้อมูลเมตาอื่นๆ ที่มีข้อมูลที่คุณบันทึกไว้

นำเข้าข้อมูลโปรแกรมจำลอง

ตอนนี้คุณสามารถนำเข้าข้อมูลดังกล่าวซึ่งเป็นส่วนหนึ่งของเวิร์กโฟลว์การพัฒนาและเริ่มต้นต่อจากจุดที่ค้างไว้ได้แล้ว

ก่อนอื่น ให้หยุดโปรแกรมจำลองหากโปรแกรมดังกล่าวทำงานอยู่ โดยกด CTRL+C ในเครื่องเทอร์มินัล

จากนั้นเรียกใช้คำสั่ง emulators:start ที่คุณเคยเห็นแล้ว แต่มีการแฟล็กเพื่อบอกว่าต้องนำเข้าข้อมูลใด ดังนี้

firebase emulators:start --import ./emulators_data

เมื่อโปรแกรมจำลองทำงานแล้ว ให้ไปที่ UI โปรแกรมจำลองที่ localhost:4000 แล้วคุณจะเห็นข้อมูลเดียวกันกับที่เคยใช้ก่อนหน้านี้

ส่งออกข้อมูลโดยอัตโนมัติเมื่อปิดโปรแกรมจำลอง

นอกจากนี้คุณยังส่งออกข้อมูลโดยอัตโนมัติเมื่อปิดโปรแกรมจำลองได้ แทนที่จะจำว่าต้องส่งออกข้อมูลเมื่อสิ้นสุดเซสชันการพัฒนาทุกเซสชัน

เมื่อคุณเริ่มโปรแกรมจำลอง ให้เรียกใช้คำสั่ง emulators:start พร้อมด้วยแฟล็กเพิ่มเติม 2 รายการ

firebase emulators:start --import ./emulators_data --export-on-exit

เสร็จแล้ว! ตอนนี้ข้อมูลของคุณจะได้รับการบันทึกและโหลดซ้ำทุกครั้งที่คุณทำงานกับโปรแกรมจำลองสำหรับโปรเจ็กต์นี้ คุณยังระบุไดเรกทอรีอื่นเป็นอาร์กิวเมนต์ของ –export-on-exit flag ได้ แต่ค่าเริ่มต้นจะเป็นไดเรกทอรีที่ส่งไปยัง –import

คุณสามารถใช้ตัวเลือกเหล่านี้ผสมผสานกันอย่างไรก็ได้ นี่คือหมายเหตุจากเอกสาร: สามารถระบุไดเรกทอรีการส่งออกด้วยแฟล็กนี้: firebase emulators:start --export-on-exit=./saved-data หากใช้ --import เส้นทางการส่งออกจะมีค่าเริ่มต้นเหมือนกัน ตัวอย่างเช่น firebase emulators:start --import=./data-path --export-on-exit สุดท้าย ส่งผ่านเส้นทางไดเรกทอรีต่างๆ ไปยังแฟล็ก --import และ --export-on-exit หากต้องการ

8. ยินดีด้วย

คุณเริ่มต้นใช้งานโปรแกรมจำลอง Firebase และ Flutter เรียบร้อยแล้ว คุณดูโค้ดที่สมบูรณ์ของ Codelab ได้ใน "เสร็จสมบูรณ์" ไดเรกทอรีใน GitHub: Flutter Codelabs

สรุปประเด็นที่ได้พูดถึง

  • การตั้งค่าแอป Flutter เพื่อใช้ Firebase
  • การสร้างโปรเจ็กต์ Firebase
  • CLI สำหรับ FlutterFire
  • Firebase CLI
  • โปรแกรมจำลองการตรวจสอบสิทธิ์ Firebase
  • โปรแกรมจำลอง Firebase Firestore
  • การนำเข้าและส่งออกข้อมูลโปรแกรมจำลอง

ขั้นตอนถัดไป

ดูข้อมูลเพิ่มเติม

Sparky ภูมิใจในตัวคุณ

2a0ad195769368b1.gif