การพัฒนาท้องถิ่นสำหรับแอป Flutter ของคุณโดยใช้ Firebase Emulator Suite

1. ก่อนที่คุณจะเริ่ม

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

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

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

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

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

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

cd5c4753bbee8af.png8cb4d21f656540bf.png

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

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

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

สิ่งที่คุณต้องการ

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

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

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

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

  1. ลงชื่อเข้าใช้คอนโซล Firebase
  2. ในคอนโซล Firebase คลิก เพิ่มโครงการ (หรือ สร้างโครงการ ) และป้อนชื่อสำหรับโครงการ Firebase ของคุณ (เช่น " Firebase-Flutter-Codelab")

fe6aeab3b91965ed.png

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

d1fcec48bf251eaa.png

หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับโครงการ Firebase โปรดดู ทำความเข้าใจโครงการ Firebase

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

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

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

เปิดใช้งาน Cloud Firestore

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

เปิดใช้งาน Cloud Firestore:

  1. ในส่วน Build ของคอนโซล 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 ซึ่งมีโค้ดสำหรับคอลเล็กชันของ codelabs รหัสสำหรับ codelab นี้อยู่ใน flutter-codelabs/firebase-emulator-suite

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

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

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

ติดตั้ง Firebase CLI

Firebase CLI มีเครื่องมือสำหรับจัดการโปรเจ็กต์ Firebase ของคุณ CLI จำเป็นต้องใช้ Emulator Suite ดังนั้นคุณจะต้องติดตั้ง

มีหลายวิธีในการติดตั้ง 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 ทั้งสองแล้ว คุณสามารถตั้งค่าผลิตภัณฑ์ Firebase แต่ละรายการ (เช่น Firestore) ดาวน์โหลดโปรแกรมจำลอง และเพิ่ม Firebase ลงในแอป Flutter ของคุณด้วยคำสั่งเทอร์มินัลเพียงไม่กี่คำสั่ง

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

firebase init

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

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

f11dcab439e6ac1e.png

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

3bdc0c6934991c25.png

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

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

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

8544e41037637b07.png

กำหนดค่า FlutterFire

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

flutterfire configure

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

ภาพหน้าจอต่อไปนี้แสดงพร้อมท์ที่คุณจะต้องตอบ

619b7aca6dc15472.png301c9534f594f472.png

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

12199a85ade30459.png

เพิ่มแพ็คเกจ Firebase ให้กับแอพ Flutter

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

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.โผ

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());
}

โค้ดสองสามบรรทัดแรกจะเริ่มต้น 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 Auth

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

3c1bfded40733189.png

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

ถัดไป คุณจะได้อธิบายขั้นตอนการเพิ่มผู้ใช้ลงในโปรแกรมจำลอง Firebase Auth จากนั้นจึงบันทึกผู้ใช้รายนั้นผ่าน Flutter UI

เพิ่มผู้ใช้

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

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

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

logged_out_view.dart

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

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

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

ขั้นที่สอง กรอกวิธี 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 เพื่อแสดงชื่อจริงของผู้ใช้

logged_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 บนหน้าแรกของ Emulator 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 ดังนั้น Firestore จึงสร้างขึ้นมา

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

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

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

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

a978fb34fb8a83da.png

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

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

ที่นี่ คุณควรเห็นรายการที่มีลักษณะดังนี้:

f0b37f0341639035.png

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

385d62152e99aad4.png

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

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

อ่านจาก Firestore

Firestore ใช้การซิงโครไนซ์ข้อมูลเพื่อส่งข้อมูลที่อัปเดตไปยังอุปกรณ์ที่เชื่อมต่อ ในโค้ด Flutter คุณสามารถฟัง (หรือสมัคร) คอลเลกชันและเอกสารของ Firestore และโค้ดของคุณจะได้รับแจ้งทุกครั้งที่ข้อมูลมีการเปลี่ยนแปลง ในแอพนี้ การฟังการอัปเดต 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 พร้อมกับแฟล็กเพิ่มเติมอีกสองรายการ

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 emulator และ Flutter เรียบร้อยแล้ว คุณสามารถค้นหาโค้ดที่เสร็จสมบูรณ์แล้วสำหรับ Codelab นี้ได้ในไดเร็กทอรี "สมบูรณ์" บน github: Flutter Codelabs

สิ่งที่เราได้กล่าวถึง

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

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

เรียนรู้เพิ่มเติม

สปาร์กี้ภูมิใจในตัวคุณ!

2a0ad195769368b1.gif