Lernen Sie Firebase für Flutter kennen

Mit Sammlungen den Überblick behalten Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.

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

Was Sie lernen werden

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

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 ( Xcode-Tools erforderlich).
  • Der Android-Emulator (erfordert Einrichtung in Android Studio ).

Sie benötigen außerdem Folgendes:

  • Ein Browser Ihrer Wahl, z. B. Google Chrome.
  • Eine IDE oder einen Texteditor Ihrer Wahl, der mit den Dart- und Flutter-Plug-ins konfiguriert ist, z. B. Android Studio oder Visual Studio Code .
  • Die neueste stable Version von Flutter oder beta , wenn Sie gerne am Limit leben.
  • Ein Google-Konto für die Erstellung und Verwaltung Ihres Firebase-Projekts.
  • Die Firebase CLI hat sich bei Ihrem Google-Konto angemeldet.

2. Holen Sie sich den Beispielcode

Laden Sie die ursprüngliche 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 flutter-codelabs Verzeichnis enthält den Code für eine Sammlung von Codelabs. Der Code für dieses Codelab befindet sich im 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 auf der zweiten Stufe.

  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 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 Ihrer bevorzugten 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, 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/src/widgets.dart : Diese Datei enthält eine Handvoll Widgets, um den Stil der App zu standardisieren. Sie bilden den Bildschirm der Starter-App.
    • lib/src/authentication.dart : Diese Datei enthält eine Teilimplementierung der Authentifizierung mit einer Reihe von Widgets, um eine Anmeldebenutzererfahrung für die E-Mail-basierte Authentifizierung von Firebase zu erstellen. Diese Widgets für den Authentifizierungsablauf 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 Look-and-Feel.

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

3. Erstellen und konfigurieren Sie ein Firebase-Projekt

Die Anzeige von Veranstaltungsinformationen ist großartig für Ihre Gäste, aber für niemanden allein 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-Nutzungsbedingungen, aber überspringen Sie 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 verwendet 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 müssen speziell konfiguriert oder in der Firebase-Konsole aktiviert werden.

Aktivieren Sie die E-Mail-Anmeldeauthentifizierung

  1. Erweitern Sie im Bereich Projektübersicht der Firebase-Konsole das Menü Erstellen .
  2. Klicken Sie auf Authentifizierung > Erste Schritte > Anmeldemethode > E-Mail/Kennwort > Aktivieren > Speichern .

58e3e3e23c2f16a4.png

Firestore aktivieren

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

Firestore aktivieren:

  • Klicken Sie im Menü Erstellen auf Cloud Firestore > 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 Ihrem Projekt die FlutterFire Abhängigkeiten 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 befinden sich 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 firebase_core -Paket ist der gemeinsame Code, der für alle Firebase Flutter-Plug-ins 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 firebase_ui_auth -Paket bietet eine Reihe von Widgets und Dienstprogrammen, um die Entwicklergeschwindigkeit mit Authentifizierungsabläufen zu erhöhen.

$ flutter pub add firebase_ui_auth

Sie haben die erforderlichen Pakete hinzugefügt, aber Sie müssen auch die iOS-, Android-, macOS- und Web-Runner-Projekte konfigurieren, um Firebase ordnungsgemäß zu verwenden. Sie verwenden auch das provider , das die Trennung der Geschäftslogik von der 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 -Befehlszeilenschnittstelle auf Ihrem Computer.
  2. Installieren Sie die FlutterFire-CLI:
$ dart pub global activate flutterfire_cli

Nach der Installation ist der Befehl flutterfire global 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 in Ihrem Projekt eine firebase_options.dart -Datei.

macOS konfigurieren

Flutter auf macOS erstellt vollständig Sandbox-Apps. Da sich diese App in das Netzwerk integriert, 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. RSVP-Funktionalität hinzufügen

Nachdem Sie der App Firebase hinzugefügt haben, können Sie eine RSVP -Schaltfläche erstellen, die Personen mit Authentifizierung registriert. Für Android nativ, iOS nativ und Web gibt es vorgefertigte FirebaseUI Auth -Authentifizierungspakete, aber Sie müssen diese Funktion 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 Authentifizierungsflusses 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. Ändern Sie die Importe oben in der Datei lib/main.dart :

lib/main.dart

import 'dart:async';                                     // new
import 'package:firebase_auth/firebase_auth.dart'        // new
    hide EmailAuthProvider, PhoneAuthProvider;           // new
import 'package:firebase_core/firebase_core.dart';       // new
import 'package:firebase_ui_auth/firebase_ui_auth.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 -Anweisungen führen Firebase Core und Auth ein, rufen das provider ab, das das App-Statusobjekt in der gesamten Widget-Struktur verfügbar macht, und schließen die Authentifizierungs-Widgets aus dem Paket firebase_ui_auth ein.

Dieses ApplicationState -Anwendungsstatusobjekt hat eine Hauptverantwortung für diesen Schritt, nämlich die Widget-Struktur zu warnen, dass eine Aktualisierung auf einen authentifizierten Status stattgefunden hat.

  1. Fügen Sie am Ende der Datei lib/main.dart die folgende Klasse hinzu:

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

Sie verwenden einen Anbieter nur, um den Status des Anmeldestatus eines Benutzers an die App zu übermitteln. Um einen Benutzer sich anmelden zu lassen, verwenden Sie die Benutzeroberflächen, die vom Paket firebase_ui_auth bereitgestellt werden, was eine großartige Möglichkeit ist, Anmeldebildschirme in Ihren Apps schnell zu booten.

Integrieren Sie den Authentifizierungsablauf

  1. Verbinden Sie den App-Status mit der App-Initialisierung und fügen Sie dann den Authentifizierungsablauf 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.
}

Die Änderung an der main() Funktion macht das Anbieterpaket für die Instanziierung des App-Statusobjekts mit dem ChangeNotifierProvider Widget verantwortlich. Sie verwenden diese spezifische provider , da das App-Statusobjekt die ChangeNotifier -Klasse erweitert, die das provider darüber informiert, wann abhängige Widgets erneut angezeigt werden sollen.

  1. Aktualisieren Sie Ihre App, um die Navigation zu verschiedenen Bildschirmen zu handhaben, die FirebaseUI für Sie bereitstellt:

lib/main.dart

class App extends StatelessWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      //Start adding here
      initialRoute: '/home',
      routes: {
        '/home': (context) {
          return const HomePage();
        },
        '/sign-in': ((context) {
          return SignInScreen(
            actions: [
              ForgotPasswordAction(((context, email) {
                Navigator.of(context)
                    .pushNamed('/forgot-password', arguments: {'email': email});
              })),
              AuthStateChangeAction(((context, state) {
                if (state is SignedIn || state is UserCreated) {
                  var user = (state is SignedIn)
                      ? state.user
                      : (state as UserCreated).credential.user;
                  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);
                  }
                  Navigator.of(context).pushReplacementNamed('/home');
                }
              })),
            ],
          );
        }),
        '/forgot-password': ((context) {
          final arguments = ModalRoute.of(context)?.settings.arguments
              as Map<String, dynamic>?;

          return ForgotPasswordScreen(
            email: arguments?['email'] as String,
            headerMaxExtent: 200,
          );
        }),
        '/profile': ((context) {
          return ProfileScreen(
            providers: [],
            actions: [
              SignedOutAction(
                ((context) {
                  Navigator.of(context).pushReplacementNamed('/home');
                }),
              ),
            ],
          );
        })
      },
      // end adding here
      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,
      ),
    );
  }
}

Jedem Bildschirm ist basierend auf dem neuen Status des Authentifizierungsflusses ein anderer Aktionstyp zugeordnet. Nach den meisten Statusänderungen bei der Authentifizierung können Sie zu einem bevorzugten Bildschirm zurückkehren, unabhängig davon, ob es sich um den Startbildschirm oder einen anderen Bildschirm, z. B. das Profil, handelt.

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

lib/main.dart

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Consumer<ApplicationState>(
        builder: (context, appState, child) => 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 packen es in ein Consumer -Widget. Das Consumer-Widget ist die übliche Methode, mit der das provider verwendet werden kann, um einen Teil der Struktur neu zu erstellen, wenn sich der App-Status ändert. Das AuthFunc Widget sind die ergänzenden Widgets, die 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 fordert Sie das System auf, das Registrierungsformular auszufüllen.

e5e65065dba36b54.png

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

4ed811a25b0cf816.png

6. Schreiben Sie Nachrichten an Firestore

Es ist großartig 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 gerne kommen oder wen sie zu treffen hoffen.

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

Datenmodell

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

7c20dc8424bb1d84.png

Nachrichten zu Firestore hinzufügen

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 Sendeschaltfläche hinzu, und dann fügen Sie den Code hinzu, der diese Elemente mit der Datenbank verbindet.

  1. Fügen Sie in der Datei lib/main.dart Importe für die Pakete 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';
  1. Fügen Sie am Ende der Datei lib/main.dart ein zustandsbehaftetes GuestBook -Widget hinzu, um die UI-Elemente eines Nachrichtenfelds und einer Sendeschaltfläche zu erstellen:

lib/main.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 ein paar interessante Punkte. Zunächst instanziieren Sie ein Formular, damit Sie überprüfen können, ob die Nachricht tatsächlich Inhalt enthält, und dem Benutzer eine Fehlermeldung anzeigen, wenn keine 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 ihrer 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 auch, dass das TextFormField in ein Expanded -Widget eingeschlossen ist, wodurch das TextFormField wird, zusätzlichen Platz in der Zeile zu füllen. Um besser zu verstehen, warum dies erforderlich ist, siehe 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.

  1. Bearbeiten Sie 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 aktualisieren diesen Code in Kürze, damit er funktioniert.

App-Vorschau

Wenn ein Benutzer auf SENDEN klickt, wird das folgende Code-Snippet ausgelöst. 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 in der guestbook hinzu.

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

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

lib/main.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.
}

UI und Datenbank verbinden

Sie haben eine Benutzeroberfläche, in 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 Firestore hinzuzufügen. Jetzt müssen Sie nur noch die beiden verbinden.

  • Nehmen Sie in der Datei 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, _) => 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 Consumer<ApplicationState> erneut, um den App-Zustand 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. 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. Sie sehen die Nachricht jedoch nicht in Ihrer eigentlichen Flutter-App, 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 das folgende Code-Snippet an:

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. Zeit, das zu 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 App-Status Code hinzu, der auf neu hinzugefügte Nachrichten von der App wartet.

  1. Fügen Sie in der Datei lib/main.dart vor dem GuestBook Widget die folgende Klasse hinzu, um eine strukturierte Ansicht der Daten verfügbar zu machen, die Sie in Firestore speichern.

lib/main.dart

class GuestBookMessage {
  GuestBookMessage({required this.name, required this.message});
  final String name;
  final String message;
}
  1. Fügen Sie im Abschnitt von ApplicationState , in dem Sie Status und Getter definieren, die folgenden Zeilen hinzu:

lib/main.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 abzumelden, wenn er sich abmeldet:

lib/main.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 dort eine Abfrage über die guestbook erstellen und das Abonnieren und Abbestellen dieser Sammlung handhaben. Sie hören sich den Stream an, wo Sie einen lokalen Cache der Nachrichten in der guestbook rekonstruieren und auch einen Verweis auf dieses Abonnement speichern, damit Sie es später wieder abbestellen können. Hier passiert viel, also sollten Sie es in einem Debugger untersuchen, um zu untersuchen, was passiert, um ein klareres mentales Modell zu erhalten. Weitere Informationen finden Sie unter Abrufen von Echtzeit-Updates mit Firestore .

  1. Fügen Sie im GuestBook Widget eine Liste von Nachrichten als Teil der Konfiguration hinzu, um diesen sich ändernden Zustand mit der Benutzeroberfläche zu verbinden:

lib/main.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/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 Methode build() mit einem Spalten-Widget und fügen dann eine Sammlung für am Ende der untergeordneten Column Column , um einen neuen Paragraph für jede Nachricht in der Nachrichtenliste zu generieren.

  1. Aktualisieren Sie den Hauptteil von HomePage , 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.loggedIn) ...[
        const Header('Discussion'),
        GuestBook(
          addMessage: (message) =>
              appState.addMessageToGuestBook(message),
          messages: appState.guestBookMessages, // new
        ),
      ],
    ],
  ),
),

Testen Sie die Nachrichtensynchronisierung

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

Nachrichtensynchronisation testen:

  1. Suchen Sie in der App die 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 die Fenster und Registerkarten hinweg synchronisiert.
  4. Optional: Löschen, ändern oder fügen Sie neue Nachrichten im Datenbankmenü der Firebase-Konsole manuell hinzu. Alle Änderungen werden in der Benutzeroberfläche angezeigt.

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

App-Vorschau

8. Richten Sie grundlegende Sicherheitsregeln ein

Sie haben 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 frühen Phasen der Entwicklung verwenden. Als Best Practice sollten Sie bei der Entwicklung 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 die gesamte Datenbank bis hin zu Vorgängen in einem bestimmten Dokument.

Richten Sie grundlegende Sicherheitsregeln ein:

  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 Ihrem Regelsatz die Lese- und Schreibregeln 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 zu chatten, die an der Veranstaltung interessiert sind. Außerdem weißt du nur, ob jemand kommt, wenn er es im Chat sagt.

In diesem Schritt organisieren Sie sich und lassen die Leute wissen, wie viele Leute kommen. Sie fügen dem App-Status einige Funktionen hinzu. Die erste ist die Möglichkeit für einen angemeldeten Benutzer, zu nominieren, ob er teilnehmen möchte. Der zweite ist ein Zähler dafür, wie viele Personen teilnehmen.

  1. Fügen Sie in der Datei lib/main.dart die folgenden Zeilen zum Abschnitt accessors 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});
  }
}
  1. Aktualisieren Sie die Methode init() von ApplicationState wie folgt:

lib/main.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) {
        _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();
    });
  }

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

  1. Fügen Sie nach der GuestBookMessage Deklaration die folgende Enumeration hinzu:

lib/main.dart

enum Attending { yes, no, unknown }
  1. Definieren Sie ein neues Widget, das sich wie alte Optionsfelder verhält:

lib/main.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: [
              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'),
              ),
            ],
          ),
        );
    }
  }
}

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 mit einer flachen Darstellung ausgeblendet.

  1. Aktualisieren Sie die Methode build() von HomePage , um YesNoSelection zu nutzen, ermöglichen Sie es einem angemeldeten Benutzer, anzugeben, ob er teilnehmen möchte, und zeigen Sie die Anzahl der Teilnehmer für die Veranstaltung an:

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.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 mit den Schaltflächen hinzufügen, abgelehnt werden. Sie müssen die Regeln aktualisieren, um Ergänzungen zur Sammlung der attendees zuzulassen.

  1. Greifen Sie in der attendees auf die Authentifizierungs-UID zu, die Sie als Dokumentnamen verwendet haben, und vergewissern Sie sich, dass die UID des Einreichers mit dem Dokument uid , 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, aber 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

10. Herzlichen Glückwunsch!

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

Mehr erfahren