Lernen Sie Firebase für Flutter kennen

1. Bevor Sie beginnen

In diesem Codelab lernen Sie einige der Grundlagen von Firebase zum Erstellen mobiler Flutter-Apps für Android und iOS.

Voraussetzungen

Was Sie lernen werden

  • So erstellen Sie mit Flutter eine Event-RSVP- und Gästebuch-Chat-App auf Android, iOS, im Web und macOS.
  • So authentifizieren Sie Benutzer mit der Firebase-Authentifizierung und synchronisieren Daten mit Firestore.

Der Startbildschirm der App auf Android

Der Startbildschirm der App auf iOS

Was du brauchen wirst

Eines der folgenden Geräte:

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

Sie benötigen außerdem Folgendes:

  • Ein Browser Ihrer Wahl, z. B. Google Chrome.
  • Eine IDE oder ein Texteditor Ihrer Wahl, konfiguriert mit den Dart- und Flutter-Plugins, z. B. Android Studio oder Visual Studio Code .
  • Die neueste stable Version von Flutter oder beta , wenn Sie gerne am Rande leben.
  • Ein Google-Konto für die Erstellung und Verwaltung Ihres Firebase-Projekts.
  • Die Firebase CLI meldet sich bei Ihrem Google-Konto an.

2. Holen Sie sich den Beispielcode

Laden Sie die erste Version Ihres Projekts von GitHub herunter:

  1. Klonen Sie über die Befehlszeile das GitHub-Repository im Verzeichnis flutter-codelabs :
git clone https://github.com/flutter/codelabs.git flutter-codelabs

Das Verzeichnis flutter-codelabs enthält den Code für eine Sammlung von Codelabs. Der Code für dieses Codelab befindet sich im Verzeichnis flutter-codelabs/firebase-get-to-know-flutter . Das Verzeichnis enthält eine Reihe von Schnappschüssen, die zeigen, wie Ihr Projekt am Ende jedes Schritts aussehen sollte. Sie befinden sich beispielsweise im zweiten Schritt.

  1. Finden Sie die passenden Dateien für den zweiten Schritt:
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 soll, schauen Sie in dem Verzeichnis nach, das nach dem Schritt benannt ist, an dem Sie interessiert sind.

Importieren Sie die Starter-App

  • Öffnen oder importieren Sie das Verzeichnis flutter-codelabs/firebase-get-to-know-flutter/step_02 in Ihrer bevorzugten IDE. Dieses Verzeichnis enthält den Startercode für das Codelab, das aus einer noch nicht funktionsfähigen Flutter-Meetup-App besteht.

Suchen Sie die Dateien, die bearbeitet werden müssen

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

  • Suchen Sie die folgenden Dateien:
    • lib/main.dart : Diese Datei enthält den Haupteinstiegspunkt und das App-Widget.
    • lib/home_page.dart : Diese Datei enthält das Homepage-Widget.
    • lib/src/widgets.dart : Diese Datei enthält eine Handvoll Widgets, die dabei helfen, den Stil der App zu standardisieren. Sie bilden den Bildschirm der Starter-App.
    • lib/src/authentication.dart : Diese Datei enthält eine teilweise Implementierung der Authentifizierung mit einer Reihe von Widgets, um eine Login-Benutzererfahrung für die E-Mail-basierte Firebase-Authentifizierung zu erstellen. Diese Widgets für den Authentifizierungsfluss werden in der Starter-App noch nicht verwendet, aber Sie fügen sie bald hinzu.

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

Überprüfen Sie die Datei lib/main.dart

Diese App nutzt das google_fonts Paket, um Roboto zur Standardschriftart in der gesamten App zu machen. Sie können „fonts.google.com“ durchsuchen und die Schriftarten, die Sie dort entdecken, in verschiedenen Teilen der App verwenden.

Sie verwenden die Hilfs-Widgets aus der Datei lib/src/widgets.dart in Form von Header , Paragraph und IconAndDetail . Diese Widgets eliminieren doppelten Code, um das in HomePage beschriebene Seitenlayout übersichtlicher zu gestalten. Dies ermöglicht auch ein einheitliches Erscheinungsbild.

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

Der Startbildschirm der App auf Android

Der Startbildschirm der App auf iOS

Der Startbildschirm der App im Web

Der Startbildschirm der App unter macOS

3. Erstellen und konfigurieren Sie ein Firebase-Projekt

Die Anzeige von Veranstaltungsinformationen ist für Ihre Gäste großartig, aber für niemanden allein ist sie nicht sehr nützlich. Sie müssen der App einige dynamische Funktionen hinzufügen. Dazu müssen Sie Firebase mit Ihrer App verbinden. Um mit Firebase zu beginnen, müssen Sie ein Firebase-Projekt erstellen und konfigurieren.

Erstellen Sie ein Firebase-Projekt

  1. Melden Sie sich bei Firebase an.
  2. Klicken Sie in der Konsole auf Projekt hinzufügen oder Projekt erstellen .
  3. Geben Sie im Feld „Projektname“ „Firebase-Flutter-Codelab“ ein und klicken Sie dann auf „Weiter“ .

4395e4e67c08043a.png

  1. Klicken Sie sich durch die Projekterstellungsoptionen. Wenn Sie dazu aufgefordert werden, akzeptieren Sie die Firebase-Bedingungen, überspringen Sie jedoch die Einrichtung von Google Analytics, da Sie es für diese App nicht verwenden werden.

b7138cde5f2c7b61.png

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

Die App nutzt die folgenden Firebase-Produkte, die für Web-Apps verfügbar sind:

  • Authentifizierung: Ermöglicht Benutzern die Anmeldung bei Ihrer App.
  • Firestore: Speichert strukturierte Daten in der Cloud und erhält sofortige Benachrichtigungen, wenn sich Daten ändern.
  • Firebase-Sicherheitsregeln: Sichert Ihre Datenbank.

Einige dieser Produkte erfordern eine spezielle Konfiguration oder Sie müssen sie in der Firebase-Konsole aktivieren.

Aktivieren Sie die E-Mail-Anmeldeauthentifizierung

  1. Erweitern Sie im Projektübersichtsbereich der Firebase-Konsole das Menü „Erstellen“ .
  2. Klicken Sie auf Authentifizierung > Erste Schritte > Anmeldemethode > E-Mail/Passwort > Aktivieren > Speichern .

58e3e3e23c2f16a4.png

Aktivieren Sie Firestore

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

Firestore aktivieren:

  • Klicken Sie im Menü „Erstellen“ auf Firestore-Datenbank > Datenbank erstellen .

99e8429832d23fa3.png

  1. Wählen Sie „Im Testmodus starten“ und lesen Sie dann den Haftungsausschluss zu den Sicherheitsregeln. Der Testmodus stellt sicher, dass Sie während der Entwicklung frei in die Datenbank schreiben können.

6be00e26c72ea032.png

  1. Klicken Sie auf Weiter und wählen Sie dann den Speicherort für Ihre Datenbank aus. Sie können die Standardeinstellung verwenden. Sie können den Standort später nicht mehr ändern.

278656eefcfb0216.png

  1. Klicken Sie auf Aktivieren .

4. Konfigurieren Sie Firebase

Um Firebase mit Flutter zu verwenden, müssen Sie die folgenden Aufgaben ausführen, um das Flutter-Projekt für die korrekte Verwendung der FlutterFire Bibliotheken zu konfigurieren:

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

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

Abhängigkeiten konfigurieren

Sie müssen die FlutterFire Bibliotheken für die beiden Firebase-Produkte hinzufügen, die Sie in dieser App verwenden: Authentifizierung und Firestore.

  • Fügen Sie über die Befehlszeile die folgenden Abhängigkeiten hinzu:
$ flutter pub add firebase_core

Das Paket firebase_core ist der gemeinsame Code, der für alle Firebase Flutter-Plugins erforderlich ist.

$ flutter pub add firebase_auth

Das Paket firebase_auth ermöglicht die Integration mit der Authentifizierung.

$ flutter pub add cloud_firestore

Das Paket cloud_firestore ermöglicht den Zugriff auf den Firestore-Datenspeicher.

$ flutter pub add provider

Das Paket firebase_ui_auth stellt eine Reihe von Widgets und Dienstprogrammen bereit, um die Entwicklergeschwindigkeit mit Authentifizierungsabläufen zu erhöhen.

$ flutter pub add firebase_ui_auth

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

Installieren Sie die FlutterFire-CLI

Die FlutterFire-CLI hängt von der zugrunde liegenden Firebase-CLI ab.

  1. Falls Sie dies noch nicht getan haben, installieren Sie die Firebase-CLI auf Ihrem Computer.
  2. Installieren Sie die FlutterFire-CLI:
$ dart pub global activate flutterfire_cli

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

Konfigurieren Sie Ihre Apps

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

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

$ flutterfire configure

Der Konfigurationsbefehl führt Sie durch die folgenden Prozesse:

  1. Wählen Sie ein Firebase-Projekt basierend auf der .firebaserc Datei oder aus der Firebase-Konsole aus.
  2. Bestimmen Sie Plattformen für die Konfiguration, z. B. Android, iOS, macOS und Web.
  3. Identifizieren Sie die Firebase-Apps, aus denen die Konfiguration extrahiert werden soll. Standardmäßig versucht die CLI, Firebase-Apps basierend auf Ihrer aktuellen Projektkonfiguration automatisch abzugleichen.
  4. Generieren Sie eine Datei firebase_options.dart in Ihrem Projekt.

Konfigurieren Sie macOS

Flutter unter macOS erstellt vollständig Sandbox-Apps. Da diese App in das Netzwerk integriert wird, um mit den Firebase-Servern zu kommunizieren, müssen Sie Ihre App 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 Informationen finden Sie unter Desktop-Unterstützung für Flutter .

5. Fügen Sie die RSVP-Funktionalität hinzu

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

Das Projekt, das Sie zuvor abgerufen haben, enthielt eine Reihe von Widgets, die die Benutzeroberfläche für den größten Teil des Authentifizierungsablaufs implementieren. Sie implementieren die Geschäftslogik, um die Authentifizierung in die App zu integrieren.

Fügen Sie Geschäftslogik mit dem Provider -Paket hinzu

Verwenden Sie das provider , um ein zentralisiertes App-Statusobjekt im gesamten Flutter-Widget-Baum der App verfügbar zu machen:

  1. Erstellen Sie eine neue Datei namens app_state.dart mit folgendem Inhalt:

lib/app_state.dart

import 'package:firebase_auth/firebase_auth.dart'
    hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'firebase_options.dart';

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

  bool _loggedIn = false;
  bool get loggedIn => _loggedIn;

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

    FirebaseUIAuth.configureProviders([
      EmailAuthProvider(),
    ]);

    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loggedIn = true;
      } else {
        _loggedIn = false;
      }
      notifyListeners();
    });
  }
}

Die import führen Firebase Core und Auth ein, ziehen das provider ein, das das App-Statusobjekt im gesamten Widget-Baum verfügbar macht, und schließen die Authentifizierungs-Widgets aus dem Paket firebase_ui_auth ein.

Dieses ApplicationState Anwendungsstatusobjekt hat für diesen Schritt eine Hauptaufgabe: Es soll den Widget-Baum darüber informieren, dass eine Aktualisierung auf einen authentifizierten Status stattgefunden hat.

Sie verwenden einen Anbieter lediglich, um der App den Status des Anmeldestatus eines Benutzers mitzuteilen. Um einem Benutzer die Anmeldung zu ermöglichen, verwenden Sie die vom Paket firebase_ui_auth bereitgestellten Benutzeroberflächen. Dies ist eine hervorragende Möglichkeit, Anmeldebildschirme in Ihren Apps schnell zu booten.

Integrieren Sie den Authentifizierungsfluss

  1. Ändern Sie die Importe oben in der Datei lib/main.dart :

lib/main.dart

import 'package:firebase_ui_auth/firebase_ui_auth.dart'; // new
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';               // new
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';                 // new

import 'app_state.dart';                                 // new
import 'home_page.dart';
  1. Verbinden Sie den App-Status mit der App-Initialisierung und fügen Sie dann den Authentifizierungsfluss zu HomePage hinzu:

lib/main.dart

void main() {
  // Modify from here...
  WidgetsFlutterBinding.ensureInitialized();

  runApp(ChangeNotifierProvider(
    create: (context) => ApplicationState(),
    builder: ((context, child) => const App()),
  ));
  // ...to here.
}

Durch die Änderung der Funktion main() ist das Anbieterpaket für die Instanziierung des App-Statusobjekts mit dem ChangeNotifierProvider -Widget verantwortlich. Sie verwenden diese spezielle provider , da das App-Statusobjekt die ChangeNotifier Klasse erweitert, die dem provider mitteilt, wann abhängige Widgets erneut angezeigt werden müssen.

  1. Aktualisieren Sie Ihre App, um die Navigation zu verschiedenen Bildschirmen zu ermöglichen, die FirebaseUI für Sie bereitstellt, indem Sie eine GoRouter Konfiguration erstellen:

lib/main.dart

// Add GoRouter configuration outside the App class
final _router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => const HomePage(),
      routes: [
        GoRoute(
          path: 'sign-in',
          builder: (context, state) {
            return SignInScreen(
              actions: [
                ForgotPasswordAction(((context, email) {
                  final uri = Uri(
                    path: '/sign-in/forgot-password',
                    queryParameters: <String, String?>{
                      'email': email,
                    },
                  );
                  context.push(uri.toString());
                })),
                AuthStateChangeAction(((context, state) {
                  final user = switch (state) {
                    SignedIn state => state.user,
                    UserCreated state => state.credential.user,
                    _ => null
                  };
                  if (user == null) {
                    return;
                  }
                  if (state is UserCreated) {
                    user.updateDisplayName(user.email!.split('@')[0]);
                  }
                  if (!user.emailVerified) {
                    user.sendEmailVerification();
                    const snackBar = SnackBar(
                        content: Text(
                            'Please check your email to verify your email address'));
                    ScaffoldMessenger.of(context).showSnackBar(snackBar);
                  }
                  context.pushReplacement('/');
                })),
              ],
            );
          },
          routes: [
            GoRoute(
              path: 'forgot-password',
              builder: (context, state) {
                final arguments = state.uri.queryParameters;
                return ForgotPasswordScreen(
                  email: arguments['email'],
                  headerMaxExtent: 200,
                );
              },
            ),
          ],
        ),
        GoRoute(
          path: 'profile',
          builder: (context, state) {
            return ProfileScreen(
              providers: const [],
              actions: [
                SignedOutAction((context) {
                  context.pushReplacement('/');
                }),
              ],
            );
          },
        ),
      ],
    ),
  ],
);
// end of GoRouter configuration

// Change MaterialApp to MaterialApp.router and add the routerConfig
class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Firebase Meetup',
      theme: ThemeData(
        buttonTheme: Theme.of(context).buttonTheme.copyWith(
              highlightColor: Colors.deepPurple,
            ),
        primarySwatch: Colors.deepPurple,
        textTheme: GoogleFonts.robotoTextTheme(
          Theme.of(context).textTheme,
        ),
        visualDensity: VisualDensity.adaptivePlatformDensity,
        useMaterial3: true,
      ),
      routerConfig: _router, // new
    );
  }
}

Jedem Bildschirm ist basierend auf dem neuen Status des Authentifizierungsablaufs eine andere Art von Aktion zugeordnet. Nach den meisten Statusänderungen bei der Authentifizierung können Sie zu einem bevorzugten Bildschirm zurückkehren, sei es der Startbildschirm oder ein anderer Bildschirm, z. B. das Profil.

  1. Integrieren Sie in der Build-Methode der HomePage Klasse den App-Status mit dem AuthFunc -Widget:

lib/home_page.dart

import 'package:firebase_auth/firebase_auth.dart' // new
    hide EmailAuthProvider, PhoneAuthProvider;    // new
import 'package:flutter/material.dart';           // new
import 'package:provider/provider.dart';          // new

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

class HomePage extends StatelessWidget {
  const HomePage({super.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, _) => AuthFunc(
                loggedIn: appState.loggedIn,
                signOut: () {
                  FirebaseAuth.instance.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 AuthFunc Widget und binden es in ein Consumer Widget ein. Das Consumer-Widget ist die übliche Möglichkeit, mit dem provider einen Teil des Baums neu zu erstellen, wenn sich der App-Status ändert. Das AuthFunc Widget ist das zusätzliche Widget, das Sie testen.

Testen Sie den Authentifizierungsablauf

cdf2d25e436bd48d.png

  1. Tippen Sie in der App auf die RSVP- Schaltfläche, um den SignInScreen zu starten.

2a2cd6d69d172369.png

  1. Gib eine E-mail Adresse ein. Wenn Sie bereits registriert sind, werden Sie vom System aufgefordert, ein Passwort einzugeben. Andernfalls werden Sie vom System aufgefordert, das Registrierungsformular auszufüllen.

e5e65065dba36b54.png

  1. Geben Sie ein Passwort mit weniger als sechs Zeichen ein, um den Fehlerbehandlungsablauf zu überprüfen. Wenn Sie registriert sind, sehen Sie stattdessen das Passwort für.
  2. Geben Sie falsche Passwörter ein, um den Fehlerbehandlungsablauf zu überprüfen.
  3. Geben Sie das richtige Passwort ein. Sie sehen die Anmeldeerfahrung, die dem Benutzer die Möglichkeit bietet, sich abzumelden.

4ed811a25b0cf816.png

6. Schreiben Sie Nachrichten an Firestore

Es ist schön zu wissen, dass Benutzer kommen, aber Sie müssen den Gästen in der App etwas anderes zu tun geben. Was wäre, wenn sie Nachrichten in einem Gästebuch hinterlassen könnten? Sie können mitteilen, warum sie sich auf den Besuch freuen oder wen sie kennenzulernen hoffen.

Um die Chatnachrichten, die Benutzer in der App schreiben, zu speichern, verwenden Sie Firestore .

Datenmodell

Firestore ist eine NoSQL-Datenbank und die in der Datenbank gespeicherten Daten sind in Sammlungen, Dokumente, Felder und Untersammlungen aufgeteilt. Sie speichern jede Nachricht des Chats als Dokument in einer guestbook , einer Sammlung auf oberster Ebene.

7c20dc8424bb1d84.png

Fügen Sie Nachrichten zum Firestore hinzu

In diesem Abschnitt fügen Sie die Funktionalität hinzu, mit der Benutzer Nachrichten in die Datenbank schreiben können. Zuerst fügen Sie ein Formularfeld und eine Schaltfläche zum Senden hinzu und dann fügen Sie den Code hinzu, der diese Elemente mit der Datenbank verbindet.

  1. Erstellen Sie eine neue Datei mit dem Namen „ guest_book.dart und fügen Sie ein zustandsbehaftetes GuestBook Widget hinzu, um die UI-Elemente eines Nachrichtenfelds und einer Schaltfläche „Senden“ zu erstellen:

lib/guest_book.dart

import 'dart:async';

import 'package:flutter/material.dart';

import 'src/widgets.dart';

class GuestBook extends StatefulWidget {
  const GuestBook({required this.addMessage, super.key});

  final FutureOr<void> Function(String message) addMessage;

  @override
  State<GuestBook> 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 einige interessante Punkte. Zuerst instanziieren Sie ein Formular, damit Sie überprüfen können, ob die Nachricht tatsächlich Inhalt enthält, und dem Benutzer eine Fehlermeldung anzeigen können, wenn keiner vorhanden ist. Um ein Formular zu validieren, greifen Sie mit einem GlobalKey auf den Formularstatus hinter dem Formular zu. Weitere Informationen zu Schlüsseln und deren Verwendung finden Sie unter Verwendung von Schlüsseln .

Beachten Sie auch die Art und Weise, wie die Widgets angeordnet sind. Sie haben eine Row mit einem TextFormField und einem StyledButton , der eine Row enthält. Beachten Sie außerdem, dass das TextFormField in ein Expanded Widget eingeschlossen ist, wodurch das TextFormField gezwungen wird, jeden zusätzlichen Platz in der Zeile auszufüllen. Um besser zu verstehen, warum dies erforderlich ist, lesen Sie Erläuterungen zu Einschränkungen .

Da Sie nun über ein Widget verfügen, mit dem der Benutzer Text zum Hinzufügen zum Gästebuch eingeben kann, müssen Sie ihn auf dem Bildschirm anzeigen.

  1. Bearbeiten Sie den Hauptteil von HomePage , um die folgenden zwei Zeilen am Ende der untergeordneten ListView Elemente hinzuzufügen:
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, reicht jedoch nicht aus, um etwas Nützliches zu tun. Sie aktualisieren diesen Code in Kürze, damit er funktionsfähig ist.

App-Vorschau

Der Startbildschirm der App auf Android mit Chat-Integration

Der Startbildschirm der App auf iOS mit Chat-Integration

Der Startbildschirm der App im Web mit Chat-Integration

Der Startbildschirm der App auf macOS mit Chat-Integration

Wenn ein Benutzer auf SENDEN klickt, wird das folgende Code-Snippet ausgelöst. Es fügt den Inhalt des Nachrichteneingabefelds zur guestbook der Datenbank hinzu. Konkret fügt die Methode addMessageToGuestBook den Nachrichteninhalt einem neuen Dokument mit einer automatisch generierten ID in der guestbook hinzu.

Beachten Sie, dass FirebaseAuth.instance.currentUser.uid ein Verweis auf die automatisch generierte eindeutige ID ist, die die Authentifizierung allen angemeldeten Benutzern vergibt.

  • Fügen Sie in der Datei lib/app_state.dart die Methode addMessageToGuestBook hinzu. Diese Fähigkeit verknüpfen Sie im nächsten Schritt mit der Benutzeroberfläche.

lib/app_state.dart

import 'package:cloud_firestore/cloud_firestore.dart'; // new
import 'package:firebase_auth/firebase_auth.dart'
    hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'firebase_options.dart';

class ApplicationState extends ChangeNotifier {

  // Current content of ApplicationState elided ...

  // Add from here...
  Future<DocumentReference> addMessageToGuestBook(String message) {
    if (!_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 Sie UI und Datenbank

Sie haben eine Benutzeroberfläche, in der der Benutzer den Text eingeben kann, den er dem Gästebuch hinzufügen möchte, und Sie haben den Code, um den Eintrag zu Firestore hinzuzufügen. Jetzt müssen Sie nur noch die beiden verbinden.

  • Nehmen Sie in der Datei lib/home_page.dart die folgende Änderung am HomePage Widget vor:

lib/home_page.dart

import 'package:firebase_auth/firebase_auth.dart'
    hide EmailAuthProvider, PhoneAuthProvider;
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

import 'app_state.dart';
import 'guest_book.dart';                         // new
import 'src/authentication.dart';
import 'src/widgets.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, _) => AuthFunc(
                loggedIn: appState.loggedIn,
                signOut: () {
                  FirebaseAuth.instance.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.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 erneut Consumer<ApplicationState> , um den App-Status für den Teil des Baums verfügbar zu machen, den Sie rendern. Dadurch 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 Versenden von Nachrichten

  1. Melden Sie sich bei Bedarf bei der App an.
  2. Geben Sie eine Nachricht ein, z. B. Hey there! , und klicken Sie dann auf SENDEN .

Diese Aktion schreibt die Nachricht in Ihre Firestore-Datenbank. Allerdings wird die Meldung in Ihrer eigentlichen Flutter-App nicht angezeigt, da Sie den Abruf der Daten noch implementieren müssen, was Sie im nächsten Schritt tun. Im Datenbank- Dashboard der Firebase-Konsole können Sie jedoch Ihre hinzugefügte Nachricht in der guestbook sehen. Wenn Sie mehr Nachrichten senden, fügen Sie Ihrer guestbook weitere Dokumente hinzu. Sehen Sie sich beispielsweise den folgenden Codeausschnitt an:

713870af0b3b63c.png

7. Nachrichten lesen

Es ist schön, dass Gäste Nachrichten in die Datenbank schreiben können, diese aber noch nicht in der App sehen können. Zeit, das zu beheben!

Nachrichten synchronisieren

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

  1. Erstellen Sie eine neue Datei guest_book_message.dart und fügen Sie die folgende Klasse hinzu, um eine strukturierte Ansicht der Daten bereitzustellen, die Sie in Firestore speichern.

lib/guest_book_message.dart

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

  final String name;
  final String message;
}
  1. Fügen Sie in der Datei lib/app_state.dart die folgenden Importe hinzu:

lib/app_state.dart

import 'dart:async';                                     // new

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart'
    hide EmailAuthProvider, PhoneAuthProvider;
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'firebase_options.dart';
import 'guest_book_message.dart';                        // new
  1. Fügen Sie im Abschnitt ApplicationState , in dem Sie Status und Getter definieren, die folgenden Zeilen hinzu:

lib/app_state.dart

  bool _loggedIn = false;
  bool get loggedIn => _loggedIn;

  // Add from here...
  StreamSubscription<QuerySnapshot>? _guestBookSubscription;
  List<GuestBookMessage> _guestBookMessages = [];
  List<GuestBookMessage> get guestBookMessages => _guestBookMessages;
  // ...to here.
  1. Fügen Sie im Initialisierungsabschnitt von ApplicationState die folgenden Zeilen hinzu, um eine Abfrage über die Dokumentensammlung zu abonnieren, wenn sich ein Benutzer anmeldet, und um das Abonnement abzumelden, wenn er sich abmeldet:

lib/app_state.dart

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

    FirebaseUIAuth.configureProviders([
      EmailAuthProvider(),
    ]);
    
    FirebaseAuth.instance.userChanges().listen((user) {
      if (user != null) {
        _loggedIn = true;
        _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();
        });
      } else {
        _loggedIn = false;
        _guestBookMessages = [];
        _guestBookSubscription?.cancel();
      }
      notifyListeners();
    });
  }

Dieser Abschnitt ist wichtig, da Sie hier eine Abfrage für die guestbook erstellen und das Abonnieren und Abbestellen dieser Sammlung verwalten. Sie hören sich den Stream an, rekonstruieren dabei einen lokalen Cache der Nachrichten in der guestbook und speichern außerdem einen Verweis auf dieses Abonnement, damit Sie es später wieder abbestellen können. Hier ist viel los, daher sollten Sie es in einem Debugger erkunden, um zu untersuchen, was passiert, um ein klareres mentales Modell zu erhalten. Weitere Informationen finden Sie unter Echtzeit-Updates mit Firestore erhalten .

  1. Fügen Sie in der Datei lib/guest_book.dart den folgenden Import hinzu:
import 'guest_book_message.dart';
  1. Fügen Sie im GuestBook Widget als Teil der Konfiguration eine Liste von Nachrichten hinzu, um diesen sich ändernden Status mit der Benutzeroberfläche zu verbinden:

lib/guest_book.dart

class GuestBook extends StatefulWidget {
  // Modify the following line:
  const GuestBook({
    super.key, 
    required this.addMessage, 
    required this.messages,
  });

  final FutureOr<void> Function(String message) addMessage;
  final List<GuestBookMessage> messages; // new

  @override
  _GuestBookState createState() => _GuestBookState();
}
  1. Ändern Sie in _GuestBookState die build Methode wie folgt, um diese Konfiguration verfügbar zu machen:

lib/guest_book.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 der Column eine Sammlung für hinzu, um für jede Nachricht in der Nachrichtenliste einen neuen Paragraph zu generieren.

  1. Aktualisieren Sie den Hauptteil von HomePage , um GuestBook mit dem neuen messages korrekt zu erstellen:

lib/home_page.dart

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

Testen Sie die Nachrichtensynchronisierung

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

Nachrichtensynchronisierung testen:

  1. Suchen Sie in der App nach den Nachrichten, die Sie zuvor in der Datenbank erstellt haben.
  2. Schreiben Sie neue Nachrichten. Sie erscheinen sofort.
  3. Öffnen Sie Ihren Arbeitsbereich in mehreren Fenstern oder Registerkarten. Die Nachrichten werden in Echtzeit über alle Fenster und Registerkarten hinweg synchronisiert.
  4. Optional: Im Menü „Datenbank“ der Firebase-Konsole können Sie Nachrichten manuell löschen, ändern oder neue hinzufügen. Alle Änderungen werden in der Benutzeroberfläche angezeigt.

Glückwunsch! Sie lesen Firestore-Dokumente in Ihrer App!

App-Vorschau

Der Startbildschirm der App auf Android mit Chat-Integration

Der Startbildschirm der App auf iOS mit Chat-Integration

Der Startbildschirm der App im Web mit Chat-Integration

Der Startbildschirm der App auf macOS mit Chat-Integration

8. Richten Sie grundlegende Sicherheitsregeln ein

Sie richten Firestore zunächst für die Verwendung des Testmodus ein, was bedeutet, dass Ihre Datenbank für Lese- und Schreibvorgänge geöffnet ist. Sie sollten den Testmodus jedoch nur in frühen Entwicklungsstadien verwenden. Als Best Practice sollten Sie beim Entwickeln Ihrer App Sicherheitsregeln für Ihre Datenbank einrichten. Sicherheit ist ein wesentlicher Bestandteil der Struktur und des Verhaltens Ihrer App.

Mit Firebase-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 der gesamten Datenbank bis hin zu Vorgängen an einem bestimmten Dokument.

Legen Sie grundlegende Sicherheitsregeln fest:

  1. Klicken Sie im Menü „Entwickeln“ der Firebase-Konsole auf Datenbank > Regeln . Sie sollten die folgenden Standardsicherheitsregeln und eine Warnung sehen, dass die Regeln öffentlich sind:

7767a2d2e64e7275.png

  1. Identifizieren Sie 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.
  }
}

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.

  1. Fügen Sie die Lese- und Schreibregeln 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;
    }
  }
}

Jetzt können nur angemeldete Benutzer Nachrichten im Gästebuch lesen, aber nur der Autor einer Nachricht kann eine Nachricht bearbeiten.

  1. 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 das Chatten, wenn sie an der Veranstaltung interessiert sind. Außerdem wissen Sie nur, ob jemand kommt, wenn er es im Chat sagt.

In diesem Schritt organisieren Sie sich und teilen den Leuten mit, wie viele Leute kommen. Sie fügen dem App-Status einige Funktionen hinzu. Die erste besteht darin, dass ein angemeldeter Benutzer angeben kann, ob er teilnehmen möchte. Der zweite ist ein Zähler dafür, wie viele Personen anwesend sind.

  1. Fügen Sie in der Datei lib/app_state.dart die folgenden Zeilen zum Accessors-Abschnitt des ApplicationState hinzu, damit der UI-Code mit diesem Status interagieren kann:

lib/app_state.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});
  }
}
  1. Aktualisieren Sie die init() Methode von ApplicationState wie folgt:

lib/app_state.dart

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

    FirebaseUIAuth.configureProviders([
      EmailAuthProvider(),
    ]);

    // 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) {
        _loggedIn = true;
        _emailVerified = user.emailVerified;
        _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 {
        _loggedIn = false;
        _emailVerified = false;
        _guestBookMessages = [];
        _guestBookSubscription?.cancel();
        _attendingSubscription?.cancel(); // new
      }
      notifyListeners();
    });
  }

Dieser Code fügt eine immer abonnierte Abfrage hinzu, um die Anzahl der Teilnehmer zu ermitteln, und eine zweite Abfrage, die nur aktiv ist, während ein Benutzer angemeldet ist, um festzustellen, ob der Benutzer teilnimmt.

  1. Fügen Sie die folgende Enumeration oben in der Datei lib/app_state.dart hinzu.

lib/app_state.dart

enum Attending { yes, no, unknown }
  1. Erstellen Sie eine neue Datei yes_no_selection.dart und definieren Sie ein neues Widget, das sich wie Optionsfelder verhält:

lib/yes_no_selection.dart

import 'package:flutter/material.dart';

import 'app_state.dart';
import 'src/widgets.dart';

class YesNoSelection extends StatelessWidget {
  const YesNoSelection(
      {super.key, 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: [
              FilledButton(
                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),
              FilledButton(
                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'),
              ),
            ],
          ),
        );
    }
  }
}

Es beginnt in einem unbestimmten Zustand, in dem weder „Ja“ noch „Nein“ ausgewählt ist. Sobald der Benutzer auswählt, ob er teilnehmen möchte, wird diese Option mit einer ausgefüllten Schaltfläche hervorgehoben und die andere Option wird mit einer flachen Darstellung ausgeblendet.

  1. Aktualisieren Sie build() Methode von HomePage , um YesNoSelection zu nutzen, einem angemeldeten Benutzer die Möglichkeit zu geben, zu benennen, ob er teilnimmt, und die Anzahl der Teilnehmer für die Veranstaltung anzuzeigen:

lib/home_page.dart

Consumer<ApplicationState>(
  builder: (context, appState, _) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      // Add from here...
      switch (appState.attendees) {
        1 => const Paragraph('1 person going'),
        >= 2 => Paragraph('${appState.attendees} people going'),
        _ => const Paragraph('No one going'),
      },
      // ...to here.
      if (appState.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

Sie haben bereits einige Regeln eingerichtet, sodass die Daten, die Sie über die Schaltflächen hinzufügen, abgelehnt werden. Sie müssen die Regeln aktualisieren, um Ergänzungen zur attendees zu ermöglichen.

  1. Suchen Sie in der attendees nach der Authentifizierungs-UID, die Sie als Dokumentnamen verwendet haben, und stellen Sie sicher, dass die uid des Einreichers mit der des Dokuments übereinstimmt, das er schreibt:
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // ... //
    match /attendees/{userId} {
      allow read: if true;
      allow write: if request.auth.uid == userId;
    }
  }
}

Dadurch kann jeder die Teilnehmerliste lesen, da dort keine privaten Daten vorhanden sind, sondern nur der Ersteller sie aktualisieren kann.

  1. 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;

    }
  }
}
  1. Optional: Klicken Sie in der App auf Schaltflächen, um die Ergebnisse im Firestore-Dashboard in der Firebase-Konsole anzuzeigen.

App-Vorschau

Der Startbildschirm der App auf Android

Der Startbildschirm der App auf iOS

Der Startbildschirm der App im Web

Der Startbildschirm der App auf macOS

10. Herzlichen Glückwunsch!

Sie haben Firebase verwendet, um eine interaktive Echtzeit-Web-App zu erstellen!

Erfahren Sie mehr