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

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

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

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

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

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

מה תיצרו

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

cd5c4753bbee8af.png 8cb4d21f656540bf.png

מה תלמדו

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

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

מה צריך להכין

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

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

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

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

  1. נכנסים למסוף Firebase באמצעות חשבון Google.
  2. לוחצים על הלחצן כדי ליצור פרויקט חדש, ואז מזינים שם לפרויקט (לדוגמה, Firebase-Flutter-Codelab).
  3. לוחצים על המשך.
  4. אם מוצגת בקשה לעשות זאת, קוראים ומאשרים את התנאים של Firebase, ואז לוחצים על המשך.
  5. (אופציונלי) מפעילים את העזרה מבוססת-AI במסוף Firebase (שנקראת Gemini ב-Firebase).
  6. ב-codelab הזה לא צריך להשתמש ב-Google Analytics, ולכן משביתים את האפשרות Google Analytics.
  7. לוחצים על יצירת פרויקט, מחכים שהפרויקט יוקצה ולוחצים על המשך.

מידע נוסף על פרויקטים ב-Firebase זמין במאמר הסבר על פרויקטים ב-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. לוחצים על Enable.

3. הגדרת אפליקציית Flutter

לפני שמתחילים, צריך להוריד את קוד ההתחלה ולהתקין את Firebase CLI.

קבלת קוד לתחילת הדרך

משכפלים את המאגר ב-GitHub משורת הפקודה:

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

לחלופין, אם מותקן אצלכם הכלי GitHub's cli:

gh repo clone flutter/codelabs flutter-codelabs

צריך לשכפל את הקוד לדוגמה לספרייה flutter-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. כדי להשתמש ב-Emulator Suite, צריך להתקין את ה-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, אפשר להגדיר מוצרים ספציפיים של 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 שרוצים להשתמש בו ואת הפלטפורמות שרוצים להגדיר. ב-codelab הזה, הדוגמאות משתמשות ב-Flutter Web, אבל אתם יכולים להגדיר את פרויקט Firebase כך שישתמש בכל האפשרויות.

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

619b7aca6dc15472.png 301c9534f594f472.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.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

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

3c1bfded40733189.png

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

בשלב הבא, נסביר איך להוסיף משתמש לאמולטור של Firebase Auth, ואז להתחבר באמצעות ממשק המשתמש של Flutter.

הוספת משתמש

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

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

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

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 הוא 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 Auth. הפונקציה מבצעת קריאה ל-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 הוגדר באמולטור כשציינתם את המשתמש הראשון. עכשיו, כשמתחברים לאפליקציה, מוצגת ההודעה "ברוך שובך, דאש!" במקום 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 (תאריך) יקבל כל מחרוזת, כי הוא פשוט יותר ב-codelab הזה. אין לה אימות חזק או שהיא לא מתייחסת לאובייקטים של DateTime בשום צורה).

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

הכרטיסייה Requests (בקשות) באמולטור Firestore

בממשק המשתמש, עוברים אל אמולטור Firestore ומסתכלים בכרטיסייה 'נתונים'. עכשיו אמור להופיע אוסף בשם '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
  • ייבוא וייצוא של נתונים מהאמולטור

השלבים הבאים

מידע נוסף

ספארקי גאה בך!

2a0ad195769368b1.gif