Lokale Entwicklung für Ihre Flutter-Apps mit der Firebase Emulator Suite

1. Hinweis

In diesem Codelab erfahren Sie, wie Sie die Firebase Emulator Suite mit Flutter bei der lokalen Entwicklung verwenden. Sie erfahren, wie Sie die E-Mail-Passwort-Authentifizierung über die Emulator Suite verwenden und wie Sie Daten im Firestore-Emulator lesen und schreiben. Zum Schluss importieren und exportieren Sie Daten aus den Emulatoren, damit Sie bei der Entwicklung immer mit denselben fiktiven Daten arbeiten.

Vorbereitung

In diesem Codelab wird davon ausgegangen, dass Sie bereits Erfahrung mit Flutter haben. Falls nicht, sollten Sie sich zunächst mit den Grundlagen vertraut machen. Die folgenden Links sind hilfreich:

Außerdem sollten Sie bereits einige Firebase-Kenntnisse haben, das ist aber in Ordnung, wenn Sie Firebase noch nie einem Flutter-Projekt hinzugefügt haben. Wenn Sie mit der Firebase Console nicht vertraut sind oder Firebase noch nicht kennen, sehen Sie sich zuerst die folgenden Links an:

Was Sie erstellen

In diesem Codelab erfahren Sie, wie Sie eine einfache Tagebuch-App erstellen. Die Anwendung hat einen Anmeldebildschirm und einen Bildschirm, auf dem Sie frühere Tagebucheinträge lesen und neue erstellen können.

cd5c4753bbee8af.png 8cb4d21f656540bf.png

Lerninhalte

Sie erfahren, wie Sie Firebase verwenden und die Firebase Emulator Suite in Ihren Flutter-Entwicklungsworkflow einbinden und verwenden. Folgende Firebase-Themen werden behandelt:

Diese Themen werden nur insoweit behandelt, wie sie für die Firebase-Emulator-Suite erforderlich sind. In diesem Codelab erfahren Sie, wie Sie Ihrer Flutter-App ein Firebase-Projekt hinzufügen und mit der Firebase Emulator Suite entwickeln. Es wird keine ausführlichen Diskussionen zu Firebase Authentication oder Firestore geben. Wenn Sie mit diesen Themen nicht vertraut sind, empfehlen wir Ihnen, mit dem Codelab „Einführung in Firebase für Flutter“ zu beginnen.

Voraussetzungen

  • Grundkenntnisse in Flutter und das installierte SDK
  • IntelliJ JetBrains oder VS Code-Texteditoren
  • Google Chrome-Browser (oder Ihr bevorzugtes Entwicklungsziel für Flutter) Bei einigen Terminalbefehlen in diesem Codelab wird davon ausgegangen, dass Sie Ihre App in Chrome ausführen.

2. Firebase-Projekt erstellen und einrichten

Als Erstes müssen Sie ein Firebase-Projekt in der Firebase-Webkonsole erstellen. Der Großteil dieses Codelabs konzentriert sich auf die Emulator Suite, die eine lokal ausgeführte Benutzeroberfläche verwendet. Sie müssen jedoch zuerst ein vollständiges Firebase-Projekt einrichten.

Firebase-Projekt erstellen

  1. Melden Sie sich in der Firebase Console an.
  2. Klicken Sie in der Firebase Console auf Projekt hinzufügen (oder Projekt erstellen) und geben Sie einen Namen für Ihr Firebase-Projekt ein, z. B. Firebase-Flutter-Codelab.

fe6aeab3b91965ed.png

  1. Klicken Sie sich durch die Optionen für die Projekterstellung. Akzeptieren Sie die Firebase-Nutzungsbedingungen, wenn Sie dazu aufgefordert werden. Überspringen Sie die Einrichtung von Google Analytics, da Sie Analytics für diese App nicht verwenden werden.

d1fcec48bf251eaa.png

Weitere Informationen zu Firebase-Projekten finden Sie unter Firebase-Projekte verstehen.

Die App, die Sie erstellen, verwendet zwei Firebase-Produkte, die für Flutter-Apps verfügbar sind:

  • Firebase Authentication, damit sich Ihre Nutzer in Ihrer App anmelden können.
  • Cloud Firestore speichert strukturierte Daten in der Cloud und benachrichtigt Sie sofort, wenn sich Daten ändern.

Diese beiden Produkte erfordern eine spezielle Konfiguration oder müssen über die Firebase Console aktiviert werden.

Cloud Firestore aktivieren

Die Flutter-App verwendet Cloud Firestore, um Tagebucheinträge zu speichern.

So aktivieren Sie Cloud Firestore:

  1. Klicken Sie in der Firebase Console im Abschnitt Build auf Cloud Firestore.
  2. Klicken Sie auf Datenbank erstellen. 99e8429832d23fa3.png
  3. Wählen Sie die Option Im Testmodus starten aus. Lesen Sie den Haftungsausschluss zu den Sicherheitsregeln. Der Testmodus stellt sicher, dass Sie während der Entwicklung ungehindert in die Datenbank schreiben können. Klicken Sie auf Weiter. 6be00e26c72ea032.png
  4. Wählen Sie den Speicherort für Ihre Datenbank aus (Sie können einfach den Standardwert verwenden). Dieser Speicherort kann später nicht mehr geändert werden. 278656eefcfb0216.png
  5. Klicken Sie auf Aktivieren.

3. Flutter-App einrichten

Sie müssen den Startercode herunterladen und die Firebase CLI installieren, bevor wir beginnen.

Startcode abrufen

Klonen Sie das GitHub-Repository über die Befehlszeile:

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

Alternativ können Sie auch die GitHub-Befehlszeile verwenden:

gh repo clone flutter/codelabs flutter-codelabs

Der Beispielcode sollte in das Verzeichnis flutter-codelabs geklont werden, das den Code für eine Sammlung von Codelabs enthält. Der Code für dieses Codelab befindet sich in flutter-codelabs/firebase-emulator-suite.

Die Verzeichnisstruktur unter flutter-codelabs/firebase-emulator-suite besteht aus zwei Flutter-Projekten. Eine heißt complete, auf die Sie verweisen können, wenn Sie überspringen möchten oder einen Querverweis auf Ihren eigenen Code vornehmen möchten. Das andere Projekt heißt start.

Der Code, mit dem Sie beginnen möchten, befindet sich im Verzeichnis flutter-codelabs/firebase-emulator-suite/start. Öffnen oder importieren Sie dieses Verzeichnis in Ihre bevorzugte IDE.

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

Firebase CLI installieren

Die Firebase CLI bietet Tools zum Verwalten Ihrer Firebase-Projekte. Sie müssen die Befehlszeile installieren, um die Emulator Suite zu verwenden.

Es gibt verschiedene Möglichkeiten, die Befehlszeile zu installieren. Wenn Sie macOS oder Linux verwenden, ist die einfachste Methode, diesen Befehl über das Terminal auszuführen:

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

Nach der Installation der Befehlszeile müssen Sie sich bei Firebase authentifizieren.

  1. Melden Sie sich mit Ihrem Google-Konto in Firebase an. Führen Sie dazu den folgenden Befehl aus:
firebase login
  1. Dieser Befehl verbindet Ihren lokalen Computer mit Firebase und gewährt Ihnen Zugriff auf Ihre Firebase-Projekte.
  1. Prüfen Sie, ob die Befehlszeile richtig installiert ist und Zugriff auf Ihr Konto hat, indem Sie Ihre Firebase-Projekte auflisten. Führen Sie dazu diesen Befehl aus:
firebase projects:list
  1. Die angezeigte Liste sollte mit den Firebase-Projekten in der Firebase Console übereinstimmen. Sie sollten mindestens „firebase-flutter-codelab“ sehen.

FlutterFire CLI installieren

Die FlutterFire CLI basiert auf der Firebase CLI und erleichtert die Einbindung eines Firebase-Projekts in Ihre Flutter-App.

Installieren Sie zuerst die Befehlszeile:

dart pub global activate flutterfire_cli

Prüfen Sie, ob die Befehlszeile installiert wurde. Führen Sie den folgenden Befehl im Flutter-Projektverzeichnis aus und prüfen Sie, ob die Befehlszeile das Hilfemenü ausgibt.

flutterfire --help

Firebase CLI und FlutterFire CLI verwenden, um der Flutter-App Ihr Firebase-Projekt hinzuzufügen

Wenn Sie die beiden Befehlszeilen installiert haben, können Sie einzelne Firebase-Produkte wie Firestore einrichten, die Emulatoren herunterladen und Firebase mit nur wenigen Terminalbefehlen zu Ihrer Flutter-App hinzufügen.

Führen Sie zuerst den folgenden Befehl aus, um die Firebase-Einrichtung abzuschließen:

firebase init

Mit diesem Befehl werden Sie durch eine Reihe von Fragen geführt, die für die Einrichtung Ihres Projekts erforderlich sind. Diese Screenshots zeigen den Ablauf:

  1. Wählen Sie „Firestore“ und „Emulatoren“ aus. Es gibt keine Authentifizierungsoption, da keine Konfiguration verwendet wird, die über Ihre Flutter-Projektdateien geändert werden kann.fe6401d769be8f53.png
  2. Wählen Sie als Nächstes „Vorhandenes Projekt verwenden“ aus, wenn Sie dazu aufgefordert werden.

f11dcab439e6ac1e.png

  1. Wählen Sie nun das Projekt aus, das Sie in einem vorherigen Schritt erstellt haben: „flutter-firebase-codelab“.

3bdc0c6934991c25.png

  1. Als Nächstes werden Ihnen einige Fragen zum Benennen der generierten Dateien gestellt. Drücken Sie bei jeder Frage die Eingabetaste, um den Standardwert auszuwählen. 9bfa2d507e199c59.png
  2. Zum Schluss müssen Sie die Emulatoren konfigurieren. Wählen Sie in der Liste „Firestore“ und „Authentifizierung“ aus und drücken Sie bei jeder Frage zu den Ports, die für jeden Emulator verwendet werden sollen, die Eingabetaste. Wenn Sie gefragt werden, ob Sie die Emulator-Benutzeroberfläche verwenden möchten, wählen Sie die Standardeinstellung Ja aus.

Am Ende des Vorgangs sollte die Ausgabe in etwa so aussehen wie im folgenden Screenshot.

Wichtig: Ihre Ausgabe kann sich leicht von meiner im Screenshot unten unterscheiden, da die letzte Frage standardmäßig mit „Nein“ beantwortet wird, wenn Sie die Emulatoren bereits heruntergeladen haben.

8544e41037637b07.png

FlutterFire konfigurieren

Als Nächstes können Sie mit FlutterFire den erforderlichen Dart-Code generieren, um Firebase in Ihrer Flutter-App zu verwenden.

flutterfire configure

Wenn Sie diesen Befehl ausführen, werden Sie aufgefordert, auszuwählen, welches Firebase-Projekt Sie verwenden und welche Plattformen Sie einrichten möchten. In diesem Codelab wird in den Beispielen Flutter Web verwendet. Sie können Ihr Firebase-Projekt jedoch so einrichten, dass alle Optionen verwendet werden.

Die folgenden Screenshots zeigen die Prompts, die Sie beantworten müssen.

619b7aca6dc15472.png 301c9534f594f472.png

Dieser Screenshot zeigt die Ausgabe am Ende des Vorgangs. Wenn Sie mit Firebase vertraut sind, werden Sie feststellen, dass Sie keine Anwendungen in der Console erstellen mussten, sondern dass die FlutterFire-Befehlszeile dies für Sie erledigt hat.

12199a85ade30459.png

Firebase-Pakete zur Flutter-App hinzufügen

Im letzten Einrichtungsschritt fügen Sie Ihrem Flutter-Projekt die relevanten Firebase-Pakete hinzu. Prüfen Sie im Terminal, ob Sie sich im Stammverzeichnis des Flutter-Projekts unter flutter-codelabs/firebase-emulator-suite/start befinden. Führen Sie dann die drei folgenden Befehle aus:

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

Dies sind die einzigen Pakete, die Sie in dieser Anwendung verwenden.

4. Firebase-Emulatoren aktivieren

Bisher sind die Flutter-App und Ihr Firebase-Projekt so eingerichtet, dass Sie die Emulatoren verwenden können. Sie müssen jedoch dem Flutter-Code noch mitteilen, dass ausgehende Firebase-Anfragen an die lokalen Ports weitergeleitet werden sollen.

Fügen Sie zuerst den Firebase-Initialisierungscode und den Emulator-Einrichtungscode der main-Funktion in main.dart. hinzu.

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

In den ersten Codezeilen wird Firebase initialisiert. Wenn Sie mit Firebase in einer Flutter-App arbeiten, sollten Sie zuerst WidgetsFlutterBinding.ensureInitialized und Firebase.initializeApp aufrufen.

Anschließend wird in der Zeile if (kDebugMode) festgelegt, dass Ihre App auf die Emulatoren und nicht auf ein Produktions-Firebase-Projekt ausgerichtet werden soll. kDebugMode sorgt dafür, dass ein Targeting auf die Emulatoren nur erfolgt, wenn Sie sich in einer Entwicklungsumgebung befinden. Da kDebugMode ein konstanter Wert ist, weiß der Dart-Compiler, dass dieser Codeblock im Release-Modus vollständig entfernt werden muss.

Emulatoren starten

Sie sollten die Emulatoren starten, bevor Sie die Flutter-App starten. Starten Sie zuerst die Emulatoren, indem Sie im Terminal Folgendes ausführen:

firebase emulators:start

Dieser Befehl startet die Emulatoren und macht localhost-Ports verfügbar, mit denen wir mit ihnen interagieren können. Wenn Sie diesen Befehl ausführen, sollte die Ausgabe in etwa so aussehen:

bb7181eb70829606.png

Diese Ausgabe gibt Aufschluss darüber, welche Emulatoren ausgeführt werden und wo Sie sie finden. Sehen Sie sich zuerst die Benutzeroberfläche des Emulators unter localhost:4000 an.

11563f4c7216de81.png

Dies ist die Startseite der Benutzeroberfläche des lokalen Emulators. Hier werden alle verfügbaren Emulatoren mit dem Status „An“ oder „Aus“ aufgeführt.

5. Firebase Auth-Emulator

Der erste Emulator, den Sie verwenden, ist der Authentication-Emulator. Klicken Sie auf der Karte „Authentifizierung“ in der Benutzeroberfläche auf „Zum Emulator“. Daraufhin wird eine Seite wie diese angezeigt:

3c1bfded40733189.png

Diese Seite ähnelt der Seite der Auth-Webkonsole. Sie enthält eine Tabelle, in der die Nutzer aufgelistet sind, z. B. in der Onlinekonsole, und Sie können Nutzer manuell hinzufügen. Ein großer Unterschied besteht darin, dass die einzige Authentifizierungsmethode, die auf den Emulatoren verfügbar ist, per E-Mail und Passwort erfolgt. Das ist für die lokale Entwicklung ausreichend.

Als Nächstes fügen Sie dem Firebase Auth-Emulator einen Nutzer hinzu und melden ihn dann über die Flutter-UI an.

Nutzer hinzufügen

Klicken Sie auf die Schaltfläche „Nutzer hinzufügen“ und füllen Sie das Formular mit den folgenden Informationen aus:

  • Anzeigename: Bindestrich
  • E-Mail: dash@email.com
  • Passwort: Dashboard

Senden Sie das Formular. Sie sehen, dass die Tabelle nun einen Nutzer enthält. Jetzt können Sie den Code aktualisieren, um sich mit diesem Nutzer anzumelden.

logged_out_view.dart

Der einzige Code im LoggedOutView-Widget, der aktualisiert werden muss, befindet sich im Callback, der ausgelöst wird, wenn ein Nutzer auf die Anmeldeschaltfläche klickt. Aktualisieren Sie den Code so:

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'),
          ),
        ),
      ],
    ),
   ),
  );
 }
}

Im aktualisierten Code werden die Strings TODO durch die E-Mail-Adresse und das Passwort ersetzt, die Sie im Authentifizierungsemulator erstellt haben. In der nächsten Zeile wurde die Zeile if(true) durch Code ersetzt, der prüft, ob state.user null ist. Der Code in AppClass verdeutlicht dies.

app_state.dart

Zwei Teile des Codes in AppState müssen aktualisiert werden. Weisen Sie dem Klassenmitglied „AppState.user“ zuerst den Typ User aus dem Paket firebase_auth zu, anstatt den Typ Object.

Füllen Sie als Nächstes die AppState.login-Methode wie unten gezeigt aus:

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!');
   }
 } 
 // ...
}

Die Typdefinition für den Nutzer lautet jetzt User?. Diese User-Klasse stammt aus Firebase Auth und stellt erforderliche Informationen wie User.displayName bereit, auf die später eingegangen wird.

Dies ist der grundlegende Code, der erforderlich ist, um einen Nutzer mit einer E-Mail-Adresse und einem Passwort in Firebase Auth anzumelden. Es wird eine FirebaseAuth-Aufruf zur Anmeldung ausgeführt, die ein Future<UserCredential>-Objekt zurückgibt. Wenn der Future abgeschlossen ist, prüft dieser Code, ob ein User an UserCredential angehängt ist. Wenn sich ein Nutzer im Anmeldedatenobjekt befindet, hat sich ein Nutzer erfolgreich angemeldet und die Property AppState.user kann festgelegt werden. Wenn nicht, ist ein Fehler aufgetreten und der Fehler wird ausgegeben.

Die einzige Codezeile in dieser Methode, die speziell für diese App und nicht für allgemeinen FirebaseAuth-Code ist, ist der Aufruf der Methode _listenForEntries. Diese wird im nächsten Schritt behandelt.

TODO: Aktionssymbol: Laden Sie die App neu und drücken Sie dann auf die Anmeldeschaltfläche, wenn sie angezeigt wird. Dadurch wird in der App eine Seite mit der Überschrift „Willkommen zurück, Person!“ geöffnet. Die Authentifizierung muss funktionieren, da Sie diese Seite aufrufen konnten. Es muss jedoch eine kleine Änderung an logged_in_view.dart vorgenommen werden, damit der tatsächliche Name des Nutzers angezeigt wird.

logged_in_view.dart

Ändern Sie die erste Zeile in der LoggedInView.build-Methode:

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(
 // ...

Diese Zeile ruft nun die displayName aus der User-Property des AppState-Objekts ab. Diese displayName wurde im Emulator festgelegt, als Sie Ihren ersten Nutzer definiert haben. Wenn Sie sich in Ihrer App anmelden, sollte jetzt „Willkommen zurück, Dash!“ und nicht mehr TODO angezeigt werden.

6. Daten im Firestore-Emulator lesen und schreiben

Sehen Sie sich zuerst den Firestore-Emulator an. Klicken Sie auf der Startseite der Emulator-Benutzeroberfläche (localhost:4000) auf der Karte „Firestore“ auf „Zum Emulator“. Das sollte so aussehen:

Emulator:

791fce7dc137910a.png

Firebase Console:

e0dde9aea34af050.png

Wenn Sie bereits Erfahrung mit Firestore haben, werden Sie feststellen, dass diese Seite der Firestore-Seite der Firebase Console ähnelt. Es gibt jedoch einige nennenswerte Unterschiede.

  1. Sie können alle Daten mit nur einem Tastendruck löschen. Das wäre bei Produktionsdaten gefährlich, aber hilfreich für eine schnelle Iteration. Wenn Sie an einem neuen Projekt arbeiten und sich Ihr Datenmodell ändert, ist es einfach, dieses zu bereinigen.
  2. Es gibt einen Tab „Anfragen“. Auf diesem Tab können Sie eingehende Anfragen an diesen Emulator beobachten. Auf diesen Tab werde ich gleich noch genauer eingehen.
  3. Es gibt keine Registerkarten für Regeln, Indexe oder Nutzung. Es gibt ein Tool (im nächsten Abschnitt beschrieben), mit dem Sie Sicherheitsregeln schreiben können. Sie können jedoch keine Sicherheitsregeln für den lokalen Emulator festlegen.

Zusammenfassend lässt sich sagen, dass diese Version von Firestore mehr Tools bietet, die während der Entwicklung nützlich sind, und Tools, die in der Produktion benötigt werden, entfernt.

In Firestore schreiben

Bevor wir uns mit dem Tab „Anfragen“ im Emulator befassen, senden Sie eine Anfrage. Dazu sind Codeupdates erforderlich. Verbinden Sie zuerst das Formular in der App, um ein neues Tagebuch Entry in Firestore zu schreiben.

So reichen Sie eine Entry ein:

  1. Nutzer füllt das Formular aus und drückt die Schaltfläche Submit
  2. Die Benutzeroberfläche ruft AppState.writeEntryToFirebase auf.
  3. AppState.writeEntryToFirebase fügt Firebase einen Eintrag hinzu

Der Code in Schritt 1 oder 2 muss nicht geändert werden. Der einzige Code, der für Schritt 3 hinzugefügt werden muss, wird in der Klasse AppState hinzugefügt. Nehmen Sie die folgende Änderung an AppState.writeEntryToFirebase vor.

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,
   });
 }
 // ...
}

Der Code in der Methode „writeEntryToFirebase“ ruft eine Referenz auf die Sammlung „Entries“ in Firestore ab. Es wird dann ein neuer Eintrag hinzugefügt, der vom Typ Map<String, String> sein muss.

In diesem Fall gab es die Sammlung „Einträge“ in Firestore nicht, daher wurde sie von Firestore erstellt.

Nachdem Sie diesen Code hinzugefügt haben, laden Sie Ihre App per Hot Reload neu oder starten Sie sie neu, melden Sie sich an und rufen Sie die Ansicht EntryForm auf. Sie können das Formular mit einer beliebigen Strings ausfüllen. Das Feld „Datum“ akzeptiert jeden String, da es für dieses Codelab vereinfacht wurde. Es gibt keine strenge Validierung und DateTime-Objekte werden nicht berücksichtigt.)

Klicken Sie im Formular auf „Senden“. In der App passiert nichts, aber Sie können den neuen Eintrag in der Emulator-Benutzeroberfläche sehen.

Tab „Anfragen“ im Firestore-Emulator

Rufen Sie in der Benutzeroberfläche den Firestore-Emulator auf und sehen Sie sich den Tab „Daten“ an. Sie sollten jetzt eine Sammlung namens „Einträge“ im Stammverzeichnis Ihrer Datenbank sehen. Dieses sollte ein Dokument enthalten, das dieselben Informationen enthält, die Sie in das Formular eingegeben haben.

a978fb34fb8a83da.png

Das bestätigt, dass der AppState.writeEntryToFirestore funktioniert hat. Sie können sich die Anfrage jetzt auf dem Tab „Anfragen“ genauer ansehen. Klicken Sie jetzt auf diesen Tab.

Firestore-Emulatoranfragen

Sie sollten eine Liste sehen, die in etwa so aussieht:

f0b37f0341639035.png

Sie können auf einen der Listeneinträge klicken, um sich hilfreiche Informationen anzusehen. Klicken Sie auf den CREATE-Listeneintrag, der Ihrer Anfrage entspricht, um einen neuen Journaleintrag zu erstellen. Daraufhin wird eine neue Tabelle angezeigt, die so aussieht:

385d62152e99aad4.png

Wie bereits erwähnt, bietet der Firestore-Emulator Tools zum Entwickeln der Sicherheitsregeln Ihrer Anwendung. In dieser Ansicht sehen Sie genau, welche Zeile in Ihren Sicherheitsregeln diese Anfrage bestanden (oder fehlgeschlagen ist, falls dies der Fall war). In einer robusteren App können Sicherheitsregeln erweitert werden und mehrere Autorisierungsüberprüfungen umfassen. Diese Ansicht wird zum Schreiben und Debuggen dieser Autorisierungsregeln verwendet.

Außerdem können Sie damit ganz einfach alle Teile dieser Anfrage prüfen, einschließlich der Metadaten und der Authentifizierungsdaten. Diese Daten werden zum Schreiben komplexer Autorisierungsregeln verwendet.

Aus Firestore lesen

Firestore nutzt die Datensynchronisierung, um aktualisierte Daten per Push an verbundene Geräte zu übertragen. In Flutter-Code können Sie Firestore-Sammlungen und ‑Dokumente abhören (oder abonnieren). Ihr Code wird dann jedes Mal benachrichtigt, wenn sich Daten ändern. In dieser Anwendung wird mit der Methode AppState._listenForEntries auf Firestore-Aktualisierungen gewartet.

Dieser Code funktioniert in Verbindung mit StreamController und Stream namens AppState._entriesStreamController bzw. AppState.entries. Dieser Code ist bereits geschrieben, ebenso wie der gesamte Code in der Benutzeroberfläche, der zum Anzeigen der Daten aus Firestore erforderlich ist.

Aktualisieren Sie die _listenForEntries-Methode so, dass sie dem Code unten entspricht:

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);
   });
 }
 // ...
}

Dieser Code überwacht die Sammlung „Einträge“ in Firestore. Wenn Firestore diesen Client benachrichtigt, dass neue Daten vorhanden sind, werden diese Daten übergeben und der Code in _listenForEntries ändert alle untergeordneten Dokumente in ein Objekt, das unsere App verwenden kann (Entry). Anschließend fügt es diese Einträge dem StreamController namens _entriesStreamController hinzu, das von der UI überwacht wird. Dieser Code ist das einzige erforderliche Update.

Denken Sie daran, dass die Methode AppState.logIn _listenForEntries aufruft, wodurch der Überwachungsprozess nach der Anmeldung eines Nutzers gestartet wird.

// ...
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!');
 }
}
// ...

Führen Sie die App jetzt aus. Sie sollte so aussehen:

b8a31c7a8900331.gif

7. Daten in den Emulator exportieren und importieren

Firebase-Emulatoren unterstützen das Importieren und Exportieren von Daten. Wenn Sie die Importe und Exporte verwenden, können Sie die Entwicklung mit denselben Daten fortsetzen, wenn Sie eine Pause einlegen und die Entwicklung fortsetzen. Sie können auch Datendateien in Git committen, damit andere Entwickler, mit denen Sie zusammenarbeiten, dieselben Daten verwenden können.

Emulatordaten exportieren

Exportieren Sie zunächst die Emulatordaten, die Sie bereits haben. Öffnen Sie ein neues Terminalfenster und geben Sie den folgenden Befehl ein, während die Emulatoren noch ausgeführt werden:

firebase emulators:export ./emulators_data

.emulators_data ist ein Argument, das Firebase angibt, wohin die Daten exportiert werden sollen. Wenn das Verzeichnis nicht vorhanden ist, wird es erstellt. Sie können einen beliebigen Namen für dieses Verzeichnis verwenden.

Wenn Sie diesen Befehl ausführen, wird im Terminal, in dem Sie den Befehl ausgeführt haben, diese Ausgabe angezeigt:

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

Wenn Sie zum Terminalfenster wechseln, in dem die Emulatoren ausgeführt werden, sehen Sie diese Ausgabe:

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

Im Projektverzeichnis sollte außerdem ein Verzeichnis namens ./emulators_data angezeigt werden, das unter anderem JSON-Dateien mit den von Ihnen gespeicherten Daten enthält.

Emulatordaten importieren

Jetzt können Sie diese Daten als Teil Ihres Entwicklungsworkflows importieren und dort weitermachen, wo Sie aufgehört haben.

Beenden Sie zuerst die Emulatoren, sofern sie ausgeführt werden. Drücken Sie dazu in Ihrem Terminal CTRL+C.

Führen Sie als Nächstes den Befehl emulators:start aus, den Sie bereits kennen, aber mit einem Flag, das angibt, welche Daten importiert werden sollen:

firebase emulators:start --import ./emulators_data

Wenn die Emulatoren gestartet sind, rufen Sie die Emulator-Benutzeroberfläche unter localhost:4000 auf. Dort sollten dieselben Daten angezeigt werden, mit denen Sie zuvor gearbeitet haben.

Daten beim Schließen von Emulatoren automatisch exportieren

Du kannst Daten auch automatisch exportieren, wenn du die Emulatoren beendest, anstatt daran zu denken, die Daten am Ende jeder Entwicklungssitzung zu exportieren.

Führen Sie beim Starten der Emulatoren den Befehl emulators:start mit zwei zusätzlichen Flags aus.

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

Voilà! Ihre Daten werden jetzt jedes Mal gespeichert und neu geladen, wenn Sie mit den Emulatoren für dieses Projekt arbeiten. Sie können auch ein anderes Verzeichnis als Argument für –export-on-exit flag angeben. Standardmäßig wird jedoch das Verzeichnis verwendet, das an –import übergeben wird.

Sie können auch eine Kombination dieser Optionen verwenden. Hinweis aus der Dokumentation: Das Exportverzeichnis kann mit diesem Flag angegeben werden: firebase emulators:start --export-on-exit=./saved-data. Wenn --import verwendet wird, ist der Exportpfad standardmäßig derselbe, z. B. firebase emulators:start --import=./data-path --export-on-exit. Übergeben Sie schließlich bei Bedarf verschiedene Verzeichnispfade an die Flags --import und --export-on-exit.

8. Glückwunsch!

Sie haben „Einrichtung und Ausführung mit dem Firebase-Emulator und Flutter“ abgeschlossen. Den vollständigen Code für dieses Codelab finden Sie im Verzeichnis „complete“ auf GitHub: Flutter Codelabs

Behandelte Themen

  • Flutter-App zur Verwendung von Firebase einrichten
  • Firebase-Projekt einrichten
  • FlutterFire-CLI
  • Firebase CLI
  • Firebase Authentication-Emulator
  • Firebase Firestore-Emulator
  • Emulatordaten importieren und exportieren

Nächste Schritte

Weitere Informationen

Sparky ist stolz auf dich!

2a0ad195769368b1.gif