Poznaj Firebase dla Flutter

1. Zanim zaczniesz

Podczas tego ćwiczenia z programowania nauczysz się podstaw Firebase dotyczących tworzenia aplikacji mobilnych Flutter na Androida i iOS.

Warunki wstępne

W tym ćwiczeniu z programowania zakładamy, że znasz Fluttera i zainstalowałeś Flutter SDK oraz edytor .

Co stworzysz

W tym ćwiczeniu z kodowania zbudujesz aplikację do czatu z RSVP i księgi gości na Androida, iOS, Internet i macOS, używając Fluttera. Będziesz uwierzytelniać użytkowników za pomocą Uwierzytelniania Firebase i synchronizować dane za pomocą Cloud Firestore.

Co będziesz potrzebował

Możesz uruchomić to ćwiczenia z programowania przy użyciu dowolnego z następujących urządzeń:

Oprócz powyższego będziesz także potrzebować:

  • Wybrana przeglądarka, taka jak Chrome.
  • IDE lub edytor tekstu do wyboru, taki jak Android Studio lub VS Code , skonfigurowany z wtyczkami Dart i Flutter.
  • Najnowsza stable wersja Fluttera (lub beta , jeśli lubisz życie na krawędzi).
  • Konto Google, takie jak konto Gmail, do tworzenia projektu Firebase i zarządzania nim.
  • Narzędzie wiersza poleceń firebase , zalogowane na Twoje konto Gmail.
  • Przykładowy kod ćwiczenia z programowania. Zobacz następny krok, aby dowiedzieć się, jak uzyskać kod.

2. Pobierz przykładowy kod

Zacznijmy od pobrania wstępnej wersji naszego projektu z GitHub.

Sklonuj repozytorium GitHub z wiersza poleceń:

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

Alternatywnie, jeśli masz zainstalowane narzędzie cli GitHub :

gh repo clone flutter/codelabs flutter-codelabs

Przykładowy kod należy sklonować do katalogu flutter-codelabs , który zawiera kod kolekcji ćwiczeń z kodowania. Kod tego ćwiczenia z programowania znajduje się w folderze flutter-codelabs/firebase-get-to-know-flutter .

Struktura katalogów pod flutter-codelabs/firebase-get-to-know-flutter to seria migawek pokazujących, gdzie powinieneś być na końcu każdego nazwanego kroku. To jest krok 2, więc zlokalizowanie pasujących plików jest tak proste, jak:

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

Jeśli chcesz przeskoczyć do przodu lub zobaczyć, jak coś powinno wyglądać po kroku, zajrzyj do katalogu o nazwie odpowiadającej kroku, który Cię interesuje.

Zaimportuj aplikację startową

Otwórz lub zaimportuj flutter-codelabs/firebase-get-to-know-flutter/step_02 do preferowanego środowiska IDE. Ten katalog zawiera kod startowy ćwiczenia z programowania, które składa się z niedziałającej jeszcze aplikacji Flutter meetup.

Znajdź pliki do pracy

Kod w tej aplikacji jest rozłożony na wiele katalogów. Ten podział funkcjonalności ma na celu ułatwienie pracy poprzez grupowanie kodu według funkcjonalności.

Zlokalizuj w projekcie następujące pliki:

  • lib/main.dart : Ten plik zawiera główny punkt wejścia i widżet aplikacji.
  • lib/src/widgets.dart : Ten plik zawiera garść widżetów, które pomagają ujednolicić stylizację aplikacji. Służą one do komponowania ekranu aplikacji startowej.
  • lib/src/authentication.dart : ten plik zawiera częściową implementację uwierzytelniania FirebaseUI z zestawem widżetów do tworzenia interfejsu użytkownika służącego do logowania w celu uwierzytelniania opartego na poczcie e-mail Firebase. Te widżety przepływu uwierzytelniania nie są jeszcze używane w aplikacji startowej, ale wkrótce zostaną one połączone.

W razie potrzeby dodasz dodatkowe pliki, aby zbudować resztę aplikacji.

Przeglądanie pliku lib/main.dart

Ta aplikacja korzysta z pakietu google_fonts , aby umożliwić nam uczynienie Roboto domyślną czcionką w całej aplikacji. Ćwiczeniem dla zmotywowanego czytelnika jest zapoznanie się z fonts.google.com i używanie znalezionych tam czcionek w różnych częściach aplikacji.

Korzystasz z widżetów pomocniczych z lib/src/widgets.dart w postaci Header , Paragraph i IconAndDetail . Te widżety zmniejszają bałagan w układzie strony opisanym w HomePage , eliminując zduplikowany kod. Ma to dodatkową zaletę, że zapewnia spójny wygląd i styl.

Oto jak wygląda Twoja aplikacja na Androida, iOS, w Internecie i macOS:

Podgląd aplikacji

3. Utwórz i skonfiguruj projekt Firebase

Wyświetlanie informacji o wydarzeniu jest świetne dla Twoich gości, ale samo pokazywanie wydarzeń nie jest zbyt przydatne dla nikogo. Dodajmy do tej aplikacji dynamiczną funkcjonalność. W tym celu musisz podłączyć Firebase do swojej aplikacji. Aby rozpocząć korzystanie z Firebase, musisz utworzyć i skonfigurować projekt Firebase.

Utwórz projekt Firebase

  1. Zaloguj się do Firebase .
  2. W konsoli Firebase kliknij Dodaj projekt (lub Utwórz projekt ) i nazwij swój projekt Firebase Firebase-Flutter-Codelab .

4395e4e67c08043a.png

  1. Przejrzyj opcje tworzenia projektu. Po wyświetleniu monitu zaakceptuj warunki Firebase. Pomiń konfigurowanie Google Analytics, ponieważ nie będziesz używać Analytics dla tej aplikacji.

b7138cde5f2c7b61.png

Aby dowiedzieć się więcej o projektach Firebase, zobacz Omówienie projektów Firebase .

Tworzona aplikacja korzysta z kilku produktów Firebase dostępnych dla aplikacji internetowych:

  • Uwierzytelnianie Firebase , aby umożliwić użytkownikom logowanie się w Twojej aplikacji.
  • Cloud Firestore do zapisywania uporządkowanych danych w chmurze i otrzymywania natychmiastowych powiadomień o zmianie danych.
  • Firebase Security Rules , aby zabezpieczyć bazę danych.

Niektóre z tych produktów wymagają specjalnej konfiguracji lub muszą być włączone za pomocą konsoli Firebase.

Włącz logowanie przez e-mail na potrzeby Uwierzytelniania Firebase

Aby umożliwić użytkownikom logowanie się do aplikacji internetowej, użyjesz metody logowania przez e-mail/hasło do tego ćwiczenia z programowania:

  1. W konsoli Firebase rozwiń menu Buduj w lewym panelu.
  2. Kliknij Uwierzytelnianie , a następnie kliknij przycisk Rozpocznij , a następnie kartę Metoda logowania (lub kliknij tutaj, aby przejść bezpośrednio do karty Metoda logowania ).
  3. Kliknij E-mail/hasło na liście Dostawcy logowania , ustaw przełącznik Włącz w pozycji włączonej, a następnie kliknij Zapisz . 58e3e3e23c2f16a4.png

Włącz Cloud Firestore

Aplikacja internetowa używa Cloud Firestore do zapisywania wiadomości na czacie i odbierania nowych wiadomości na czacie.

Włącz Cloud Firestore:

  1. W sekcji Kompilacja konsoli Firebase kliknij Cloud Firestore .
  2. Kliknij Utwórz bazę danych . 99e8429832d23fa3.png
  1. Wybierz opcję Uruchom w trybie testowym . Przeczytaj zastrzeżenie dotyczące zasad bezpieczeństwa. Tryb testowy zapewnia, że ​​możesz swobodnie pisać do bazy danych podczas tworzenia. Kliknij Dalej . 6be00e26c72ea032.png
  1. Wybierz lokalizację swojej bazy danych (możesz po prostu użyć domyślnej). Pamiętaj, że tej lokalizacji nie można później zmienić. 278656eefcfb0216.png
  2. Kliknij Włącz .

4. Konfiguracja Firebase

Aby korzystać z Firebase z Flutter, musisz wykonać proces konfiguracji projektu Flutter, aby poprawnie wykorzystywał biblioteki FlutterFire:

  • Dodaj zależności FlutterFire do swojego projektu
  • Zarejestruj żądaną platformę w projekcie Firebase
  • Pobierz plik konfiguracyjny specyficzny dla platformy i dodaj go do kodu.

W katalogu najwyższego poziomu aplikacji Flutter znajdują się podkatalogi o nazwach android , ios , macos i web . Te katalogi zawierają pliki konfiguracyjne specyficzne dla platformy, odpowiednio dla systemów iOS i Android.

Skonfiguruj zależności

Musisz dodać biblioteki FlutterFire dla dwóch produktów Firebase używanych w tej aplikacji — Firebase Auth i Cloud Firestore. Uruchom następujące trzy polecenia, aby dodać zależności.

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

firebase_core to wspólny kod wymagany dla wszystkich wtyczek Firebase Flutter.

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

firebase_auth umożliwia integrację z funkcją uwierzytelniania Firebase.

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

cloud_firestore umożliwia dostęp do przechowywania danych Cloud Firestore.

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

Po dodaniu wymaganych pakietów należy również skonfigurować projekty iOS, Android, macOS i Web runner, aby odpowiednio korzystać z Firebase. Korzystasz również z pakietu provider , który umożliwi oddzielenie logiki biznesowej od logiki wyświetlania.

Instalowanie flutterfire

FlutterFire CLI zależy od bazowego interfejsu Firebase CLI. Jeśli jeszcze tego nie zrobiłeś, upewnij się, że Firebase CLI jest zainstalowany na Twoim komputerze.

Następnie zainstaluj FlutterFire CLI, uruchamiając następujące polecenie:

$ dart pub global activate flutterfire_cli

Po zainstalowaniu polecenie flutterfire będzie dostępne na całym świecie.

Konfigurowanie aplikacji

Interfejs CLI wyodrębnia informacje z projektu Firebase i wybranych aplikacji projektowych w celu wygenerowania całej konfiguracji dla określonej platformy.

W katalogu głównym aplikacji uruchom polecenie configure:

$ flutterfire configure

Polecenie konfiguracji przeprowadzi Cię przez szereg procesów:

  1. Wybór projektu Firebase (na podstawie pliku .firebaserc lub z konsoli Firebase).
  2. Pytaj, jakie platformy (np. Android, iOS, macOS i web) chcesz skonfigurować.
  3. Określ, które aplikacje Firebase dla wybranych platform mają zostać użyte do wyodrębnienia konfiguracji. Domyślnie interfejs wiersza polecenia podejmie próbę automatycznego dopasowania aplikacji Firebase na podstawie bieżącej konfiguracji projektu.
  4. Wygeneruj plik firebase_options.dart w swoim projekcie.

Skonfiguruj macOS

Flutter na macOS buduje aplikacje w pełni piaskownicy. Ponieważ ta aplikacja integruje się za pomocą sieci, aby komunikować się z serwerami Firebase, musisz skonfigurować swoją aplikację z uprawnieniami klienta sieciowego.

macos/Runner/DebugProfile.uprawnienia

<?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.uprawnienia

<?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>

Zobacz uprawnienia i piaskownica aplikacji, aby uzyskać więcej informacji.

5. Dodaj logowanie użytkownika (RSVP)

Po dodaniu Firebase do aplikacji możesz skonfigurować przycisk RSVP, który rejestruje osoby korzystające z uwierzytelniania Firebase . W przypadku natywnego systemu Android, natywnego systemu iOS i sieci Web dostępne są gotowe pakiety FirebaseUI Auth, ale w przypadku Fluttera trzeba będzie zbudować tę funkcję.

Projekt pobrany w kroku 2 zawierał zestaw widżetów implementujących interfejs użytkownika dla większości procesu uwierzytelniania. Zaimplementujesz logikę biznesową, aby zintegrować Uwierzytelnianie Firebase z aplikacją.

Logika biznesowa z dostawcą

Zamierzasz użyć pakietu provider , aby udostępnić scentralizowany obiekt stanu aplikacji w drzewie aplikacji widżetów Flutter. Na początek zmodyfikuj importy w górnej części lib/main.dart :

lib/main.dart

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

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

Wiersze import wprowadzają Firebase Core i Auth, pobierają pakiet provider , którego używasz, aby udostępnić obiekt stanu aplikacji za pośrednictwem drzewa widżetów i zawierają widżety uwierzytelniania z lib/src .

Ten obiekt stanu aplikacji, ApplicationState , ma dwa główne obowiązki w tym kroku, ale zyska dodatkowe obowiązki, gdy dodasz więcej możliwości do aplikacji w późniejszych krokach. Pierwszym obowiązkiem jest zainicjowanie biblioteki Firebase za pomocą wywołania Firebase.initializeApp() , a następnie obsługa przepływu autoryzacji. Dodaj następującą klasę na końcu lib/main.dart :

lib/main.dart

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

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

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

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

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

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

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

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

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

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

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

Warto zwrócić uwagę na kilka kluczowych punktów w tej klasie. Użytkownik zaczyna nieuwierzytelniony, aplikacja wyświetla formularz z prośbą o podanie adresu e-mail użytkownika, w zależności od tego, czy ten adres e-mail jest zapisany, aplikacja poprosi użytkownika o rejestrację lub poprosi o hasło, a następnie zakładając, że wszystko działa, użytkownik jest uwierzytelniony.

Należy zauważyć, że nie jest to pełna implementacja przepływu FirebaseUI Auth, ponieważ nie obsługuje ona przypadku użytkownika z istniejącym kontem, który ma problemy z zalogowaniem. Wdrożenie tej dodatkowej możliwości pozostawiono jako ćwiczenie dla zmotywowany czytelnik.

Integracja przepływu uwierzytelniania

Teraz, gdy masz już początek stanu aplikacji, nadszedł czas, aby połączyć stan aplikacji z inicjalizacją aplikacji i dodać przepływ uwierzytelniania do HomePage . Zaktualizuj główny punkt wejścia, aby zintegrować stan aplikacji za pośrednictwem pakietu provider :

lib/main.dart

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

Modyfikacja funkcji main sprawia, że ​​pakiet dostawcy odpowiada za tworzenie instancji obiektu stanu aplikacji za pomocą widżetu ChangeNotifierProvider . Używasz tej konkretnej klasy dostawcy, ponieważ obiekt stanu aplikacji rozszerza ChangeNotifier i dzięki temu pakiet provider wie, kiedy ponownie wyświetlić zależne widżety. Na koniec zintegruj stan aplikacji z Authentication , aktualizując metodę build HomePage :

lib/main.dart

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

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

Tworzysz wystąpienie widgetu Authentication i umieszczasz go w widgecie Consumer . Widget Konsumenta w zwykły sposób, w jaki pakiet provider może być używany do odbudowy części drzewa, gdy zmieni się stan aplikacji. Widżet Authentication to interfejs uwierzytelniania, który będzie teraz testowany.

Testowanie przepływu uwierzytelniania

cdf2d25e436bd48d.png

Oto początek przepływu uwierzytelniania, w którym użytkownik może dotknąć przycisku RSVP, aby zainicjować formularz e-mail.

2a2cd6d69d172369.png

Po wprowadzeniu adresu e-mail system potwierdza, czy użytkownik jest już zarejestrowany, w takim przypadku użytkownik jest proszony o podanie hasła, alternatywnie, jeśli użytkownik nie jest zarejestrowany, przechodzi przez formularz rejestracyjny.

e5e65065dba36b54.png

Spróbuj wprowadzić krótkie hasło (mniej niż sześć znaków), aby sprawdzić procedurę obsługi błędów. Jeśli użytkownik jest zarejestrowany, zamiast tego zobaczy hasło.

fbb3ea35fb4f67a.png

Na tej stronie upewnij się, że wpisałeś nieprawidłowe hasła, aby sprawdzić obsługę błędów na tej stronie. Wreszcie, gdy użytkownik jest zalogowany, zobaczysz środowisko zalogowania, które oferuje użytkownikowi możliwość ponownego wylogowania.

4ed811a25b0cf816.png

I dzięki temu zaimplementowałeś przepływ uwierzytelniania. Gratulacje!

6. Napisz wiadomości do Cloud Firestore

Świadomość, że użytkownicy nadchodzą, jest świetna, ale dajmy gościom coś innego do zrobienia w aplikacji. A gdyby mogli zostawiać wiadomości w księdze gości? Mogą podzielić się, dlaczego są podekscytowani, że mogą przyjść lub kogo mają nadzieję spotkać.

Aby przechowywać wiadomości czatu, które użytkownicy piszą w aplikacji, użyjesz Cloud Firestore .

Model danych

Cloud Firestore to baza danych NoSQL, a dane przechowywane w bazie danych są podzielone na kolekcje, dokumenty, pola i podkolekcje. Każdą wiadomość z czatu będziesz przechowywać jako dokument w zbiorze najwyższego poziomu o nazwie guestbook .

7c20dc8424bb1d84.png

Dodaj wiadomości do Firestore

W tej sekcji dodasz funkcjonalność umożliwiającą użytkownikom pisanie nowych wiadomości do bazy danych. Najpierw dodajesz elementy interfejsu użytkownika (pole formularza i przycisk wyślij), a następnie dodajesz kod, który łączy te elementy z bazą danych.

Najpierw dodaj importy dla pakietu cloud_firestore i dart:async .

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';

Aby skonstruować elementy interfejsu użytkownika pola wiadomości i przycisku wysyłania, dodaj nowy widżet GuestBook na dole lib/main.dart .

lib/main.dart

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

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

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

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

Jest tu kilka interesujących miejsc. Po pierwsze, tworzysz wystąpienie formularza, dzięki czemu możesz sprawdzić, czy wiadomość rzeczywiście zawiera pewną zawartość, i pokazać użytkownikowi komunikat o błędzie, jeśli go nie ma. Sposób sprawdzania poprawności formularza obejmuje dostęp do stanu formularza za formularzem, a do tego celu używasz GlobalKey . Aby uzyskać więcej informacji na temat klawiszy i jak ich używać, zobacz odcinek Flutter Widgets 101 „Kiedy używać klawiszy” .

Zwróć również uwagę na sposób rozmieszczenia widżetów, masz Row , z TextFormField i StyledButton , który sam zawiera Row . Należy również zauważyć, że TextFormField jest opakowany w widżet Expanded , co zmusza TextFormField do zajęcia dodatkowego miejsca w wierszu. Aby lepiej zrozumieć, dlaczego jest to wymagane, zapoznaj się z artykułem Rozumienie ograniczeń .

Teraz, gdy masz już widżet, który umożliwia użytkownikowi wpisanie tekstu do dodania do Księgi Gości, musisz wyświetlić go na ekranie. Aby to zrobić, edytuj HomePage , aby dodać następujące dwa wiersze u dołu elementów 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)),

Chociaż to wystarczy, aby wyświetlić Widget, nie wystarczy zrobić nic pożytecznego. Wkrótce zaktualizujesz ten kod, aby działał.

Podgląd aplikacji

Kliknięcie przycisku WYŚLIJ przez użytkownika spowoduje uruchomienie poniższego fragmentu kodu. Dodaje zawartość pola wejściowego wiadomości do kolekcji guestbook bazy danych. W szczególności metoda addMessageToGuestBook dodaje treść wiadomości do nowego dokumentu (z automatycznie wygenerowanym identyfikatorem) do kolekcji guestbook .

Pamiętaj, że FirebaseAuth.instance.currentUser.uid to odwołanie do automatycznie wygenerowanego unikalnego identyfikatora, który Uwierzytelnianie Firebase nadaje wszystkim zalogowanym użytkownikom.

Wprowadź kolejną zmianę w pliku lib/main.dart . Dodaj metodę addMessageToGuestBook . W następnym kroku połączysz interfejs użytkownika i tę funkcję.

lib/main.dart

class ApplicationState extends ChangeNotifier {

  // Current content of ApplicationState elided ...

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

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

Podłączanie interfejsu użytkownika do bazy danych

Masz interfejs, w którym użytkownik może wpisać tekst, który chce dodać do księgi gości, i masz kod, aby dodać wpis do Cloud Firestore. Teraz wszystko, co musisz zrobić, to połączyć je ze sobą. W lib/main.dart wprowadź następującą zmianę w widżecie HomePage .

lib/main.dart

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

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

Zamieniłeś dwie linie dodane na początku tego kroku pełną implementacją. Ponownie używasz Consumer<ApplicationState> , aby udostępnić stan aplikacji dla renderowanej części drzewa. Dzięki temu możesz zareagować na wpisanie wiadomości w interfejsie użytkownika i opublikować ją w bazie danych. W kolejnej sekcji przetestujesz, czy dodane wiadomości są publikowane w bazie danych.

Przetestuj wysyłanie wiadomości

  1. Upewnij się, że jesteś zalogowany w aplikacji.
  2. Wpisz wiadomość, taką jak „Hej, tam!”, a następnie kliknij WYŚLIJ .

To działanie zapisuje wiadomość w Twojej bazie danych Cloud Firestore. Jednak nie zobaczysz jeszcze komunikatu w swojej rzeczywistej aplikacji Flutter, ponieważ nadal musisz wdrożyć pobieranie danych. Zrobisz to w następnym kroku.

Ale możesz zobaczyć nowo dodaną wiadomość w konsoli Firebase.

W konsoli Firebase, w panelu Bazy danych , powinieneś zobaczyć kolekcję guestbook z nowo dodaną wiadomością. Jeśli będziesz nadal wysyłać wiadomości, Twoja kolekcja księgi gości będzie zawierać wiele dokumentów, takich jak:

Konsola Firebase

713870af0b3b63c.png

7. Przeczytaj wiadomości

Fajnie, że goście mogą pisać wiadomości do bazy danych, ale nie widzą ich jeszcze w aplikacji. Naprawmy to!

Synchronizuj wiadomości

Aby wyświetlić komunikaty, musisz dodać detektory, które uruchamiają się po zmianie danych, a następnie utworzyć element interfejsu użytkownika, który pokazuje nowe komunikaty. Do stanu aplikacji dodasz kod, który nasłuchuje nowo dodanych wiadomości z aplikacji.

Tuż nad widżetem GuestBook następująca klasa wartości. Ta klasa udostępnia uporządkowany widok danych, które przechowujesz w Cloud Firestore.

lib/main.dart

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

W sekcji ApplicationState , w której definiujesz stan i gettery, dodaj następujące nowe wiersze:

lib/main.dart

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

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

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

I na koniec w sekcji inicjowania ApplicationState dodaj następujące elementy, aby zasubskrybować zapytanie dotyczące kolekcji dokumentów, gdy użytkownik się loguje, i anulować subskrypcję, gdy się wyloguje.

lib/main.dart

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

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

Ta sekcja jest ważna, ponieważ tutaj tworzysz zapytanie dotyczące kolekcji guestbook oraz obsługujesz subskrybowanie i anulowanie subskrypcji tej kolekcji. Słuchasz strumienia, w którym rekonstruujesz lokalną pamięć podręczną wiadomości w kolekcji guestbook , a także przechowujesz odniesienie do tej subskrypcji, aby móc później z niej zrezygnować. Dużo się tutaj dzieje i warto spędzić trochę czasu w debuggerze, sprawdzając, co się stanie, kiedy uzyskamy wyraźniejszy model mentalny.

Więcej informacji znajdziesz w dokumentacji Cloud Firestore .

W widżecie GuestBook musisz połączyć ten zmieniający się stan z interfejsem użytkownika. Modyfikujesz widżet, dodając listę wiadomości w ramach jego konfiguracji.

lib/main.dart

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

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

Następnie udostępniamy tę nową konfigurację w _GuestBookState , modyfikując metodę build w następujący sposób.

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

Zawijasz poprzednią zawartość metody budowania widżetem Column , a następnie na końcu elementów potomnych Column dodajesz kolekcję, aby wygenerować nowy Paragraph dla każdej wiadomości na liście wiadomości.

Na koniec musisz teraz zaktualizować HomePage , aby poprawnie skonstruować GuestBook z nowym parametrem messages .

lib/main.dart

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

Przetestuj synchronizację wiadomości

Cloud Firestore automatycznie i natychmiastowo synchronizuje dane z klientami subskrybowanymi do bazy danych.

  1. Wiadomości utworzone wcześniej w bazie danych powinny zostać wyświetlone w aplikacji. Zapraszam do pisania nowych wiadomości; powinny pojawić się natychmiast.
  2. Jeśli otworzysz obszar roboczy w wielu oknach lub kartach, wiadomości będą synchronizowane w czasie rzeczywistym między kartami.
  3. (Opcjonalnie) Możesz spróbować ręcznie usunąć, zmodyfikować lub dodać nowe wiadomości bezpośrednio w sekcji Baza danych w konsoli Firebase; wszelkie zmiany powinny pojawić się w interfejsie użytkownika.

Gratulacje! Czytasz dokumenty Cloud Firestore w swojej aplikacji!

Przegląd aplikacji

8. Skonfiguruj podstawowe zasady bezpieczeństwa

Początkowo konfigurujesz Cloud Firestore do korzystania z trybu testowego, co oznacza, że ​​Twoja baza danych jest otwarta na odczyty i zapisy. Jednak należy używać trybu testowego tylko na bardzo wczesnych etapach rozwoju. Najlepszym rozwiązaniem jest skonfigurowanie reguł zabezpieczeń dla bazy danych podczas tworzenia aplikacji. Bezpieczeństwo powinno być integralną częścią struktury i zachowania Twojej aplikacji.

Reguły bezpieczeństwa pozwalają kontrolować dostęp do dokumentów i zbiorów w Twojej bazie danych. Elastyczna składnia reguł umożliwia tworzenie reguł, które dopasowują wszystko, od wszystkich zapisów do całej bazy danych, po operacje na określonym dokumencie.

W konsoli Firebase możesz pisać reguły bezpieczeństwa dla Cloud Firestore:

  1. W sekcji Rozwijaj konsoli Firebase kliknij Baza danych , a następnie wybierz kartę Reguły (lub kliknij tutaj, aby przejść bezpośrednio do karty Reguły ).
  2. Powinieneś zobaczyć następujące domyślne reguły bezpieczeństwa, wraz z ostrzeżeniem, że reguły są publiczne.

7767a2d2e64e7275.png

Zidentyfikuj kolekcje

Najpierw zidentyfikuj kolekcje, w których aplikacja zapisuje dane.

W match /databases/{database}/documents wskaż kolekcję, którą chcesz zabezpieczyć:

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

Dodaj zasady bezpieczeństwa

Ponieważ użyłeś UID uwierzytelniania jako pola w każdym dokumencie księgi gości, możesz uzyskać UID uwierzytelniania i sprawdzić, czy każda osoba próbująca pisać w dokumencie ma pasujący UID uwierzytelniania.

Dodaj reguły odczytu i zapisu do swojego zestawu reguł, jak pokazano poniżej:

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

Teraz w księdze gości tylko zalogowani użytkownicy mogą czytać wiadomości (dowolna wiadomość!), ale tylko autor wiadomości może edytować wiadomość.

Dodaj reguły walidacji

Dodaj sprawdzanie poprawności danych, aby upewnić się, że w dokumencie znajdują się wszystkie oczekiwane pola:

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. Dodatkowy krok: Przećwicz to, czego się nauczyłeś

Zapisz status RSVP uczestnika

W tej chwili Twoja aplikacja umożliwia ludziom rozpoczęcie rozmowy, jeśli są zainteresowani wydarzeniem. Ponadto jedynym sposobem, aby dowiedzieć się, czy ktoś nadchodzi, jest opublikowanie tego na czacie. Zorganizujmy się i dajmy ludziom znać, ile osób przyjdzie.

Zamierzasz dodać kilka nowych możliwości do stanu aplikacji. Pierwsza to możliwość nominowania przez zalogowanego użytkownika, czy uczestniczy, czy nie. Druga możliwość to licznik, ile osób faktycznie uczestniczy.

W lib/main.dart dodaj następujące elementy do sekcji akcesorów, aby umożliwić interakcję kodu interfejsu użytkownika z tym stanem:

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

Zaktualizuj metodę init ApplicationState w następujący sposób:

lib/main.dart

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

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

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

Powyższe dodaje zawsze subskrybowane zapytanie, aby sprawdzić liczbę uczestników, oraz drugie zapytanie, które jest aktywne tylko wtedy, gdy użytkownik jest zalogowany, aby dowiedzieć się, czy użytkownik uczestniczy. Następnie dodaj następujące wyliczenie po deklaracji GuestBookMessage :

lib/main.dart

enum Attending { yes, no, unknown }

Zamierzasz teraz zdefiniować nowy widżet, który działa jak stare przyciski radiowe. Rozpoczyna się w nieokreślonym stanie, bez zaznaczenia ani tak, ani nie, ale gdy użytkownik wybierze, czy uczestniczy, czy nie, pokazuje tę opcję podświetloną z wypełnionym przyciskiem, a drugą opcję cofa się z płaskim renderowaniem.

lib/main.dart

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

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

Następnie musisz zaktualizować metodę budowania HomePage , aby skorzystać z YesNoSelection , umożliwiając zalogowanemu użytkownikowi nominowanie, jeśli uczestniczy. Wyświetlisz również liczbę uczestników tego wydarzenia.

lib/main.dart

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

Dodaj zasady

Ponieważ masz już skonfigurowane pewne reguły, nowe dane, które dodajesz za pomocą przycisków, zostaną odrzucone. Musisz zaktualizować reguły, aby umożliwić dodawanie do kolekcji attendees .

W przypadku kolekcji attendees , ponieważ użyłeś identyfikatora UID uwierzytelnienia jako nazwy dokumentu, możesz go pobrać i sprawdzić, czy uid osoby przesyłającej jest taki sam, jak dokument, który piszą. Umożliwisz wszystkim odczytanie listy uczestników (ponieważ nie ma tam prywatnych danych), ale tylko twórca powinien mieć możliwość jej aktualizacji.

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

Dodaj reguły walidacji

Dodaj sprawdzanie poprawności danych, aby upewnić się, że w dokumencie znajdują się wszystkie oczekiwane pola:

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;

    }
  }
}

(Opcjonalnie) Możesz teraz wyświetlić wyniki kliknięcia przycisków. Przejdź do panelu Cloud Firestore w konsoli Firebase.

Podgląd aplikacji

10. Gratulacje!

Używasz Firebase do tworzenia interaktywnej aplikacji internetowej działającej w czasie rzeczywistym!

Co omówiliśmy

  • Uwierzytelnianie Firebase
  • Cloud Firestore
  • Zasady bezpieczeństwa Firebase

Następne kroki

  • Chcesz dowiedzieć się więcej o innych produktach Firebase? Może chcesz przechowywać pliki graficzne przesyłane przez użytkowników? Lub wysyłać powiadomienia do swoich użytkowników? Zapoznaj się z dokumentacją Firebase . Chcesz dowiedzieć się więcej o wtyczkach Flutter dla Firebase? Sprawdź FlutterFire , aby uzyskać więcej informacji.
  • Chcesz dowiedzieć się więcej o Cloud Firestore? A może chcesz poznać podkolekcje i transakcje? Przejdź do laboratorium programowania internetowego Cloud Firestore, aby zapoznać się z bardziej szczegółowymi ćwiczeniami na temat Cloud Firestore. Lub obejrzyj tę serię YouTube, aby poznać Cloud Firestore !

Ucz się więcej

Jak poszło?

Będziemy wdzięczni za Twoją opinię! Proszę wypełnić (bardzo) krótki formularz tutaj .