Lokalne programowanie aplikacji Flutter z użyciem Pakietu emulatorów Firebase

1. Zanim zaczniesz

Z tego modułu dowiesz się, jak używać Pakietu emulatorów Firebase z Flutterem podczas programowania lokalnego. Dowiesz się, jak używać uwierzytelniania za pomocą adresu e-mail i hasła w Pakiecie emulatorów oraz jak odczytywać i zapisywać dane w emulatorze Firestore. Na koniec nauczysz się importować i eksportować dane z emulatorów, aby za każdym razem, gdy wrócisz do tworzenia aplikacji, pracować z tymi samymi fałszywymi danymi.

Wymagania wstępne

W tym ćwiczeniu z programowania zakładamy, że masz już pewne doświadczenie w korzystaniu z Fluttera. Jeśli nie, najpierw warto poznać podstawy. Przydatne mogą być te linki:

Powinieneś też mieć pewne doświadczenie w korzystaniu z Firebase, ale nie musisz mieć za sobą dodawania Firebase do projektu Flutter. Jeśli nie znasz konsoli Firebase lub dopiero zaczynasz korzystać z Firebase, najpierw zapoznaj się z tymi artykułami:

Co utworzysz

Z tego przewodnika dowiesz się, jak utworzyć prostą aplikację do prowadzenia dziennika. Aplikacja będzie miała ekran logowania oraz ekran, na którym można czytać wcześniejsze wpisy w dzienniku i tworzyć nowe.

cd5c4753bbee8af.png 8cb4d21f656540bf.png

Czego się nauczysz

Dowiesz się, jak zacząć korzystać z Firebase oraz jak zintegrować i używać pakietu emulatorów Firebase w procesie tworzenia aplikacji we Flutterze. Omówimy te tematy dotyczące Firebase:

Pamiętaj, że te tematy są omawiane w zakresie niezbędnym do omówienia pakietu emulatorów Firebase. Ten przewodnik skupia się na dodawaniu projektu Firebase do aplikacji Flutter i na programowaniu z użyciem pakietu Firebase Emulator Suite. Nie będziemy szczegółowo omawiać Uwierzytelniania Firebase ani Firestore. Jeśli nie znasz tych tematów, zacznij od samouczka Getting to Know Firebase for Flutter.

Czego potrzebujesz

  • Znajomość platformy Flutter i zainstalowany pakiet SDK
  • Edytory tekstu IntelliJ JetBrains lub VS Code
  • przeglądarka Google Chrome (lub inne preferowane środowisko docelowe do tworzenia aplikacji we Flutterze); Niektóre polecenia terminala w tym samouczku zakładają, że aplikacja jest uruchomiona w Chrome).

2. Tworzenie i konfigurowanie projektu Firebase

Pierwszym zadaniem, które musisz wykonać, jest utworzenie projektu Firebase w konsoli internetowej Firebase. Większość tego samouczka będzie dotyczyć Pakietu emulatorów, który korzysta z lokalnie działającego interfejsu, ale najpierw musisz skonfigurować pełny projekt Firebase.

Tworzenie projektu Firebase

  1. Zaloguj się w konsoli Firebase, korzystając ze swojego konta Google.
  2. Kliknij przycisk, aby utworzyć nowy projekt, a potem wpisz jego nazwę (np. Firebase-Flutter-Codelab).
  3. Kliknij Dalej.
  4. Po wyświetleniu monitu przeczytaj i zaakceptuj warunki usługi Firebase, a potem kliknij Dalej.
  5. (Opcjonalnie) Włącz w konsoli Firebase pomoc AI (nazywaną „Gemini w Firebase”).
  6. W tym samouczku nie potrzebujesz Google Analytics, więc wyłącz opcję Google Analytics.
  7. Kliknij Utwórz projekt, poczekaj, aż projekt zostanie udostępniony, a następnie kliknij Dalej.

Więcej informacji o projektach Firebase znajdziesz w artykule Projekty w Firebase.

Konfigurowanie usług Firebase

Tworzona aplikacja korzysta z 2 usług Firebase dostępnych w przypadku aplikacji Flutter:

  • Uwierzytelnianie Firebase, aby umożliwić użytkownikom logowanie się w aplikacji.
  • Cloud Firestore – zapisywanie danych strukturalnych w chmurze i otrzymywanie natychmiastowych powiadomień o zmianach danych.

Te 2 usługi wymagają specjalnej konfiguracji lub włączenia w konsoli Firebase.

Włączanie Cloud Firestore

Aplikacja Flutter używa Cloud Firestore do zapisywania wpisów w dzienniku.

Włącz Cloud Firestore:

  1. W sekcji Kompilacja w konsoli Firebase kliknij Cloud Firestore.
  2. Kliknij Utwórz bazę danych. 99e8429832d23fa3.png
  3. Wybierz opcję Uruchom w trybie testowym. Przeczytaj wyłączenie odpowiedzialności dotyczące reguł zabezpieczeń. Tryb testowy zapewnia możliwość swobodnego zapisywania danych w bazie danych podczas programowania. Kliknij Dalej. 6be00e26c72ea032.png
  4. Wybierz lokalizację bazy danych (możesz użyć domyślnej). Pamiętaj, że tej lokalizacji nie można później zmienić. 278656eefcfb0216.png
  5. Kliknij Włącz.

3. Konfigurowanie aplikacji Flutter

Zanim zaczniemy, musisz pobrać kod początkowy i zainstalować wiersz poleceń Firebase.

Pobieranie kodu startowego

Sklonuj repozytorium GitHub z wiersza poleceń:

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

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 codelabów. Kod do tego ćwiczenia jest w języku flutter-codelabs/firebase-emulator-suite.

Struktura katalogu w flutter-codelabs/firebase-emulator-suite składa się z 2 projektów Fluttera. Jeden z nich to complete, który możesz wykorzystać, jeśli chcesz przejść dalej lub porównać go z własnym kodem. Drugi projekt nazywa się start.

Kod, od którego chcesz zacząć, znajduje się w katalogu flutter-codelabs/firebase-emulator-suite/start. Otwórz lub zaimportuj ten katalog w preferowanym środowisku IDE.

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

Zainstaluj wiersz poleceń Firebase

Wiersz poleceń Firebase udostępnia narzędzia do zarządzania projektami Firebase. Interfejs wiersza poleceń jest wymagany do korzystania z pakietu emulatorów, więc musisz go zainstalować.

CLI można zainstalować na wiele sposobów. Jeśli używasz systemu macOS lub Linux, najprostszym sposobem jest uruchomienie tego polecenia w terminalu:

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

Po zainstalowaniu wiersza poleceń musisz uwierzytelnić się w Firebase.

  1. Zaloguj się w Firebase za pomocą konta Google, uruchamiając to polecenie:
firebase login
  1. To polecenie łączy komputer lokalny z Firebase i przyznaje Ci dostęp do projektów Firebase.
  1. Sprawdź, czy interfejs CLI jest prawidłowo zainstalowany i ma dostęp do Twojego konta, wyświetlając listę projektów Firebase. Uruchom to polecenie:
firebase projects:list
  1. Wyświetlona lista powinna być taka sama jak lista projektów Firebase w konsoli Firebase. Powinien wyświetlić się co najmniej projekt firebase-flutter-codelab.

Instalowanie interfejsu wiersza poleceń FlutterFire

Wiersz poleceń FlutterFire jest oparty na wierszu poleceń Firebase i ułatwia integrację projektu Firebase z aplikacją Flutter.

Najpierw zainstaluj interfejs wiersza poleceń:

dart pub global activate flutterfire_cli

Sprawdź, czy interfejs CLI został zainstalowany. Uruchom to polecenie w katalogu projektu Flutter i sprawdź, czy interfejs wiersza poleceń wyświetla menu pomocy.

flutterfire --help

Użyj wiersza poleceń Firebase i FlutterFire, aby dodać projekt Firebase do aplikacji Flutter

Po zainstalowaniu obu interfejsów CLI możesz skonfigurować poszczególne usługi Firebase (np. Firestore), pobrać emulatory i dodać Firebase do aplikacji Flutter za pomocą zaledwie kilku poleceń terminala.

Najpierw dokończ konfigurację Firebase, uruchamiając to polecenie:

firebase init

To polecenie wyświetli serię pytań potrzebnych do skonfigurowania projektu. Zrzuty ekranu przedstawiające ten proces:

  1. Gdy pojawi się prośba o wybranie funkcji, kliknij „Firestore” i „Emulatory”. (Nie ma opcji Uwierzytelnianie, ponieważ nie korzysta ona z konfiguracji, którą można modyfikować w plikach projektu Flutter). fe6401d769be8f53.png
  2. Następnie, gdy pojawi się odpowiedni komunikat, kliknij „Użyj istniejącego projektu”.

f11dcab439e6ac1e.png

  1. Teraz wybierz projekt utworzony w poprzednim kroku: flutter-firebase-codelab.

3bdc0c6934991c25.png

  1. Następnie zadamy Ci kilka pytań dotyczących nazw plików, które zostaną wygenerowane. W przypadku każdego pytania proponuję nacisnąć „Enter”, aby wybrać domyślną odpowiedź. 9bfa2d507e199c59.png
  2. Na koniec musisz skonfigurować emulatory. Na liście wybierz Firestore i Authentication, a następnie naciśnij „Enter” przy każdym pytaniu o konkretne porty, które mają być używane w przypadku poszczególnych emulatorów. Gdy pojawi się pytanie, czy chcesz używać interfejsu emulatora, wybierz domyślną odpowiedź „Yes” (Tak).

Po zakończeniu procesu powinny pojawić się dane wyjściowe podobne do tych na zrzucie ekranu poniżej.

Ważne: wynik może się nieco różnić od mojego, jak widać na zrzucie ekranu poniżej, ponieważ ostatnie pytanie będzie domyślnie miało odpowiedź „Nie”, jeśli masz już pobrane emulatory.

8544e41037637b07.png

Konfigurowanie FlutterFire

Następnie możesz użyć FlutterFire, aby wygenerować potrzebny kod Dart do korzystania z Firebase w aplikacji Flutter.

flutterfire configure

Po uruchomieniu tego polecenia pojawi się prośba o wybranie projektu Firebase, którego chcesz użyć, oraz platform, które chcesz skonfigurować. W tym samouczku przykłady korzystają z Flutter Web, ale możesz skonfigurować projekt Firebase tak, aby korzystać ze wszystkich opcji.

Na zrzutach ekranu poniżej widać, na jakie pytania musisz odpowiedzieć.

619b7aca6dc15472.png 301c9534f594f472.png

Ten zrzut ekranu przedstawia dane wyjściowe na końcu procesu. Jeśli znasz Firebase, zauważysz, że nie musisz tworzyć aplikacji w konsoli, ponieważ zrobił to za Ciebie interfejs wiersza poleceń FlutterFire.

12199a85ade30459.png

Dodawanie pakietów Firebase do aplikacji Flutter

Ostatnim krokiem konfiguracji jest dodanie odpowiednich pakietów Firebase do projektu Flutter. W terminalu upewnij się, że jesteś w katalogu głównym projektu Flutter w flutter-codelabs/firebase-emulator-suite/start. Następnie uruchom te 3 polecenia:

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

To jedyne pakiety, których będziesz używać w tej aplikacji.

4. Włączanie emulatorów Firebase

Aplikacja Flutter i projekt Firebase są już skonfigurowane do korzystania z emulatorów, ale musisz jeszcze poinformować kod Fluttera, aby przekierowywał wychodzące żądania Firebase do portów lokalnych.

Najpierw dodaj kod inicjowania Firebase i kod konfiguracji emulatora do funkcji main w pliku main.dart..

main.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

import 'app_state.dart';
import 'firebase_options.dart';
import 'logged_in_view.dart';
import 'logged_out_view.dart';


void main() async {
 WidgetsFlutterBinding.ensureInitialized();
 await Firebase.initializeApp(
   options: DefaultFirebaseOptions.currentPlatform,
 );

 if (kDebugMode) {
   try {
     FirebaseFirestore.instance.useFirestoreEmulator('localhost', 8080);
     await FirebaseAuth.instance.useAuthEmulator('localhost', 9099);
   } catch (e) {
     // ignore: avoid_print
     print(e);
   }
 }

 runApp(MyApp());
}

Pierwsze kilka wierszy kodu inicjuje Firebase. Jeśli korzystasz z Firebase w aplikacji Flutter, prawie zawsze warto zacząć od wywołania funkcji WidgetsFlutterBinding.ensureInitializedFirebase.initializeApp.

Następnie kod zaczynający się od wiersza if (kDebugMode) informuje aplikację, że ma kierować reklamy na emulatory, a nie na produkcyjny projekt Firebase. kDebugMode zapewnia, że kierowanie na emulatory będzie następować tylko w środowisku programistycznym. Ponieważ kDebugMode jest stałą wartością, kompilator Dart wie, że w trybie wydania może całkowicie usunąć ten blok kodu.

Uruchom emulatory

Emulatory należy uruchomić przed uruchomieniem aplikacji Flutter. Najpierw uruchom emulatory, wpisując w terminalu to polecenie:

firebase emulators:start

To polecenie uruchamia emulatory i udostępnia porty hosta lokalnego, z którymi możemy wchodzić w interakcje. Po uruchomieniu tego polecenia powinny pojawić się dane wyjściowe podobne do tych:

bb7181eb70829606.png

Te dane wyjściowe informują, które emulatory są uruchomione i gdzie można je zobaczyć. Najpierw zapoznaj się z interfejsem emulatora na stronie localhost:4000.

11563f4c7216de81.png

To strona główna interfejsu lokalnego emulatora. Wyświetla listę wszystkich dostępnych emulatorów, a każdy z nich jest oznaczony stanem włączony lub wyłączony.

5. Emulator Uwierzytelniania Firebase

Pierwszym emulatorem, którego użyjesz, będzie emulator uwierzytelniania. Zacznij od emulatora uwierzytelniania. W tym celu kliknij „Go to emulator” (Przejdź do emulatora) na karcie Authentication (Uwierzytelnianie) w interfejsie. Zobaczysz stronę podobną do tej:

3c1bfded40733189.png

Ta strona jest podobna do strony konsoli internetowej Auth. Zawiera tabelę z listą użytkowników, podobnie jak konsola online, i umożliwia ręczne dodawanie użytkowników. Najważniejsza różnica polega na tym, że jedyną opcją metody uwierzytelniania dostępną na emulatorach jest e-mail i hasło. Jest to wystarczające w przypadku programowania lokalnego.

Następnie przejdziesz przez proces dodawania użytkownika do emulatora Firebase Auth i logowania go za pomocą interfejsu Flutter.

Dodawanie użytkownika

Kliknij przycisk „Dodaj użytkownika” i wypełnij formularz tymi informacjami:

  • Wyświetlana nazwa: Dash
  • E-mail: dash@email.com
  • Hasło: dashword

Prześlij formularz. W tabeli pojawi się teraz użytkownik. Teraz możesz zaktualizować kod, aby zalogować się jako ten użytkownik.

logged_out_view.dart

Jedynym kodem w widżecie LoggedOutView, który wymaga aktualizacji, jest wywołanie zwrotne wywoływane, gdy użytkownik naciśnie przycisk logowania. Zaktualizuj kod, aby wyglądał tak:

class LoggedOutView extends StatelessWidget {
 final AppState state;
 const LoggedOutView({super.key, required this.state});
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       title: const Text('Firebase Emulator Suite Codelab'),
     ),
     body: Center(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         children: [
          Text(
           'Please log in',
            style: Theme.of(context).textTheme.displaySmall,
          ),
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: ElevatedButton(
             onPressed: () async {
              await state.logIn('dash@email.com', 'dashword').then((_) {
                if (state.user != null) {
                 context.go('/');
                }
              });
              },
              child: const Text('Log In'),
          ),
        ),
      ],
    ),
   ),
  );
 }
}

Zaktualizowany kod zastępuje ciągi TODO adresem e-mail i hasłem utworzonymi w emulatorze uwierzytelniania. W następnym wierszu wiersz if(true) został zastąpiony kodem, który sprawdza, czy state.user ma wartość null. Więcej informacji na ten temat znajdziesz w kodzie w AppClass.

app_state.dart

Należy zaktualizować 2 fragmenty kodu w AppState. Najpierw nadaj elementowi AppState.user w klasie typ User z pakietu firebase_auth, a nie typ Object.

Następnie wypełnij metodę AppState.login, jak pokazano poniżej:

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'entry.dart';

class AppState {
 AppState() {
   _entriesStreamController = StreamController.broadcast(onListen: () {
     _entriesStreamController.add([
       Entry(
         date: '10/09/2022',
         text: lorem,
         title: '[Example] My Journal Entry',
       )
     ]);
   });
 }

 User? user; // <-- changed variable type
 Stream<List<Entry>> get entries => _entriesStreamController.stream;
 late final StreamController<List<Entry>> _entriesStreamController;

 Future<void> logIn(String email, String password) async {
   final credential = await FirebaseAuth.instance
       .signInWithEmailAndPassword(email: email, password: password);
   if (credential.user != null) {
     user = credential.user!;
     _listenForEntries();
   } else {
     print('no user!');
   }
 } 
 // ...
}

Definicja typu użytkownika to teraz User?. Ta klasa User pochodzi z Firebase Auth i zawiera potrzebne informacje, takie jak User.displayName, o których wspomnimy za chwilę.

Jest to podstawowy kod potrzebny do zalogowania użytkownika za pomocą adresu e-mail i hasła w usłudze Uwierzytelnianie Firebase. Wywołuje FirebaseAuth w celu zalogowania się, co zwraca obiekt Future<UserCredential>. Gdy przyszłość się zakończy, ten kod sprawdzi, czy do UserCredential jest dołączony element User. Jeśli w obiekcie danych logowania znajduje się użytkownik, oznacza to, że użytkownik zalogował się pomyślnie i można ustawić właściwość AppState.user. Jeśli nie, wystąpił błąd i zostanie on wydrukowany.

Pamiętaj, że jedynym wierszem kodu w tej metodzie, który jest specyficzny dla tej aplikacji (a nie ogólnym kodem FirebaseAuth), jest wywołanie metody _listenForEntries, które omówimy w następnym kroku.

TODO: Action Icon – Reload your app, and then press the Login button when it renders. Spowoduje to przejście do strony z napisem „Witaj ponownie, Osobo!” u góry. Uwierzytelnianie musi działać, ponieważ umożliwia Ci przejście na tę stronę, ale w logged_in_view.dart trzeba wprowadzić niewielką aktualizację, aby wyświetlać prawdziwe imię i nazwisko użytkownika.

logged_in_view.dart

Zmień pierwszy wiersz w metodzie LoggedInView.build:

class LoggedInView extends StatelessWidget {
 final AppState state;
 LoggedInView({super.key, required this.state});

 final PageController _controller = PageController(initialPage: 1);

 @override
 Widget build(BuildContext context) {
   final name = state.user!.displayName ?? 'No Name';

   return Scaffold(
 // ...

Teraz ten wiersz pobiera wartość displayName z właściwości User w obiekcie AppState. Ten displayName został ustawiony w emulatorze, gdy zdefiniowano pierwszego użytkownika. Po zalogowaniu się w aplikacji powinien pojawić się komunikat „Witaj ponownie, Dash!”, a nie TODO.

6. Odczytywanie i zapisywanie danych w emulatorze Firestore

Najpierw zapoznaj się z emulatorem Firestore. Na stronie głównej interfejsu emulatora (localhost:4000) kliknij „Go to emulator” (Otwórz emulator) na karcie Firestore. Powinien on wyglądać podobnie do tego:

Emulator:

791fce7dc137910a.png

Konsola Firebase:

e0dde9aea34af050.png

Jeśli masz doświadczenie z Firestore, zauważysz, że ta strona wygląda podobnie do strony Firestore w konsoli Firebase. Istnieje jednak kilka istotnych różnic.

  1. Możesz wyczyścić wszystkie dane jednym kliknięciem. W przypadku danych produkcyjnych byłoby to niebezpieczne, ale w przypadku szybkiego wprowadzania zmian jest to przydatne. Jeśli pracujesz nad nowym projektem i Twój model danych ulegnie zmianie, możesz go łatwo wyczyścić.
  2. Jest tam karta „Prośby”. Na tej karcie możesz obserwować przychodzące żądania kierowane do tego emulatora. Za chwilę omówię tę kartę bardziej szczegółowo.
  3. Nie ma kart Reguły, Indeksy ani Wykorzystanie. Istnieje narzędzie (omówione w następnej sekcji), które pomaga pisać reguły zabezpieczeń, ale nie można ustawić reguł zabezpieczeń dla lokalnego emulatora.

Podsumowując, ta wersja Firestore udostępnia więcej narzędzi przydatnych podczas programowania i usuwa narzędzia potrzebne w środowisku produkcyjnym.

Zapisywanie w Firestore

Zanim omówimy kartę „Żądania” w emulatorze, najpierw wyślij żądanie. Wymaga to aktualizacji kodu. Zacznij od połączenia formularza w aplikacji, aby zapisywać nowe wpisy w dzienniku Entry w Firestore.

Ogólny proces przesyłania Entry wygląda tak:

  1. Użytkownik wypełnia formularz i klika przycisk Submit.
  2. Interfejs wywołuje AppState.writeEntryToFirebase
  3. AppState.writeEntryToFirebase dodaje wpis do Firebase,

Żaden kod z kroków 1 i 2 nie wymaga zmian. Jedyny kod, który należy dodać w kroku 3, zostanie dodany w klasie AppState. Wprowadź w AppState.writeEntryToFirebase tę zmianę:

app_state.dart

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'entry.dart';

class AppState {
 AppState() {
   _entriesStreamController = StreamController.broadcast(onListen: () {
     _entriesStreamController.add([
       Entry(
         date: '10/09/2022',
         text: lorem,
         title: '[Example] My Journal Entry',
       )
     ]);
   });
 }

 User? user;
 Stream<List<Entry>> get entries => _entriesStreamController.stream;
 late final StreamController<List<Entry>> _entriesStreamController;

 Future<void> logIn(String email, String password) async {
   final credential = await FirebaseAuth.instance
       .signInWithEmailAndPassword(email: email, password: password);
   if (credential.user != null) {
     user = credential.user!;
     _listenForEntries();
   } else {
     print('no user!');
   }
 }

 void writeEntryToFirebase(Entry entry) {
   FirebaseFirestore.instance.collection('Entries').add(<String, String>{
     'title': entry.title,
     'date': entry.date.toString(),
     'text': entry.text,
   });
 }
 // ...
}

Kod w metodzie writeEntryToFirebase pobiera odniesienie do kolekcji o nazwie „Entries” w Firestore. Następnie dodaje nowy wpis, który musi być typu Map<String, String>.

W tym przypadku kolekcja „Entries” w Firestore nie istniała, więc Firestore ją utworzył.

Po dodaniu tego kodu włącz szybkie przeładowanie lub uruchom ponownie aplikację, zaloguj się i otwórz widok EntryForm. W formularzu możesz wpisać dowolne Strings. (Pole Date przyjmie dowolny ciąg znaków, ponieważ zostało uproszczone na potrzeby tych ćwiczeń z programowania. Nie ma silnej walidacji ani nie dba w żaden sposób o obiekty DateTime).

Kliknij w formularzu przycisk przesyłania. W aplikacji nic się nie stanie, ale nowy wpis będzie widoczny w interfejsie emulatora.

Karta żądań w emulatorze Firestore

W interfejsie przejdź do emulatora Firestore i otwórz kartę „Dane”. W katalogu głównym bazy danych powinna być teraz kolekcja o nazwie „Entries”. Powinien on zawierać dokument z tymi samymi informacjami, które zostały wpisane w formularzu.

a978fb34fb8a83da.png

Potwierdza to, że AppState.writeEntryToFirestore działa, i możesz teraz dokładniej zbadać żądanie na karcie Żądania. Kliknij tę kartę.

Żądania emulatora Firestore

Powinna pojawić się lista podobna do tej:

f0b37f0341639035.png

Możesz kliknąć dowolny element na liście, aby wyświetlić wiele przydatnych informacji. Kliknij element listy CREATE odpowiadający Twojej prośbie, aby utworzyć nowy wpis w dzienniku. Wyświetli się nowa tabela, która będzie wyglądać tak:

385d62152e99aad4.png

Jak wspomnieliśmy, emulator Firestore udostępnia narzędzia do tworzenia reguł zabezpieczeń aplikacji. W tym widoku zobaczysz dokładnie, którą linię w regułach bezpieczeństwa przeszło to żądanie (lub nie przeszło, jeśli tak było). W bardziej rozbudowanych aplikacjach reguły zabezpieczeń mogą się rozrastać i zawierać wiele kontroli autoryzacji. Ten widok służy do pisania i debugowania reguł autoryzacji.

Umożliwia też łatwe sprawdzenie każdego elementu tego żądania, w tym metadanych i danych uwierzytelniających. Te dane są używane do tworzenia złożonych reguł autoryzacji.

Odczytywanie z Firestore

Firestore stosuje synchronizację danych, aby przesyłać zaktualizowane dane na połączone urządzenia. W kodzie Fluttera możesz nasłuchiwać (lub subskrybować) kolekcje i dokumenty Firestore, a Twój kod będzie powiadamiany o każdej zmianie danych. W tej aplikacji nasłuchiwanie aktualizacji Firestore odbywa się w metodzie o nazwie AppState._listenForEntries.

Ten kod działa w połączeniu z kodami StreamControllerStream, które są odpowiednio nazywane AppState._entriesStreamControllerAppState.entries. Ten kod jest już napisany, podobnie jak cały kod potrzebny w interfejsie do wyświetlania danych z Firestore.

Zaktualizuj metodę _listenForEntries, aby odpowiadała poniższemu kodowi:

app_state.dart

import 'dart:async';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

import 'entry.dart';

class AppState {
 AppState() {
   _entriesStreamController = StreamController.broadcast(onListen: () {
     _entriesStreamController.add([
       Entry(
         date: '10/09/2022',
         text: lorem,
         title: '[Example] My Journal Entry',
       )
     ]);
   });
 }

 User? user;
 Stream<List<Entry>> get entries => _entriesStreamController.stream;
 late final StreamController<List<Entry>> _entriesStreamController;

 Future<void> logIn(String email, String password) async {
   final credential = await FirebaseAuth.instance
       .signInWithEmailAndPassword(email: email, password: password);
   if (credential.user != null) {
     user = credential.user!;
     _listenForEntries();
   } else {
     print('no user!');
   }
 }

 void writeEntryToFirebase(Entry entry) {
   FirebaseFirestore.instance.collection('Entries').add(<String, String>{
     'title': entry.title,
     'date': entry.date.toString(),
     'text': entry.text,
   });
 }

 void _listenForEntries() {
   FirebaseFirestore.instance
       .collection('Entries')
       .snapshots()
       .listen((event) {
     final entries = event.docs.map((doc) {
       final data = doc.data();
       return Entry(
         date: data['date'] as String,
         text: data['text'] as String,
         title: data['title'] as String,
       );
     }).toList();

     _entriesStreamController.add(entries);
   });
 }
 // ...
}

Ten kod nasłuchuje kolekcji „Entries” w Firestore. Gdy Firestore powiadomi tego klienta o nowych danych, przekaże te dane i kod w _listenForEntries zmieni wszystkie dokumenty podrzędne w obiekt, którego może używać nasza aplikacja (Entry). Następnie doda te wpisy do StreamController o nazwie _entriesStreamController (którego nasłuchuje interfejs). Ten kod to jedyna wymagana aktualizacja.

Pamiętaj, że metoda AppState.logIn wywołuje metodę _listenForEntries, która rozpoczyna proces nasłuchiwania po zalogowaniu się użytkownika.

// ...
Future<void> logIn(String email, String password) async {
 final credential = await FirebaseAuth.instance
     .signInWithEmailAndPassword(email: email, password: password);
 if (credential.user != null) {
   user = credential.user!;
   _listenForEntries();
 } else {
   print('no user!');
 }
}
// ...

Teraz uruchom aplikację. Powinna wyglądać tak:

b8a31c7a8900331.gif

7. Eksportowanie i importowanie danych do emulatora

Emulatory Firebase obsługują importowanie i eksportowanie danych. Korzystanie z importu i eksportu umożliwia kontynuowanie prac nad projektem z tymi samymi danymi po przerwie. Możesz też zatwierdzać pliki danych w git, dzięki czemu inni programiści, z którymi współpracujesz, będą mieli dostęp do tych samych danych.

Eksportowanie danych z emulatora

Najpierw wyeksportuj dane z emulatora, które już masz. Gdy emulatory są nadal uruchomione, otwórz nowe okno terminala i wpisz to polecenie:

firebase emulators:export ./emulators_data

.emulators_data to argument, który informuje Firebase, gdzie wyeksportować dane. Jeśli katalog nie istnieje, zostanie utworzony. Możesz użyć dowolnej nazwy tego katalogu.

Po uruchomieniu tego polecenia w terminalu, w którym zostało ono uruchomione, zobaczysz te dane wyjściowe:

i  Found running emulator hub for project flutter-firebase-codelab-d6b79 at http://localhost:4400
i  Creating export directory /Users/ewindmill/Repos/codelabs/firebase-emulator-suite/complete/emulators_data
i  Exporting data to: /Users/ewindmill/Repos/codelabs/firebase-emulator-suite/complete/emulators_data
✔  Export complete

Jeśli przejdziesz do okna terminala, w którym działają emulatory, zobaczysz te dane wyjściowe:

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

W katalogu projektu powinien znajdować się katalog o nazwie ./emulators_data, który zawiera pliki JSON oraz inne pliki metadanych z zapisanymi danymi.

Importowanie danych z emulatora

Teraz możesz zaimportować te dane w ramach procesu programowania i kontynuować pracę od miejsca, w którym została przerwana.

Najpierw zatrzymaj emulatory, jeśli są uruchomione, naciskając CTRL+C w terminalu.

Następnie uruchom polecenie emulators:start, które już znasz, ale z flagą określającą, jakie dane mają zostać zaimportowane:

firebase emulators:start --import ./emulators_data

Gdy emulatory będą gotowe, otwórz interfejs emulatora pod adresem localhost:4000. Powinny się tam wyświetlać te same dane, z którymi pracowałeś(-aś) wcześniej.

Automatyczne eksportowanie danych po zamknięciu emulatorów

Możesz też eksportować dane automatycznie po zamknięciu emulatorów, zamiast pamiętać o eksportowaniu danych na koniec każdej sesji programowania.

Po uruchomieniu emulatorów wykonaj polecenie emulators:start z 2 dodatkowymi flagami.

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

Voila! Twoje dane będą teraz zapisywane i ponownie wczytywane za każdym razem, gdy będziesz pracować z emulatorami w tym projekcie. Możesz też określić inny katalog jako argument funkcji –export-on-exit flag, ale domyślnie będzie to katalog przekazany do funkcji –import.

Możesz też użyć dowolnej kombinacji tych opcji. Oto uwaga z dokumentacji: katalog eksportu można określić za pomocą tego oznaczenia: firebase emulators:start --export-on-exit=./saved-data. Jeśli użyjesz znaku --import, ścieżka eksportu domyślnie będzie taka sama, np. firebase emulators:start --import=./data-path --export-on-exit. Na koniec, jeśli chcesz, przekaż różne ścieżki katalogów do flag --import--export-on-exit.

8. Gratulacje!

Udało Ci się ukończyć moduł Wprowadzenie do emulatora Firebase i Fluttera. Ukończony kod tego ćwiczenia znajdziesz w katalogu „complete” na GitHubie: Flutter Codelabs

Omówione zagadnienia

  • Konfigurowanie aplikacji Flutter do korzystania z Firebase
  • Konfigurowanie projektu Firebase
  • FlutterFire CLI
  • wiersz poleceń Firebase
  • Emulator Uwierzytelniania Firebase
  • Emulator Firebase Firestore
  • Importowanie i eksportowanie danych z emulatora

Następne kroki

Więcej informacji

Sparky jest z Ciebie dumny.

2a0ad195769368b1.gif