Lernen Sie Firebase für Flutter kennen

1. Bevor Sie beginnen

In diesem Codelab lernen Sie einige der Grundlagen von Firebase kennen, um mobile Flutter-Apps für Android und iOS zu erstellen.

Voraussetzungen

Dieses Codelab geht davon aus, dass Sie mit Flutter vertraut sind und dass Sie das Flutter SDK und einen Editor installiert haben.

Was Sie erstellen werden

In diesem Codelab erstellen Sie mit Flutter eine Event-RSVP- und Gästebuch-Chat-App für Android, iOS, das Web und macOS. Sie authentifizieren Benutzer mit Firebase Authentication und synchronisieren Daten mit Cloud Firestore.

Was du brauchen wirst

Sie können dieses Codelab mit einem der folgenden Geräte ausführen:

  • Ein physisches Gerät (Android oder iOS), das mit Ihrem Computer verbunden und auf den Entwicklermodus eingestellt ist.
  • Der iOS-Simulator. (Erfordert die Installation von Xcode-Tools .)
  • Der Android-Emulator. (Erfordert Einrichtung in Android Studio .)

Zusätzlich zu den oben genannten benötigen Sie auch:

  • Ein Browser Ihrer Wahl, z. B. Chrome.
  • Eine IDE oder einen Texteditor Ihrer Wahl, z. B. Android Studio oder VS Code , konfiguriert mit den Dart- und Flutter-Plugins.
  • Die neueste stable Version von Flutter (oder beta , wenn Sie gerne am Limit leben).
  • Ein Google-Konto, wie ein Gmail-Konto, zum Erstellen und Verwalten Ihres Firebase-Projekts.
  • Das firebase -Befehlszeilentool , das bei Ihrem Gmail-Konto angemeldet ist.
  • Der Beispielcode des Codelabs. Im nächsten Schritt erfahren Sie, wie Sie den Code erhalten.

2. Holen Sie sich den Beispielcode

Beginnen wir mit dem Herunterladen der ersten Version unseres Projekts von GitHub.

Klonen Sie das GitHub-Repository über die Befehlszeile:

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

Alternativ, wenn Sie das CLI-Tool von GitHub installiert haben:

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-get-to-know-flutter .

Die Verzeichnisstruktur unter flutter-codelabs/firebase-get-to-know-flutter ist eine Reihe von Momentaufnahmen, wo Sie sich am Ende jedes benannten Schritts befinden sollten. Dies ist Schritt 2, das Auffinden der passenden Dateien ist also so einfach wie:

cd flutter-codelabs/firebase-get-to-know-flutter/step_02

Wenn Sie vorwärts springen oder sehen möchten, wie etwas nach einem Schritt aussehen sollte, schauen Sie in das Verzeichnis, das nach dem Schritt benannt ist, der Sie interessiert.

Importieren Sie die Starter-App

Öffnen oder importieren Sie das flutter-codelabs/firebase-get-to-know-flutter/step_02 in Ihre bevorzugte IDE. Dieses Verzeichnis enthält den Startcode für das Codelab, das aus einer noch nicht funktionierenden Flutter-Meetup-App besteht.

Suchen Sie die Dateien, an denen Sie arbeiten möchten

Der Code in dieser App ist über mehrere Verzeichnisse verteilt. Diese Aufteilung der Funktionalität soll die Arbeit erleichtern, indem Code nach Funktionalität gruppiert wird.

Suchen Sie die folgenden Dateien im Projekt:

  • lib/main.dart : Diese Datei enthält den Haupteinstiegspunkt und das Anwendungs-Widget.
  • lib/src/widgets.dart : Diese Datei enthält eine Handvoll Widgets, um das Design der Anwendung zu standardisieren. Diese werden verwendet, um den Bildschirm der Starter-App zusammenzustellen.
  • lib/src/authentication.dart : Diese Datei enthält eine teilweise Implementierung von FirebaseUI Auth mit einer Reihe von Widgets, um eine Anmeldebenutzererfahrung für die E-Mail-basierte Firebase-Authentifizierung zu erstellen. Diese Widgets für den Authentifizierungsablauf werden in der Starter-App noch nicht verwendet, aber Sie werden sie bald einbinden.

Sie fügen nach Bedarf zusätzliche Dateien hinzu, um den Rest der Anwendung zu erstellen.

Überprüfung der Datei lib/main.dart

Diese App nutzt das google_fonts -Paket, damit wir Roboto in der gesamten App zur Standardschriftart machen können. Eine Übung für den motivierten Leser besteht darin, fonts.google.com zu erkunden und die dort entdeckten Schriftarten in verschiedenen Teilen der App zu verwenden.

Sie verwenden die Hilfs-Widgets von lib/src/widgets.dart in Form von Header , Paragraph und IconAndDetail . Diese Widgets reduzieren Unordnung im Seitenlayout, das in HomePage beschrieben wird, indem doppelter Code eliminiert wird. Dies hat den zusätzlichen Vorteil, dass ein einheitliches Erscheinungsbild ermöglicht wird.

So sieht Ihre App auf Android, iOS, im Web und macOS aus:

App-Vorschau

3. Erstellen und richten Sie ein Firebase-Projekt ein

Das Anzeigen der Veranstaltungsinformationen ist großartig für Ihre Gäste, aber das bloße Anzeigen der Veranstaltungen ist für niemanden sehr nützlich. Fügen wir dieser App einige dynamische Funktionen hinzu. Dazu müssen Sie Firebase mit Ihrer App verbinden. Um mit Firebase zu beginnen, müssen Sie ein Firebase-Projekt erstellen und einrichten.

Erstellen Sie ein Firebase-Projekt

  1. Melden Sie sich bei Firebase an.
  2. Klicken Sie in der Firebase-Konsole auf Projekt hinzufügen (oder Projekt erstellen ) und nennen Sie Ihr Firebase-Projekt Firebase-Flutter-Codelab .

4395e4e67c08043a.png

  1. Klicken Sie sich durch die Projekterstellungsoptionen. 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.

b7138cde5f2c7b61.png

Weitere Informationen zu Firebase-Projekten finden Sie unter Grundlegendes zu Firebase-Projekten .

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

  • Firebase-Authentifizierung , damit sich Ihre Benutzer bei Ihrer App anmelden können.
  • Cloud Firestore , um strukturierte Daten in der Cloud zu speichern und sofort benachrichtigt zu werden, wenn sich Daten ändern.
  • Firebase-Sicherheitsregeln zum Schutz Ihrer Datenbank.

Einige dieser Produkte erfordern eine spezielle Konfiguration oder müssen über die Firebase-Konsole aktiviert werden.

Aktivieren Sie die E-Mail-Anmeldung für die Firebase-Authentifizierung

Um Benutzern die Anmeldung bei der Web-App zu ermöglichen, verwenden Sie für dieses Codelab die E- Mail/Passwort -Anmeldemethode:

  1. Erweitern Sie in der Firebase-Konsole das Menü „ Build “ im linken Bereich.
  2. Klicken Sie auf Authentifizierung und dann auf die Schaltfläche Erste Schritte und dann auf die Registerkarte Anmeldemethode (oder klicken Sie hier , um direkt zur Registerkarte Anmeldemethode zu wechseln).
  3. Klicken Sie in der Liste Anmeldeanbieter auf E- Mail/Kennwort , setzen Sie den Schalter Aktivieren auf die Position Ein und klicken Sie dann auf Speichern . 58e3e3e23c2f16a4.png

Aktivieren Sie Cloud Firestore

Die Web-App verwendet Cloud Firestore , um Chat-Nachrichten zu speichern und neue Chat-Nachrichten zu empfangen.

Cloud Firestore aktivieren:

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

4. Firebase-Konfiguration

Um Firebase mit Flutter zu verwenden, müssen Sie einem Prozess folgen, um das Flutter-Projekt so zu konfigurieren, dass die FlutterFire-Bibliotheken korrekt verwendet werden:

  • Fügen Sie Ihrem Projekt die FlutterFire-Abhängigkeiten hinzu
  • Registrieren Sie die gewünschte Plattform im Firebase-Projekt
  • Laden Sie die plattformspezifische Konfigurationsdatei herunter und fügen Sie sie dem Code hinzu.

Im obersten Verzeichnis Ihrer Flutter-App gibt es Unterverzeichnisse namens android , ios , macos und web . Diese Verzeichnisse enthalten die plattformspezifischen Konfigurationsdateien für iOS bzw. Android.

Abhängigkeiten konfigurieren

Sie müssen die FlutterFire-Bibliotheken für die beiden Firebase-Produkte hinzufügen, die Sie in dieser App verwenden – Firebase Auth und Cloud Firestore. Führen Sie die folgenden drei Befehle aus, um die Abhängigkeiten hinzuzufügen.

$ flutter pub add firebase_core 
Resolving dependencies...
+ firebase_core 1.10.5
+ firebase_core_platform_interface 4.2.2
+ firebase_core_web 1.5.2
+ flutter_web_plugins 0.0.0 from sdk flutter
+ js 0.6.3
  test_api 0.4.3 (0.4.8 available)
Changed 5 dependencies!

firebase_core ist der gemeinsame Code, der für alle Firebase Flutter-Plug-ins erforderlich ist.

$ flutter pub add firebase_auth
Resolving dependencies...
+ firebase_auth 3.3.3
+ firebase_auth_platform_interface 6.1.8
+ firebase_auth_web 3.3.4
+ intl 0.17.0
  test_api 0.4.3 (0.4.8 available)
Changed 4 dependencies!

firebase_auth ermöglicht die Integration mit der Authentifizierungsfunktion von Firebase.

$ flutter pub add cloud_firestore
Resolving dependencies...
+ cloud_firestore 3.1.4
+ cloud_firestore_platform_interface 5.4.9
+ cloud_firestore_web 2.6.4
  test_api 0.4.3 (0.4.8 available)
Changed 3 dependencies!

Der cloud_firestore ermöglicht den Zugriff auf den Cloud Firestore-Datenspeicher.

$ flutter pub add provider
Resolving dependencies...
+ nested 1.0.0
+ provider 6.0.1
  test_api 0.4.3 (0.4.8 available)
Changed 2 dependencies!

Während Sie die erforderlichen Pakete hinzugefügt haben, müssen Sie auch die iOS-, Android-, macOS- und Web-Runner-Projekte konfigurieren, um Firebase angemessen zu nutzen. Sie verwenden auch das provider , das die Trennung der Geschäftslogik von der Anzeigelogik ermöglicht.

flutterfire

Die FlutterFire-CLI hängt von der zugrunde liegenden Firebase-CLI ab. Stellen Sie sicher, dass die Firebase CLI auf Ihrem Computer installiert ist, falls Sie dies noch nicht getan haben.

Installieren Sie als Nächstes die FlutterFire-CLI, indem Sie den folgenden Befehl ausführen:

$ dart pub global activate flutterfire_cli

Nach der Installation ist der flutterfire Befehl global verfügbar.

Konfigurieren Sie Ihre Apps

Die CLI extrahiert Informationen aus Ihrem Firebase-Projekt und ausgewählten Projektanwendungen, um die gesamte Konfiguration für eine bestimmte Plattform zu generieren.

Führen Sie im Stammverzeichnis Ihrer Anwendung den Befehl configure aus:

$ flutterfire configure

Der Konfigurationsbefehl führt Sie durch eine Reihe von Prozessen:

  1. Auswählen eines Firebase-Projekts (basierend auf der .firebaserc-Datei oder aus der Firebase-Konsole).
  2. Fragen Sie nach, für welche Plattformen (z. B. Android, iOS, macOS & Web) Sie die Konfiguration wünschen.
  3. Ermitteln Sie, welche Firebase-Anwendungen für die ausgewählten Plattformen zum Extrahieren der Konfiguration verwendet werden sollen. Standardmäßig versucht die CLI, Firebase-Apps basierend auf Ihrer aktuellen Projektkonfiguration automatisch abzugleichen.
  4. Generieren Sie in Ihrem Projekt eine firebase_options.dart-Datei.

macOS konfigurieren

Flutter auf macOS erstellt vollständig Sandbox-Anwendungen. Da diese Anwendung über das Netzwerk integriert wird, um mit den Firebase-Servern zu kommunizieren, müssen Sie Ihre Anwendung mit Netzwerk-Client-Berechtigungen konfigurieren.

macos/Runner/DebugProfile.entitlements

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.security.app-sandbox</key>
	<true/>
	<key>com.apple.security.cs.allow-jit</key>
	<true/>
	<key>com.apple.security.network.server</key>
	<true/>
  <!-- Add the following two lines -->
	<key>com.apple.security.network.client</key>
	<true/>
</dict>
</plist>

macos/Runner/Release.entitlements

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.security.app-sandbox</key>
	<true/>
  <!-- Add the following two lines -->
	<key>com.apple.security.network.client</key>
	<true/>
</dict>
</plist>

Weitere Einzelheiten finden Sie unter Berechtigungen und die App-Sandbox .

5. Benutzeranmeldung hinzufügen (RSVP)

Nachdem Sie Firebase zur App hinzugefügt haben, können Sie eine RSVP-Schaltfläche einrichten, die Personen mit Firebase-Authentifizierung registriert. Für Android native, iOS native und Web gibt es vorgefertigte FirebaseUI Auth-Pakete, aber für Flutter müssen Sie diese Funktion erstellen.

Das Projekt, das Sie in Schritt 2 abgerufen haben, enthielt eine Reihe von Widgets, die die Benutzeroberfläche für den größten Teil des Authentifizierungsflusses implementieren. Sie implementieren die Geschäftslogik, um die Firebase-Authentifizierung in die Anwendung zu integrieren.

Geschäftslogik mit Anbieter

Sie werden das provider -Paket verwenden, um ein zentralisiertes Anwendungszustandsobjekt im gesamten Baum der Flutter-Widgets der Anwendung verfügbar zu machen. Ändern Sie zunächst die Importe oben in lib/main.dart :

lib/main.dart

import 'package:firebase_auth/firebase_auth.dart'; // new
import 'package:firebase_core/firebase_core.dart'; // new
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';           // new

import 'firebase_options.dart';                    // new
import 'src/authentication.dart';                  // new
import 'src/widgets.dart';

Die import führen Firebase Core und Auth ein, laden das provider , das Sie verwenden, um das Anwendungszustandsobjekt über die Widget-Struktur verfügbar zu machen, und schließen die Authentifizierungs-Widgets von lib/src ein.

Dieses Anwendungszustandsobjekt, ApplicationState , hat zwei Hauptverantwortlichkeiten für diesen Schritt, erhält aber zusätzliche Verantwortlichkeiten, wenn Sie der Anwendung in späteren Schritten weitere Funktionen hinzufügen. Die erste Verantwortung besteht darin, die Firebase-Bibliothek mit einem Aufruf von Firebase.initializeApp() zu initialisieren, und dann gibt es die Abwicklung des Autorisierungsflusses. Fügen Sie die folgende Klasse am Ende von lib/main.dart :

lib/main.dart

class ApplicationState extends ChangeNotifier {
  ApplicationState() {
    init();
  }

  Future<void> init() async {
    await Firebase.initializeApp(
      options: DefaultFirebaseOptions.currentPlatform,
    );

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
      } else {
        _loginState = ApplicationLoginState.loggedOut;
      }
      notifyListeners();
    });
  }

  ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
  ApplicationLoginState get loginState => _loginState;

  String? _email;
  String? get email => _email;

  void startLoginFlow() {
    _loginState = ApplicationLoginState.emailAddress;
    notifyListeners();
  }

  Future<void> verifyEmail(
    String email,
    void Function(FirebaseAuthException e) errorCallback,
  ) async {
    try {
      var methods =
          await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);
      if (methods.contains('password')) {
        _loginState = ApplicationLoginState.password;
      } else {
        _loginState = ApplicationLoginState.register;
      }
      _email = email;
      notifyListeners();
    } on FirebaseAuthException catch (e) {
      errorCallback(e);
    }
  }

  Future<void> signInWithEmailAndPassword(
    String email,
    String password,
    void Function(FirebaseAuthException e) errorCallback,
  ) async {
    try {
      await FirebaseAuth.instance.signInWithEmailAndPassword(
        email: email,
        password: password,
      );
    } on FirebaseAuthException catch (e) {
      errorCallback(e);
    }
  }

  void cancelRegistration() {
    _loginState = ApplicationLoginState.emailAddress;
    notifyListeners();
  }

  Future<void> registerAccount(
      String email,
      String displayName,
      String password,
      void Function(FirebaseAuthException e) errorCallback) async {
    try {
      var credential = await FirebaseAuth.instance
          .createUserWithEmailAndPassword(email: email, password: password);
      await credential.user!.updateDisplayName(displayName);
    } on FirebaseAuthException catch (e) {
      errorCallback(e);
    }
  }

  void signOut() {
    FirebaseAuth.instance.signOut();
  }
}

Es lohnt sich, einige wichtige Punkte in dieser Klasse zu beachten. Der Benutzer beginnt nicht authentifiziert, die App zeigt ein Formular an, in dem die E-Mail-Adresse des Benutzers angefordert wird. Je nachdem, ob diese E-Mail-Adresse gespeichert ist, fordert die App den Benutzer entweder auf, sich zu registrieren, oder fordert sein Passwort an, und dann, wenn alles funktioniert, den Benutzer authentifiziert ist.

Es muss beachtet werden, dass dies keine vollständige Implementierung des FirebaseUI-Authentifizierungsflusses ist, da es nicht den Fall eines Benutzers mit einem bestehenden Konto behandelt, der Probleme beim Anmelden hat. Die Implementierung dieser zusätzlichen Funktion bleibt als Übung dem überlassen motivierter Leser.

Integrieren des Authentifizierungsflusses

Nachdem Sie nun den Start des Anwendungsstatus haben, ist es an der Zeit, den Anwendungsstatus mit der App-Initialisierung zu verbinden und den Authentifizierungsfluss zu HomePage hinzuzufügen. Aktualisieren Sie den Haupteinstiegspunkt, um den Anwendungsstatus über das provider zu integrieren:

lib/main.dart

void main() {
  // Modify from here
  runApp(
    ChangeNotifierProvider(
      create: (context) => ApplicationState(),
      builder: (context, _) => App(),
    ),
  );
  // to here.
}

Durch die Änderung der main wird das Anbieterpaket für die Instanziierung des Anwendungszustandsobjekts mithilfe des ChangeNotifierProvider Widgets verantwortlich. Sie verwenden diese spezielle Anbieterklasse, weil das Anwendungszustandsobjekt ChangeNotifier erweitert und das provider dadurch weiß, wann abhängige Widgets erneut angezeigt werden sollen. Integrieren Sie schließlich den Anwendungsstatus in die Authentication , indem Sie die build von HomePage aktualisieren:

lib/main.dart

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Firebase Meetup'),
      ),
      body: ListView(
        children: <Widget>[
          Image.asset('assets/codelab.png'),
          const SizedBox(height: 8),
          const IconAndDetail(Icons.calendar_today, 'October 30'),
          const IconAndDetail(Icons.location_city, 'San Francisco'),
          // Add from here
          Consumer<ApplicationState>(
            builder: (context, appState, _) => Authentication(
              email: appState.email,
              loginState: appState.loginState,
              startLoginFlow: appState.startLoginFlow,
              verifyEmail: appState.verifyEmail,
              signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
              cancelRegistration: appState.cancelRegistration,
              registerAccount: appState.registerAccount,
              signOut: appState.signOut,
            ),
          ),
          // to here
          const Divider(
            height: 8,
            thickness: 1,
            indent: 8,
            endIndent: 8,
            color: Colors.grey,
          ),
          const Header("What we'll be doing"),
          const Paragraph(
            'Join us for a day full of Firebase Workshops and Pizza!',
          ),
        ],
      ),
    );
  }
}

Sie instanziieren das Authentication -Widget und packen es in ein Consumer -Widget. Das Consumer-Widget ist die übliche Art und Weise, wie das provider verwendet werden kann, um einen Teil des Baums neu zu erstellen, wenn sich der Anwendungsstatus ändert. Das Authentication -Widget ist die Authentifizierungs-UI, die Sie jetzt testen werden.

Testen des Authentifizierungsflusses

cdf2d25e436bd48d.png

Hier ist der Beginn des Authentifizierungsablaufs, bei dem der Benutzer auf die RSVP-Schaltfläche tippen kann, um das E-Mail-Formular zu initiieren.

2a2cd6d69d172369.png

Bei der Eingabe der E-Mail bestätigt das System, ob der Benutzer bereits registriert ist. In diesem Fall wird der Benutzer zur Eingabe eines Passworts aufgefordert. Wenn der Benutzer alternativ nicht registriert ist, geht er durch das Registrierungsformular.

e5e65065dba36b54.png

Versuchen Sie unbedingt, ein kurzes Passwort (weniger als sechs Zeichen) einzugeben, um den Ablauf der Fehlerbehandlung zu überprüfen. Wenn der Benutzer registriert ist, sieht er stattdessen das Passwort für.

fbb3ea35fb4f67a.png

Achten Sie auf dieser Seite darauf, falsche Passwörter einzugeben, um die Fehlerbehandlung auf dieser Seite zu überprüfen. Sobald der Benutzer eingeloggt ist, sehen Sie schließlich die eingeloggte Erfahrung, die dem Benutzer die Möglichkeit bietet, sich wieder abzumelden.

4ed811a25b0cf816.png

Und damit haben Sie einen Authentifizierungsfluss implementiert. Herzlichen Glückwunsch!

6. Schreiben Sie Nachrichten an Cloud Firestore

Zu wissen, dass Benutzer kommen, ist großartig, aber geben wir den Gästen in der App etwas anderes zu tun. Was wäre, wenn sie Nachrichten in einem Gästebuch hinterlassen könnten? Sie können mitteilen, warum sie gerne kommen oder wen sie zu treffen hoffen.

Um die Chat-Nachrichten zu speichern, die Benutzer in der App schreiben, verwenden Sie Cloud Firestore .

Datenmodell

Cloud Firestore ist eine NoSQL-Datenbank, und in der Datenbank gespeicherte Daten sind in Sammlungen, Dokumente, Felder und Untersammlungen aufgeteilt. Sie speichern jede Nachricht des Chats als Dokument in einer Sammlung der obersten Ebene namens guestbook .

7c20dc8424bb1d84.png

Nachrichten zu Firestore hinzufügen

In diesem Abschnitt fügen Sie die Funktionalität hinzu, mit der Benutzer neue Nachrichten in die Datenbank schreiben können. Zuerst fügen Sie die UI-Elemente (Formularfeld und Sendeschaltfläche) hinzu und dann fügen Sie den Code hinzu, der diese Elemente mit der Datenbank verbindet.

Fügen Sie zunächst Importe für das Paket cloud_firestore und dart:async hinzu.

lib/main.dart

import 'dart:async';                                    // new

import 'package:cloud_firestore/cloud_firestore.dart';  // new
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';

import 'firebase_options.dart';
import 'src/authentication.dart';
import 'src/widgets.dart';

Um die UI-Elemente eines Nachrichtenfelds und einer Sendeschaltfläche zu erstellen, fügen Sie ein neues Stateful-Widget GuestBook am Ende von lib/main.dart .

lib/main.dart

class GuestBook extends StatefulWidget {
  const GuestBook({required this.addMessage});
  final FutureOr<void> Function(String message) addMessage;

  @override
  _GuestBookState createState() => _GuestBookState();
}

class _GuestBookState extends State<GuestBook> {
  final _formKey = GlobalKey<FormState>(debugLabel: '_GuestBookState');
  final _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Form(
        key: _formKey,
        child: Row(
          children: [
            Expanded(
              child: TextFormField(
                controller: _controller,
                decoration: const InputDecoration(
                  hintText: 'Leave a message',
                ),
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return 'Enter your message to continue';
                  }
                  return null;
                },
              ),
            ),
            const SizedBox(width: 8),
            StyledButton(
              onPressed: () async {
                if (_formKey.currentState!.validate()) {
                  await widget.addMessage(_controller.text);
                  _controller.clear();
                }
              },
              child: Row(
                children: const [
                  Icon(Icons.send),
                  SizedBox(width: 4),
                  Text('SEND'),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Hier gibt es ein paar interessante Punkte. Zunächst instanziieren Sie ein Formular, damit Sie überprüfen können, ob die Nachricht tatsächlich einen Inhalt hat, und dem Benutzer eine Fehlermeldung anzeigen, falls keine vorhanden ist. Um ein Formular zu validieren, muss auf den Formularstatus hinter dem Formular zugegriffen werden, und dafür verwenden Sie einen GlobalKey . Weitere Informationen zu Tasten und deren Verwendung finden Sie in der Flutter Widgets 101-Episode „When to Use Keys“ .

Beachten Sie auch, wie die Widgets angeordnet sind, Sie haben eine Row mit einem TextFormField und einem StyledButton , der selbst eine Row enthält. Beachten Sie auch, dass das TextFormField in ein Expanded -Widget eingeschlossen ist, wodurch das TextFormField wird, zusätzlichen Platz in der Zeile einzunehmen. Um besser zu verstehen, warum dies erforderlich ist, lesen Sie bitte Einschränkungen verstehen .

Nachdem Sie nun ein Widget haben, mit dem der Benutzer Text eingeben kann, um ihn dem Gästebuch hinzuzufügen, müssen Sie ihn auf den Bildschirm bringen. Bearbeiten Sie dazu den Hauptteil von HomePage , um die folgenden zwei Zeilen am Ende der untergeordneten Elemente von ListView :

const Header("What we'll be doing"),
const Paragraph(
  'Join us for a day full of Firebase Workshops and Pizza!',
),
// Add the following two lines.
const Header('Discussion'),
GuestBook(addMessage: (message) => print(message)),

Dies reicht zwar aus, um das Widget anzuzeigen, aber nicht, um etwas Nützliches zu tun. Sie werden diesen Code in Kürze aktualisieren, damit er funktioniert.

App-Vorschau

Ein Benutzer, der auf die Schaltfläche SENDEN klickt, löst das unten stehende Code-Snippet aus. Es fügt den Inhalt des Nachrichteneingabefelds der guestbook der Datenbank hinzu. Insbesondere fügt die addMessageToGuestBook Methode den Nachrichteninhalt einem neuen Dokument (mit einer automatisch generierten ID) zur guestbook hinzu.

Beachten Sie, dass FirebaseAuth.instance.currentUser.uid ein Verweis auf die automatisch generierte eindeutige ID ist, die Firebase Authentication für alle angemeldeten Benutzer bereitstellt.

Nehmen Sie eine weitere Änderung an der Datei lib/main.dart . Fügen Sie die addMessageToGuestBook Methode hinzu. Im nächsten Schritt werden Sie die Benutzerschnittstelle und diese Funktion miteinander verbinden.

lib/main.dart

class ApplicationState extends ChangeNotifier {

  // Current content of ApplicationState elided ...

  // Add from here
  Future<DocumentReference> addMessageToGuestBook(String message) {
    if (_loginState != ApplicationLoginState.loggedIn) {
      throw Exception('Must be logged in');
    }

    return FirebaseFirestore.instance
        .collection('guestbook')
        .add(<String, dynamic>{
      'text': message,
      'timestamp': DateTime.now().millisecondsSinceEpoch,
      'name': FirebaseAuth.instance.currentUser!.displayName,
      'userId': FirebaseAuth.instance.currentUser!.uid,
    });
  }
  // To here
}

Verbinden der Benutzeroberfläche mit der Datenbank

Sie haben eine Benutzeroberfläche, auf der der Benutzer den Text eingeben kann, den er zum Gästebuch hinzufügen möchte, und Sie haben den Code, um den Eintrag zu Cloud Firestore hinzuzufügen. Jetzt müssen Sie die beiden nur noch miteinander verdrahten. Nehmen Sie in lib/main.dart die folgende Änderung am HomePage -Widget vor.

lib/main.dart

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Firebase Meetup'),
      ),
      body: ListView(
        children: <Widget>[
          Image.asset('assets/codelab.png'),
          const SizedBox(height: 8),
          const IconAndDetail(Icons.calendar_today, 'October 30'),
          const IconAndDetail(Icons.location_city, 'San Francisco'),
          Consumer<ApplicationState>(
            builder: (context, appState, _) => Authentication(
              email: appState.email,
              loginState: appState.loginState,
              startLoginFlow: appState.startLoginFlow,
              verifyEmail: appState.verifyEmail,
              signInWithEmailAndPassword: appState.signInWithEmailAndPassword,
              cancelRegistration: appState.cancelRegistration,
              registerAccount: appState.registerAccount,
              signOut: appState.signOut,
            ),
          ),
          const Divider(
            height: 8,
            thickness: 1,
            indent: 8,
            endIndent: 8,
            color: Colors.grey,
          ),
          const Header("What we'll be doing"),
          const Paragraph(
            'Join us for a day full of Firebase Workshops and Pizza!',
          ),
          // Modify from here
          Consumer<ApplicationState>(
            builder: (context, appState, _) => Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                if (appState.loginState == ApplicationLoginState.loggedIn) ...[
                  const Header('Discussion'),
                  GuestBook(
                    addMessage: (message) =>
                        appState.addMessageToGuestBook(message),
                  ),
                ],
              ],
            ),
          ),
          // To here.
        ],
      ),
    );
  }
}

Sie haben die beiden Zeilen, die Sie zu Beginn dieses Schritts hinzugefügt haben, durch die vollständige Implementierung ersetzt. Sie verwenden wieder Consumer<ApplicationState> , um den Anwendungsstatus für den Teil der Struktur verfügbar zu machen, den Sie rendern. Auf diese Weise können Sie auf jemanden reagieren, der eine Nachricht in die Benutzeroberfläche eingibt, und diese in der Datenbank veröffentlichen. Im nächsten Abschnitt testen Sie, ob die hinzugefügten Nachrichten in der Datenbank veröffentlicht werden.

Testen Sie das Senden von Nachrichten

  1. Stellen Sie sicher, dass Sie bei der App angemeldet sind.
  2. Geben Sie eine Nachricht wie „Hey there!“ ein und klicken Sie dann auf SENDEN .

Diese Aktion schreibt die Nachricht in Ihre Cloud Firestore-Datenbank. Sie werden die Nachricht jedoch noch nicht in Ihrer eigentlichen Flutter-App sehen, da Sie das Abrufen der Daten noch implementieren müssen. Das machst du im nächsten Schritt.

Sie können die neu hinzugefügte Nachricht jedoch in der Firebase-Konsole sehen.

In der Firebase-Konsole sollten Sie im Datenbank -Dashboard die guestbook mit Ihrer neu hinzugefügten Nachricht sehen. Wenn Sie weiterhin Nachrichten senden, enthält Ihre Gästebuchsammlung viele Dokumente, wie zum Beispiel:

Firebase-Konsole

713870af0b3b63c.png

7. Nachrichten lesen

Es ist schön, dass Gäste Nachrichten in die Datenbank schreiben können, aber sie können sie noch nicht in der App sehen. Lassen Sie uns das beheben!

Nachrichten synchronisieren

Um Nachrichten anzuzeigen, müssen Sie Listener hinzufügen, die ausgelöst werden, wenn sich Daten ändern, und dann ein UI-Element erstellen, das neue Nachrichten anzeigt. Sie fügen dem Anwendungsstatus Code hinzu, der auf neu hinzugefügte Nachrichten von der App wartet.

Direkt über dem GuestBook Widget die folgende Werteklasse. Diese Klasse stellt eine strukturierte Ansicht der Daten bereit, die Sie in Cloud Firestore speichern.

lib/main.dart

class GuestBookMessage {
  GuestBookMessage({required this.name, required this.message});
  final String name;
  final String message;
}

Fügen Sie im Abschnitt von ApplicationState , in dem Sie Status und Getter definieren, die folgenden neuen Zeilen hinzu:

lib/main.dart

  ApplicationLoginState _loginState = ApplicationLoginState.loggedOut;
  ApplicationLoginState get loginState => _loginState;

  String? _email;
  String? get email => _email;

  // Add from here
  StreamSubscription<QuerySnapshot>? _guestBookSubscription;
  List<GuestBookMessage> _guestBookMessages = [];
  List<GuestBookMessage> get guestBookMessages => _guestBookMessages;
  // to here.

Fügen Sie schließlich im Initialisierungsabschnitt von ApplicationState Folgendes hinzu, um eine Abfrage über die Dokumentensammlung zu abonnieren, wenn sich ein Benutzer anmeldet, und abzumelden, wenn er sich abmeldet.

lib/main.dart

  Future<void> init() async {
    await Firebase.initializeApp(
      options: DefaultFirebaseOptions.currentPlatform,
    );

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
        // Add from here
        _guestBookSubscription = FirebaseFirestore.instance
            .collection('guestbook')
            .orderBy('timestamp', descending: true)
            .snapshots()
            .listen((snapshot) {
          _guestBookMessages = [];
          for (final document in snapshot.docs) {
            _guestBookMessages.add(
              GuestBookMessage(
                name: document.data()['name'] as String,
                message: document.data()['text'] as String,
              ),
            );
          }
          notifyListeners();
        });
        // to here.
      } else {
        _loginState = ApplicationLoginState.loggedOut;
        // Add from here
        _guestBookMessages = [];
        _guestBookSubscription?.cancel();
        // to here.
      }
      notifyListeners();
    });
  }

Dieser Abschnitt ist wichtig, da Sie hier eine Abfrage über die guestbook erstellen und das Abonnieren und Abbestellen dieser Sammlung handhaben. Sie hören sich den Stream an, in dem Sie einen lokalen Cache der Nachrichten in der guestbook rekonstruieren, und speichern auch einen Verweis auf dieses Abonnement, damit Sie es später wieder abbestellen können. Hier ist viel los, und es lohnt sich, einige Zeit in einem Debugger zu verbringen, um zu untersuchen, was wann passiert, um ein klareres mentales Modell zu erhalten.

Weitere Informationen finden Sie in der Cloud Firestore-Dokumentation .

Im GuestBook Widget müssen Sie diesen sich ändernden Zustand mit der Benutzeroberfläche verbinden. Sie ändern das Widget, indem Sie als Teil seiner Konfiguration eine Liste mit Nachrichten hinzufügen.

lib/main.dart

class GuestBook extends StatefulWidget {
  // Modify the following line
  const GuestBook({required this.addMessage, required this.messages});
  final FutureOr<void> Function(String message) addMessage;
  final List<GuestBookMessage> messages; // new

  @override
  _GuestBookState createState() => _GuestBookState();
}

Als Nächstes machen wir diese neue Konfiguration in _GuestBookState , indem wir die build -Methode wie folgt ändern.

lib/main.dart

class _GuestBookState extends State<GuestBook> {
  final _formKey = GlobalKey<FormState>(debugLabel: '_GuestBookState');
  final _controller = TextEditingController();

  @override
  // Modify from here
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        // to here.
        Padding(
          padding: const EdgeInsets.all(8.0),
          child: Form(
            key: _formKey,
            child: Row(
              children: [
                Expanded(
                  child: TextFormField(
                    controller: _controller,
                    decoration: const InputDecoration(
                      hintText: 'Leave a message',
                    ),
                    validator: (value) {
                      if (value == null || value.isEmpty) {
                        return 'Enter your message to continue';
                      }
                      return null;
                    },
                  ),
                ),
                const SizedBox(width: 8),
                StyledButton(
                  onPressed: () async {
                    if (_formKey.currentState!.validate()) {
                      await widget.addMessage(_controller.text);
                      _controller.clear();
                    }
                  },
                  child: Row(
                    children: const [
                      Icon(Icons.send),
                      SizedBox(width: 4),
                      Text('SEND'),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
        // Modify from here
        const SizedBox(height: 8),
        for (var message in widget.messages)
          Paragraph('${message.name}: ${message.message}'),
        const SizedBox(height: 8),
      ],
      // to here.
    );
  }
}

Sie umschließen den vorherigen Inhalt der Build-Methode mit einem Column -Widget und fügen dann am Ende der untergeordneten Elemente von Column eine Sammlung hinzu, um einen neuen Paragraph für jede Nachricht in der Nachrichtenliste zu generieren.

Schließlich müssen Sie jetzt den Hauptteil von HomePage aktualisieren, um das GuestBook mit dem neuen messages korrekt zu erstellen.

lib/main.dart

Consumer<ApplicationState>(
  builder: (context, appState, _) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      if (appState.loginState == ApplicationLoginState.loggedIn) ...[
        const Header('Discussion'),
        GuestBook(
          addMessage: (message) =>
              appState.addMessageToGuestBook(message),
          messages: appState.guestBookMessages, // new
        ),
      ],
    ],
  ),
),

Testen Sie die Synchronisierung von Nachrichten

Cloud Firestore synchronisiert automatisch und sofort Daten mit Clients, die die Datenbank abonniert haben.

  1. Die Nachrichten, die Sie zuvor in der Datenbank erstellt haben, sollten in der App angezeigt werden. Fühlen Sie sich frei, neue Nachrichten zu schreiben; Sie sollten sofort erscheinen.
  2. Wenn Sie Ihren Arbeitsbereich in mehreren Fenstern oder Registerkarten öffnen, werden die Nachrichten in Echtzeit über die Registerkarten hinweg synchronisiert.
  3. (Optional) Sie können versuchen, Nachrichten manuell direkt im Abschnitt „ Datenbank “ der Firebase-Konsole zu löschen, zu ändern oder neue hinzuzufügen. Alle Änderungen sollten in der Benutzeroberfläche angezeigt werden.

Herzliche Glückwünsche! Sie lesen Cloud Firestore-Dokumente in Ihrer App!

App-P- Rezension

8. Richten Sie grundlegende Sicherheitsregeln ein

Sie haben Cloud Firestore zunächst für die Verwendung des Testmodus eingerichtet, was bedeutet, dass Ihre Datenbank für Lese- und Schreibvorgänge geöffnet ist. Sie sollten den Testmodus jedoch nur in sehr frühen Phasen der Entwicklung verwenden. Als Best Practice sollten Sie bei der Entwicklung Ihrer App Sicherheitsregeln für Ihre Datenbank einrichten. Sicherheit sollte ein integraler Bestandteil der Struktur und des Verhaltens Ihrer App sein.

Mit Sicherheitsregeln können Sie den Zugriff auf Dokumente und Sammlungen in Ihrer Datenbank steuern. Mit der flexiblen Regelsyntax können Sie Regeln erstellen, die alles abgleichen, von allen Schreibvorgängen in die gesamte Datenbank bis hin zu Operationen in einem bestimmten Dokument.

Sie können Sicherheitsregeln für Cloud Firestore in der Firebase-Konsole schreiben:

  1. Klicken Sie im Abschnitt „ Entwickeln “ der Firebase-Konsole auf „ Datenbank “ und wählen Sie dann die Registerkarte „ Regeln“ aus (oder klicken Sie hier , um direkt zur Registerkarte „ Regeln“ zu wechseln).
  2. Sie sollten die folgenden Standardsicherheitsregeln zusammen mit einer Warnung sehen, dass die Regeln öffentlich sind.

7767a2d2e64e7275.png

Sammlungen identifizieren

Identifizieren Sie zunächst die Sammlungen, in die die App Daten schreibt.

Identifizieren Sie in match /databases/{database}/documents die Sammlung, die Sie sichern möchten:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /guestbook/{entry} {
     // You'll add rules here in the next step.
  }
}

Fügen Sie Sicherheitsregeln hinzu

Da Sie die Authentifizierungs-UID als Feld in jedem Gästebuchdokument verwendet haben, können Sie die Authentifizierungs-UID abrufen und überprüfen, ob jeder, der versucht, in das Dokument zu schreiben, über eine passende Authentifizierungs-UID verfügt.

Fügen Sie die Lese- und Schreibregeln wie unten gezeigt zu Ihrem Regelsatz hinzu:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /guestbook/{entry} {
      allow read: if request.auth.uid != null;
      allow write:
        if request.auth.uid == request.resource.data.userId;
    }
  }
}

Für das Gästebuch können jetzt nur angemeldete Benutzer Nachrichten lesen (jede Nachricht!), aber nur der Autor einer Nachricht kann eine Nachricht bearbeiten.

Validierungsregeln hinzufügen

Fügen Sie eine Datenvalidierung hinzu, um sicherzustellen, dass alle erwarteten Felder im Dokument vorhanden sind:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /guestbook/{entry} {
      allow read: if request.auth.uid != null;
      allow write:
      if request.auth.uid == request.resource.data.userId
          && "name" in request.resource.data
          && "text" in request.resource.data
          && "timestamp" in request.resource.data;
    }
  }
}

9. Bonusschritt: Üben Sie, was Sie gelernt haben

Erfassen Sie den RSVP-Status eines Teilnehmers

Im Moment erlaubt Ihre App nur Leuten, mit dem Chat zu beginnen, wenn sie an der Veranstaltung interessiert sind. Außerdem wissen Sie nur, ob jemand kommt, wenn er es im Chat postet. Lasst uns organisieren und die Leute wissen lassen, wie viele Leute kommen.

Sie werden dem Anwendungsstatus einige neue Funktionen hinzufügen. Die erste ist die Möglichkeit für einen angemeldeten Benutzer, zu nominieren, ob er teilnimmt oder nicht. Die zweite Funktion ist ein Zähler dafür, wie viele Personen tatsächlich teilnehmen.

Fügen Sie in lib/main.dart Folgendes zum Accessors-Abschnitt hinzu, damit der UI-Code mit diesem Zustand interagieren kann:

lib/main.dart

int _attendees = 0;
int get attendees => _attendees;

Attending _attending = Attending.unknown;
StreamSubscription<DocumentSnapshot>? _attendingSubscription;
Attending get attending => _attending;
set attending(Attending attending) {
  final userDoc = FirebaseFirestore.instance
      .collection('attendees')
      .doc(FirebaseAuth.instance.currentUser!.uid);
  if (attending == Attending.yes) {
    userDoc.set(<String, dynamic>{'attending': true});
  } else {
    userDoc.set(<String, dynamic>{'attending': false});
  }
}

Aktualisieren Sie die init -Methode von ApplicationState wie folgt:

lib/main.dart

  Future<void> init() async {
    await Firebase.initializeApp(
      options: DefaultFirebaseOptions.currentPlatform,
    );

    // Add from here
    FirebaseFirestore.instance
        .collection('attendees')
        .where('attending', isEqualTo: true)
        .snapshots()
        .listen((snapshot) {
      _attendees = snapshot.docs.length;
      notifyListeners();
    });
    // To here

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loginState = ApplicationLoginState.loggedIn;
        _guestBookSubscription = FirebaseFirestore.instance
            .collection('guestbook')
            .orderBy('timestamp', descending: true)
            .snapshots()
            .listen((snapshot) {
          _guestBookMessages = [];
          for (final document in snapshot.docs) {
            _guestBookMessages.add(
              GuestBookMessage(
                name: document.data()['name'] as String,
                message: document.data()['text'] as String,
              ),
            );
          }
          notifyListeners();
        });
        // Add from here
        _attendingSubscription = FirebaseFirestore.instance
            .collection('attendees')
            .doc(user.uid)
            .snapshots()
            .listen((snapshot) {
          if (snapshot.data() != null) {
            if (snapshot.data()!['attending'] as bool) {
              _attending = Attending.yes;
            } else {
              _attending = Attending.no;
            }
          } else {
            _attending = Attending.unknown;
          }
          notifyListeners();
        });
        // to here
      } else {
        _loginState = ApplicationLoginState.loggedOut;
        _guestBookMessages = [];
        _guestBookSubscription?.cancel();
        _attendingSubscription?.cancel(); // new
      }
      notifyListeners();
    });
  }

Das obige fügt eine immer abonnierte Abfrage hinzu, um die Anzahl der Teilnehmer herauszufinden, und eine zweite Abfrage, die nur aktiv ist, während ein Benutzer angemeldet ist, um herauszufinden, ob der Benutzer teilnimmt. Fügen Sie als Nächstes die folgende Aufzählung nach der GuestBookMessage Deklaration hinzu:

lib/main.dart

enum Attending { yes, no, unknown }

Sie werden jetzt ein neues Widget definieren, das sich wie die alten Optionsfelder verhält. Es beginnt in einem unbestimmten Zustand, in dem weder „Ja“ noch „Nein“ ausgewählt ist, aber sobald der Benutzer auswählt, ob er teilnimmt oder nicht, wird diese Option mit einer gefüllten Schaltfläche hervorgehoben und die andere Option mit einem flachen Rendering zurückgezogen.

lib/main.dart

class YesNoSelection extends StatelessWidget {
  const YesNoSelection({required this.state, required this.onSelection});
  final Attending state;
  final void Function(Attending selection) onSelection;

  @override
  Widget build(BuildContext context) {
    switch (state) {
      case Attending.yes:
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            children: [
              ElevatedButton(
                style: ElevatedButton.styleFrom(elevation: 0),
                onPressed: () => onSelection(Attending.yes),
                child: const Text('YES'),
              ),
              const SizedBox(width: 8),
              TextButton(
                onPressed: () => onSelection(Attending.no),
                child: const Text('NO'),
              ),
            ],
          ),
        );
      case Attending.no:
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            children: [
              TextButton(
                onPressed: () => onSelection(Attending.yes),
                child: const Text('YES'),
              ),
              const SizedBox(width: 8),
              ElevatedButton(
                style: ElevatedButton.styleFrom(elevation: 0),
                onPressed: () => onSelection(Attending.no),
                child: const Text('NO'),
              ),
            ],
          ),
        );
      default:
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            children: [
              StyledButton(
                onPressed: () => onSelection(Attending.yes),
                child: const Text('YES'),
              ),
              const SizedBox(width: 8),
              StyledButton(
                onPressed: () => onSelection(Attending.no),
                child: const Text('NO'),
              ),
            ],
          ),
        );
    }
  }
}

Als Nächstes müssen Sie die Erstellungsmethode von HomePage aktualisieren, um YesNoSelection zu nutzen, sodass ein angemeldeter Benutzer nominieren kann, ob er teilnimmt. Außerdem wird die Anzahl der Teilnehmer für diese Veranstaltung angezeigt.

lib/main.dart

Consumer<ApplicationState>(
  builder: (context, appState, _) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      // Add from here
      if (appState.attendees >= 2)
        Paragraph('${appState.attendees} people going')
      else if (appState.attendees == 1)
        const Paragraph('1 person going')
      else
        const Paragraph('No one going'),
      // To here.
      if (appState.loginState == ApplicationLoginState.loggedIn) ...[
        // Add from here
        YesNoSelection(
          state: appState.attending,
          onSelection: (attending) => appState.attending = attending,
        ),
        // To here.
        const Header('Discussion'),
        GuestBook(
          addMessage: (message) =>
              appState.addMessageToGuestBook(message),
          messages: appState.guestBookMessages,
        ),
      ],
    ],
  ),
),

Regeln hinzufügen

Da Sie bereits einige Regeln eingerichtet haben, werden die neuen Daten, die Sie mit den Schaltflächen hinzufügen, abgelehnt. Sie müssen die Regeln aktualisieren, um das Hinzufügen zur attendees zuzulassen.

Da Sie für die Sammlung der attendees die Authentifizierungs-UID als Dokumentnamen verwendet haben, können Sie sie abrufen und überprüfen, ob die UID des Einreichers uid ist wie das Dokument, das er schreibt. Sie erlauben jedem, die Teilnehmerliste zu lesen (da dort keine privaten Daten vorhanden sind), aber nur der Ersteller sollte in der Lage sein, sie zu aktualisieren.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // ... //
    match /attendees/{userId} {
      allow read: if true;
      allow write: if request.auth.uid == userId;
    }
  }
}

Validierungsregeln hinzufügen

Fügen Sie eine Datenvalidierung hinzu, um sicherzustellen, dass alle erwarteten Felder im Dokument vorhanden sind:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // ... //
    match /attendees/{userId} {
      allow read: if true;
      allow write: if request.auth.uid == userId
          && "attending" in request.resource.data;

    }
  }
}

(Optional) Sie können jetzt die Ergebnisse des Klickens auf die Schaltflächen anzeigen. Gehen Sie in der Firebase-Konsole zu Ihrem Cloud Firestore-Dashboard.

App-Vorschau

10. Herzlichen Glückwunsch!

Sie haben mit Firebase eine interaktive Echtzeit-Webanwendung erstellt!

Was wir abgedeckt haben

  • Firebase-Authentifizierung
  • Cloud-Firestore
  • Firebase-Sicherheitsregeln

Nächste Schritte

  • Möchten Sie mehr über andere Firebase-Produkte erfahren? Vielleicht möchten Sie Bilddateien speichern, die Benutzer hochladen? Oder Benachrichtigungen an Ihre Benutzer senden? Sehen Sie sich die Firebase-Dokumentation an. Möchten Sie mehr über Flutter-Plugins für Firebase erfahren? Weitere Informationen finden Sie unter FlutterFire .
  • Möchten Sie mehr über Cloud Firestore erfahren? Vielleicht möchten Sie mehr über Untersammlungen und Transaktionen erfahren? Besuchen Sie das Web-Codelab von Cloud Firestore für ein Codelab, das sich eingehender mit Cloud Firestore befasst. Oder sehen Sie sich diese YouTube-Serie an, um Cloud Firestore kennenzulernen !

Mehr erfahren

Wie ist es gelaufen?

Wir würden uns über Ihr Feedback freuen! Bitte füllen Sie hier ein (sehr) kurzes Formular aus .