פיתוח מקומי של אפליקציות Flutter באמצעות חבילת האמולטור של Firebase

1. לפני שמתחילים

בקודלאב הזה תלמדו איך להשתמש ב-Firebase Emulator Suite עם Flutter במהלך פיתוח מקומי. תלמדו איך להשתמש באימות באמצעות אימייל וסיסמה באמצעות Emulator Suite, ואיך לקרוא ולכתוב נתונים במהדמ של Firestore. לבסוף, תלמדו לייבא ולייצא נתונים מהמכונות הווירטואליות, כדי לעבוד עם אותם נתונים מזויפים בכל פעם שתחזרו לפיתוח.

דרישות מוקדמות

אנחנו יוצאים מנקודת הנחה שיש לכם ניסיון מסוים ב-Flutter. אם לא, מומלץ קודם ללמוד את היסודות. הקישורים הבאים יכולים לעזור:

בנוסף, אתם צריכים להתנסות ב-Firebase, אבל זה בסדר אם אף פעם לא הוספתם את Firebase לפרויקט Flutter. אם אתם לא מכירים את מסוף Firebase או שאתם משתמשים חדשים ב-Firebase, מומלץ לעיין קודם בקישורים הבאים:

מה יוצרים?

ה-Codelab הזה ינחה אתכם בתהליך היצירה של אפליקציית Journaling פשוטה. באפליקציה יופיע מסך התחברות ומסך שמאפשר לקרוא רשומות קודמות ביומן וליצור רשומות חדשות.

cd5c4753bbee8af.png 8cb4d21f656540bf.png

מה תלמדו

נלמד איך להתחיל להשתמש ב-Firebase ואיך לשלב את חבילת Firebase Emulator בתהליך הפיתוח ב-Flutter ולהשתמש בה. הנושאים הבאים ב-Firebase ייכללו:

שימו לב שהנושאים האלה מופיעים במאמר רק במידה שהם נדרשים כדי להסביר את חבילת הסימולטורים של Firebase. בקודלאב הזה נסביר איך מוסיפים פרויקט Firebase לאפליקציית Flutter, ואיך מפתחים באמצעות Firebase Emulator Suite. לא יתקיימו דיונים מעמיקים על אימות ב-Firebase או על Firestore. אם אתם לא מכירים את הנושאים האלה, מומלץ להתחיל עם היכרות עם Firebase for Flutter Codelab.

מה צריך

  • ידע בעבודה עם Flutter וערכת ה-SDK שמותקנת
  • עורכי טקסט של Intellij JetBrains או VS Code
  • דפדפן Google Chrome (או יעד הפיתוח המועדף אחר שלכם ב-Flutter). חלק מפקודות המסוף בקודלאב הזה יקבלו את ההנחה שאתם מריצים את האפליקציה ב-Chrome).

2. יצירת פרויקט Firebase והגדרתו

המשימה הראשונה שתצטרכו להשלים היא ליצור פרויקט Firebase במסוף האינטרנט של Firebase. רוב הקודלאב הזה יתמקד ב-Emulator Suite, שמשתמש בממשק משתמש שפועל באופן מקומי, אבל קודם צריך להגדיר פרויקט Firebase מלא.

יצירת פרויקט Firebase

  1. נכנסים למסוף Firebase.
  2. במסוף Firebase, לוחצים על Add Project (הוספת פרויקט) (או על Create a project (יצירת פרויקט)) ומזינים שם לפרויקט ב-Firebase (לדוגמה, Firebase-Flutter-Codelab).

fe6aeab3b91965ed

  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. לוחצים על Create dataset. 99e8429832d23fa3.png
  3. בוחרים באפשרות התחלה במצב בדיקה. קוראים את כתב הוויתור לגבי כללי האבטחה. מצב הבדיקה מבטיח שתהיה לך אפשרות לכתוב בחופשיות למסד הנתונים במהלך הפיתוח. לוחצים על הבא. 6be00e26c72ea032.png
  4. בוחרים את המיקום של מסד הנתונים (אפשר פשוט להשתמש במיקום ברירת המחדל). לתשומת ליבך: לא ניתן לשנות את המיקום הזה מאוחר יותר. 278656eefcfb0216.png
  5. לוחצים על Enable.

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 נדרש כדי להשתמש בחבילת האמולטור, לכן צריך להתקין אותה.

יש כמה דרכים להתקין את ה-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.

התקנת ה-CLI של FlutterFire

ה-CLI של FlutterFire מבוסס על ה-CLI של Firebase, והוא מאפשר לשלב בקלות פרויקט 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 לכל שאלה לגבי יציאות ספציפיות לשימוש בכל אמולטור. כשמתבקשים להחליט אם להשתמש בממשק המשתמש של המהדר, צריך לבחור באפשרות ברירת המחדל 'כן'.

בסוף התהליך, הפלט אמור להיראות כמו בצילום המסך הבא.

חשוב: יכול להיות שהפלט שלכם יהיה שונה במקצת מהפלט שלי, כפי שמוצג בצילום המסך שבהמשך, כי השאלה האחרונה תהיה 'לא' כברירת מחדל אם כבר הורדת את הסימולטורים.

8544e41037637b07.png

הגדרת FlutterFire

לאחר מכן, תוכלו להשתמש ב-FlutterFire כדי ליצור את קוד ה-Dart הנדרש לשימוש ב-Firebase באפליקציית Flutter.

flutterfire configure

כשמריצים את הפקודה הזו, מופיעה בקשה לבחור את פרויקט Firebase שבו רוצים להשתמש ואת הפלטפורמות שרוצים להגדיר. בקודלאב הזה, הדוגמאות מבוססות על Flutter Web, אבל אפשר להגדיר את פרויקט Firebase כך שישתמש בכל האפשרויות.

בצילומי המסך הבאים מוצגות ההנחיות שעליך לענות עליהן.

619b7aca6dc15472.png 301c9534f594f472.png

בצילום המסך הזה מוצג הפלט בסוף התהליך. אם אתם מכירים את Firebase, תבחינו שלא הייתם צריכים ליצור אפליקציות במסוף, ו-CLI של FlutterFire עשה זאת בשבילכם.

12199a85ade30459.png

הוספת חבילות Firebase לאפליקציית Flutter

שלב ההגדרה האחרון הוא הוספת חבילות Firebase הרלוונטיות לפרויקט Flutter. בטרמינל, מוודאים שנמצאים ברמה הבסיסית (root) של פרויקט 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.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());
}

שורות הקוד הראשונות מפעילה את Firebase. לרוב, אם משתמשים ב-Firebase באפליקציית Flutter, מומלץ להתחיל בהתקשרות אל WidgetsFlutterBinding.ensureInitialized ואל Firebase.initializeApp.

לאחר מכן, הקוד שמתחיל בשורה if (kDebugMode) מורה לאפליקציה לטרגט את המהדמנים במקום פרויקט Firebase בסביבת הייצור. kDebugMode מוודא שהטירגוט למהדמנים יתבצע רק אם אתם נמצאים בסביבת פיתוח. מכיוון ש-kDebugMode הוא ערך קבוע, למהדר של Dart ברור שהוא צריך להסיר את בלוק הקוד הזה לגמרי במצב הפצה.

הפעלת האמולטורים

צריך להפעיל את הסימולטורים לפני שמפעילים את אפליקציית Flutter. קודם כול, מפעילים את הסימולטורים באמצעות הפקודה הבאה במסוף:

firebase emulators:start

הפקודה הזו מפעילה את המהדמנים ומציגה את יציאות ה-localhost שבאמצעותן אפשר לקיים איתם אינטראקציה. כשמריצים את הפקודה הזו, הפלט אמור להיראות כך:

bb7181eb70829606.png

הפלט הזה מציין אילו אמולטורים פועלים ולאן אפשר לראות את האמולטורים. קודם כול, בודקים את ממשק המשתמש של האמולטור בכתובת localhost:4000.

11563f4c7216de81.png

זהו דף הבית של ממשק המשתמש של המהדר המקומי. ברשימה מופיעים כל האמולטורים הזמינים, וכל אחד מהם מסומן בסטטוס מופעל או מושבת.

5. אמולטור Firebase Auth

הסימולטור הראשון שבו תשתמשו הוא הסימולטור של Authentication. כדי להתחיל עם אמולטור האימות, לוחצים על 'מעבר לאמולטור' בכרטיס האימות בממשק המשתמש, ותופיע דף שנראה כך:

3c1bfded40733189.png

הדף הזה דומה לדף של מסוף האינטרנט של Auth. הוא כולל טבלה עם רשימת המשתמשים, כמו במסוף אונליין, ומאפשר להוסיף משתמשים באופן ידני. הבדל גדול אחד הוא ששיטת האימות היחידה שזמינה במהדמנים היא באמצעות אימייל וסיסמה. זה מספיק לפיתוח מקומי.

בשלב הבא נסביר איך מוסיפים משתמש למהדמיר של אימות Firebase, ואז נכנסים עם המשתמש הזה לחשבון דרך ממשק המשתמש של Flutter.

הוספת משתמש

לוחצים על הלחצן 'הוספת משתמש' וממלאים את הטופס עם הפרטים הבאים:

  • שם לתצוגה: Dash
  • אימייל: dash@email.com
  • סיסמה: dashword

שולחים את הטופס, ועכשיו בטבלה מופיע משתמש. עכשיו אפשר לעדכן את הקוד כדי להתחבר באמצעות המשתמש הזה.

logged_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 הוא null. הקוד בקובץ 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. הוא מבצע קריאה ל-FirebaseAuth כדי להיכנס, והיא מחזירה אובייקט Future<UserCredential>. בסיום התהליך, הקוד הזה בודק אם מצורף User לUserCredential. אם יש משתמש באובייקט פרטי הכניסה, אז משתמש התחבר בהצלחה וניתן להגדיר את המאפיין AppState.user. אם לא, סימן שהיתה שגיאה והיא תודפס.

שימו לב ששורת הקוד היחידה בשיטה הזו שספציפית לאפליקציה הזו (ולא קוד כללי של FirebaseAuth) היא הקריאה לשיטה _listenForEntries, שתפורט בשלב הבא.

TODO: Action Icon – Reload your app, and then press the Login button when it renders. הפעולה הזו גורמת לאפליקציה לנווט לדף שבו מופיע הכיתוב 'ברוך הבא, שם המשתמש!' בחלק העליון. האימות חייב לפעול כי הוא מאפשר לך לנווט לדף הזה, אבל צריך לבצע עדכון קל ב-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. בדף הבית של ממשק המשתמש של האמולטור (localhost:4000), לוחצים על 'כניסה לאמולטור' בכרטיס של Firestore. הוא אמור להיראות כך:

אמולטור:

791fce7dc137910a.png

במסוף Firebase:

e0dde9aea34af050.png

אם יש לכם ניסיון ב-Firestore, תבחינו שהדף הזה דומה לדף Firestore במסוף Firebase. עם זאת, יש כמה הבדלים משמעותיים.

  1. אפשר למחוק את כל הנתונים בהקשה אחת על לחצן. זה מסוכן עם נתוני ייצור, אבל שימושי ליצירת גרסאות אב מהירות. אם אתם עובדים על פרויקט חדש ומודל הנתונים שלכם משתנה, קל לנקות את הנתונים.
  2. יש כרטיסייה 'בקשות'. בכרטיסייה הזו אפשר לצפות בבקשות נכנסות שנשלחות למהדר הזה. אדבר על הכרטיסייה הזו בהרחבה.
  3. אין כרטיסיות של 'כללים', 'אינדקסים' או 'שימוש'. יש כלי (שנדון בקטע הבא) שעוזר לכתוב כללי אבטחה, אבל אי אפשר להגדיר כללי אבטחה למהדר המקומי.

לסיכום, הגרסה הזו של Firestore מספקת יותר כלים שימושיים במהלך הפיתוח, ומסירה כלים שנדרשים בסביבת הייצור.

כתיבת נתונים ב-Firestore

לפני שנדבר על הכרטיסייה 'בקשות' במהדורת הדמיה, נתחיל ביצירת בקשה. לשם כך נדרשים עדכוני קוד. מתחילים על ידי חיבור הטופס באפליקציה כדי לכתוב יומן חדש Entry ב-Firestore.

התהליך הכללי לשליחת Entry הוא:

  1. המשתמש ממלא את הטופס ולחץ על הלחצן Submit
  2. ממשק המשתמש קורא ל-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>.

במקרה הזה, האוסף Entries לא היה קיים ב-Firestore, ולכן Firestore יצר אותו.

אחרי שמוסיפים את הקוד הזה, טוענים מחדש או מפעילים מחדש את האפליקציה, נכנסים לחשבון ומנווטים לתצוגה EntryForm. אפשר למלא את הטופס עם כל Strings שרוצים. (השדה Date יקבל כל מחרוזת, כי הוא פשוט יותר בקודלאב הזה. אין לו אימות חזק או עניין באובייקטים מסוג DateTime בשום צורה).

לוחצים על 'שליחה' בטופס. לא יקרה כלום באפליקציה, אבל תוכלו לראות את הרשומה החדשה בממשק המשתמש של המהדר.

הכרטיסייה 'בקשות' באמולטור של Firestore

בממשק המשתמש, עוברים לאמולטור Firestore ובודקים את הכרטיסייה Data (נתונים). עכשיו אמור להופיע אוסף ברמה הבסיסית של מסד הנתונים שנקרא 'Entries'. צריך להיות שם מסמך שמכיל את אותו המידע שהזנת בטופס.

a978fb34fb8a83da.png

זה מאשר שהפקודה AppState.writeEntryToFirestore פעלה, ועכשיו אפשר לבדוק את הבקשה לעומק בכרטיסייה 'בקשות'. עכשיו לוחצים על הכרטיסייה הזו.

בקשות של אמולטור Firestore

כאן אמורה להופיע רשימה שנראית דומה לזו:

f0b37f0341639035.png

אפשר ללחוץ על כל אחד מהפריטים ברשימה הזו כדי לראות מידע שימושי רב. לוחצים על פריט הרשימה CREATE שתואם לבקשה כדי ליצור רשומה חדשה ביומן. תוצג טבלה חדשה שנראית כך:

385d62152e99aad4.png

כפי שצוין, במהדורת האימולטור של Firestore יש כלים לפיתוח כללי האבטחה של האפליקציה. בתצוגה הזו מוצג בדיוק איזה שורה בכללי האבטחה הבקשה עברה (או נכשלה, אם זה המצב). באפליקציה חזקה יותר, אפשר להגדיר יותר כללי אבטחה עם כמה בדיקות הרשאה. התצוגה הזו משמשת לכתיבה ולניפוי באגים של כללי ההרשאה האלה.

הוא גם מספק דרך קלה לבדוק כל חלק בבקשה הזו, כולל המטא-נתונים ונתוני האימות. הנתונים האלה משמשים לכתיבת כללי הרשאה מורכבים.

קריאה מ-Firestore

מערכת Firestore משתמשת בסנכרון נתונים כדי לדחוף נתונים מעודכנים למכשירים מחוברים. בקוד Flutter, אפשר להאזין (או להירשם) לאוספים ולמסמכים ב-Firestore, והקוד יקבל התראה בכל פעם שהנתונים ישתנו. באפליקציה הזו, ההאזנה לעדכוני Firestore מתבצעת בשיטה AppState._listenForEntries.

הקוד הזה פועל בשילוב עם StreamController ו-Stream שנקראים AppState._entriesStreamController ו-AppState.entries, בהתאמה. הקוד הזה כבר נכתב, וכך גם כל הקוד הנדרש בממשק המשתמש כדי להציג את הנתונים מ-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);
   });
 }
 // ...
}

הקוד הזה מקשיב לאוסף Entries ב-Firestore. כש-Firestore מודיע ללקוח הזה שיש נתונים חדשים, הוא מעביר את הנתונים האלה והקוד ב-_listenForEntries משנה את כל מסמכי הצאצאים שלו לאובייקט שהאפליקציה שלנו יכולה להשתמש בו (Entry). לאחר מכן, הוא מוסיף את הרשומות האלה ל-StreamController שנקרא _entriesStreamController (שממשק המשתמש מקשיב לו). הקוד הזה הוא העדכון היחיד הנדרש.

לסיום, חשוב לזכור שהשיטה 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

כשהמכונות הווירטואליות מופעלות, עוברים לממשק המשתמש של המכונה הווירטואלית בכתובת localhost:4000, ואמורים להופיע אותם נתונים שבהם עבדתם בעבר.

ייצוא נתונים באופן אוטומטי כשסוגרים אמוללטורים

אפשר גם לייצא נתונים באופן אוטומטי כשאתם יוצאים מהמכונות הווירטואליות, במקום לזכור לייצא את הנתונים בסוף כל סשן פיתוח.

כשמפעילים את המהדמנים, מריצים את הפקודה emulators:start עם שני דגלים נוספים.

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

Voila! מעכשיו, הנתונים יישמרו ויטענו מחדש בכל פעם שתעבדו עם הסימולטורים של הפרויקט הזה. אפשר גם לציין ספרייה אחרת כארגומנט לפונקציה –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 הזה נמצא בספרייה complete ב-GitHub: Flutter Codelabs

מה עסקנו בו

  • הגדרת אפליקציית Flutter לשימוש ב-Firebase
  • הגדרת פרויקט Firebase
  • FlutterFire CLI
  • Firebase CLI
  • אמולטור של אימות ב-Firebase
  • אמולטור של Firebase Firestore
  • ייבוא וייצוא של נתוני אמולטור

השלבים הבאים

מידע נוסף

Sparky גאה בך!

2a0ad195769368b1.gif