Sviluppo locale delle app Flutter mediante Firebase Emulator Suite

1. Prima di iniziare

In questo codelab, imparerai a utilizzare Firebase Emulator Suite con Flutter durante lo sviluppo locale. Imparerai a utilizzare l'autenticazione tramite email e password tramite Emulator Suite e a leggere e scrivere dati nell'emulatore Firestore. Infine, imparerai a importare ed esportare dati dagli emulatori, in modo da lavorare con gli stessi dati falsi ogni volta che torni allo sviluppo.

Prerequisiti

Questo codelab presuppone che tu abbia una certa esperienza con Flutter. In caso contrario, ti consigliamo di imparare prima le nozioni di base. I seguenti link sono utili:

Dovresti anche avere un po' di esperienza con Firebase, ma non importa se non hai mai aggiunto Firebase a un progetto Flutter. Se non hai familiarità con la console Firebase o non hai mai utilizzato Firebase, consulta prima i seguenti link:

Cosa creerai

Questo codelab ti guida nella creazione di una semplice applicazione di journaling. L'applicazione avrà una schermata di accesso e una schermata che ti consente di leggere le voci del diario passate e di crearne di nuove.

cd5c4753bbee8af.png 8cb4d21f656540bf.png

Obiettivi didattici

Imparerai a iniziare a utilizzare Firebase e a integrare e utilizzare Firebase Emulator Suite nel tuo flusso di lavoro di sviluppo Flutter. Verranno trattati i seguenti argomenti di Firebase:

Tieni presente che questi argomenti vengono trattati nella misura in cui sono necessari per coprire la suite di emulatori Firebase. Questo codelab si concentra sull'aggiunta di un progetto Firebase alla tua app Flutter e sullo sviluppo utilizzando Firebase Emulator Suite. Non ci saranno discussioni approfondite su Firebase Authentication o Firestore. Se non hai familiarità con questi argomenti, ti consigliamo di iniziare con il codelab Getting to Know Firebase for Flutter.

Che cosa ti serve

  • Conoscenza pratica di Flutter e dell'SDK installato
  • Editor di testo IntelliJ JetBrains o VS Code
  • Browser Google Chrome (o un altro target di sviluppo preferito per Flutter). Alcuni comandi del terminale in questo codelab presuppongono che tu stia eseguendo l'app su Chrome)

2. Crea e configura un progetto Firebase

La prima attività da completare è la creazione di un progetto Firebase nella console web di Firebase. La maggior parte di questo codelab si concentrerà su Emulator Suite, che utilizza un'interfaccia utente in esecuzione locale, ma devi prima configurare un progetto Firebase completo.

Crea un progetto Firebase

  1. Accedi alla console Firebase utilizzando il tuo Account Google.
  2. Fai clic sul pulsante per creare un nuovo progetto, quindi inserisci un nome per il progetto (ad esempio Firebase-Flutter-Codelab).
  3. Fai clic su Continua.
  4. Se richiesto, leggi e accetta i termini di Firebase, quindi fai clic su Continua.
  5. (Facoltativo) Attiva l'assistenza AI nella console Firebase (denominata "Gemini in Firebase").
  6. Per questo codelab non hai bisogno di Google Analytics, quindi disattiva l'opzione Google Analytics.
  7. Fai clic su Crea progetto, attendi il provisioning del progetto, poi fai clic su Continua.

Per saperne di più sui progetti Firebase, consulta Informazioni sui progetti Firebase.

Configura i prodotti Firebase

L'app che stai creando utilizza due prodotti Firebase disponibili per le app Flutter:

  • Firebase Authentication per consentire agli utenti di accedere alla tua app.
  • Cloud Firestore per salvare dati strutturati sul cloud e ricevere una notifica immediata quando i dati cambiano.

Questi due prodotti richiedono una configurazione speciale o devono essere attivati utilizzando la Console Firebase.

Attiva Cloud Firestore

L'app Flutter utilizza Cloud Firestore per salvare le voci del diario.

Abilita Cloud Firestore:

  1. Nella sezione Build della console Firebase, fai clic su Cloud Firestore.
  2. Fai clic su Crea database. 99e8429832d23fa3.png
  3. Seleziona l'opzione Avvia in modalità di test. Leggi l'esclusione di responsabilità relativa alle regole di sicurezza. La modalità di test ti consente di scrivere liberamente nel database durante lo sviluppo. Fai clic su Avanti. 6be00e26c72ea032.png
  4. Seleziona la posizione per il tuo database (puoi utilizzare quella predefinita). Tieni presente che questa località non potrà essere modificata in un secondo momento. 278656eefcfb0216.png
  5. Fai clic su Abilita.

3. Configurare l'app Flutter

Prima di iniziare, devi scaricare il codice iniziale e installare l'interfaccia a riga di comando di Firebase.

Recupera il codice di avvio

Clona il repository GitHub dalla riga di comando:

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

In alternativa, se hai installato lo strumento GitHub CLI:

gh repo clone flutter/codelabs flutter-codelabs

Il codice di esempio deve essere clonato nella directory flutter-codelabs, che contiene il codice per una raccolta di codelab. Il codice per questo codelab si trova in flutter-codelabs/firebase-emulator-suite.

La struttura delle directory in flutter-codelabs/firebase-emulator-suite è costituita da due progetti Flutter. Uno si chiama complete, che puoi consultare se vuoi andare avanti o fare un riferimento incrociato al tuo codice. L'altro progetto si chiama start.

Il codice con cui vuoi iniziare si trova nella directory flutter-codelabs/firebase-emulator-suite/start. Apri o importa la directory nell'IDE che preferisci.

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

Installa l'interfaccia a riga di comando di Firebase

La CLI Firebase fornisce strumenti per la gestione dei tuoi progetti Firebase. L'interfaccia a riga di comando è necessaria per utilizzare Emulator Suite, quindi dovrai installarla.

Esistono diversi modi per installare la CLI. Il modo più semplice, se utilizzi macOS o Linux, è eseguire questo comando dal terminale:

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

Dopo aver installato la CLI, devi eseguire l'autenticazione con Firebase.

  1. Accedi a Firebase utilizzando il tuo Account Google eseguendo questo comando:
firebase login
  1. Questo comando connette la tua macchina locale a Firebase e ti concede l'accesso ai tuoi progetti Firebase.
  1. Verifica che la CLI sia installata correttamente e abbia accesso al tuo account elencando i tuoi progetti Firebase. Esegui questo comando:
firebase projects:list
  1. L'elenco visualizzato deve essere uguale a quello dei progetti Firebase elencati nella console Firebase. Dovresti visualizzare almeno firebase-flutter-codelab.

Installa l'interfaccia a riga di comando FlutterFire

L'interfaccia a riga di comando FlutterFire è basata sull'interfaccia a riga di comando di Firebase e semplifica l'integrazione di un progetto Firebase con la tua app Flutter.

Innanzitutto, installa l'interfaccia a riga di comando:

dart pub global activate flutterfire_cli

Assicurati che la CLI sia stata installata. Esegui il seguente comando nella directory del progetto Flutter e assicurati che la CLI restituisca il menu di aiuto.

flutterfire --help

Utilizza l'interfaccia a riga di comando di Firebase e l'interfaccia a riga di comando FlutterFire per aggiungere il tuo progetto Firebase alla tua app Flutter

Con le due CLI installate, puoi configurare singoli prodotti Firebase (come Firestore), scaricare gli emulatori e aggiungere Firebase alla tua app Flutter con un paio di comandi del terminale.

Innanzitutto, completa la configurazione di Firebase eseguendo questo comando:

firebase init

Questo comando ti guiderà attraverso una serie di domande necessarie per configurare il progetto. Questi screenshot mostrano il flusso:

  1. Quando ti viene chiesto di selezionare le funzionalità, seleziona "Firestore" ed "Emulatori". Non è presente un'opzione di autenticazione, in quanto non utilizza una configurazione modificabile dai file di progetto Flutter. fe6401d769be8f53.png
  2. Successivamente, seleziona "Utilizza un progetto esistente" quando richiesto.

f11dcab439e6ac1e.png

  1. Ora seleziona il progetto che hai creato in un passaggio precedente: flutter-firebase-codelab.

3bdc0c6934991c25.png

  1. Successivamente, ti verrà posta una serie di domande sulla denominazione dei file che verranno generati. Ti suggerisco di premere "Invio" per ogni domanda per selezionare il valore predefinito. 9bfa2d507e199c59.png
  2. Infine, dovrai configurare gli emulatori. Seleziona Firestore e Authentication dall'elenco, poi premi "Invio" per ogni domanda sulle porte specifiche da utilizzare per ogni emulatore. Quando ti viene chiesto se vuoi utilizzare l'interfaccia utente dell'emulatore, devi selezionare l'opzione predefinita, ovvero Sì.

Al termine della procedura, dovresti vedere un output simile allo screenshot seguente.

Importante: l'output potrebbe essere leggermente diverso dal mio, come mostrato nello screenshot di seguito, perché l'ultima domanda sarà impostata su "No" per impostazione predefinita se hai già scaricato gli emulatori.

8544e41037637b07.png

Configura FlutterFire

Dopodiché, puoi utilizzare FlutterFire per generare il codice Dart necessario per utilizzare Firebase nella tua app Flutter.

flutterfire configure

Quando viene eseguito questo comando, ti verrà chiesto di selezionare il progetto Firebase che vuoi utilizzare e le piattaforme che vuoi configurare. In questo codelab, gli esempi utilizzano Flutter Web, ma puoi configurare il progetto Firebase in modo che utilizzi tutte le opzioni.

Gli screenshot seguenti mostrano le richieste a cui dovrai rispondere.

619b7aca6dc15472.png 301c9534f594f472.png

Questo screenshot mostra l'output alla fine del processo. Se hai familiarità con Firebase, noterai che non hai dovuto creare applicazioni nella console e che l'interfaccia a riga di comando di FlutterFire lo ha fatto per te.

12199a85ade30459.png

Aggiungi pacchetti Firebase all'app Flutter

L'ultimo passaggio della configurazione consiste nell'aggiungere i pacchetti Firebase pertinenti al tuo progetto Flutter. Nel terminale, assicurati di trovarti nella directory principale del progetto Flutter in flutter-codelabs/firebase-emulator-suite/start. Quindi, esegui i tre comandi seguenti:

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

Questi sono gli unici pacchetti che utilizzerai in questa applicazione.

4. Abilitazione degli emulatori Firebase

Finora, l'app Flutter e il progetto Firebase sono configurati per poter utilizzare gli emulatori, ma devi comunque indicare al codice Flutter di reindirizzare le richieste Firebase in uscita alle porte locali.

Innanzitutto, aggiungi il codice di inizializzazione di Firebase e il codice di configurazione dell'emulatore alla funzione main in 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());
}

Le prime righe di codice inizializzano Firebase. Quasi sempre, se utilizzi Firebase in un'app Flutter, devi iniziare chiamando WidgetsFlutterBinding.ensureInitialized e Firebase.initializeApp.

Successivamente, il codice che inizia con la riga if (kDebugMode) indica alla tua app di scegliere come target gli emulatori anziché un progetto Firebase di produzione. kDebugMode garantisce che il targeting degli emulatori avvenga solo se ti trovi in un ambiente di sviluppo. Poiché kDebugMode è un valore costante, il compilatore Dart sa di rimuovere completamente il blocco di codice in modalità di rilascio.

Avvia gli emulatori

Devi avviare gli emulatori prima di avviare l'app Flutter. Per prima cosa, avvia gli emulatori eseguendo questo comando nel terminale:

firebase emulators:start

Questo comando avvia gli emulatori ed espone le porte localhost con cui possiamo interagire. Quando esegui questo comando, dovresti visualizzare un output simile a questo:

bb7181eb70829606.png

Questo output indica quali emulatori sono in esecuzione e dove puoi visualizzarli. Per prima cosa, dai un'occhiata alla UI dell'emulatore all'indirizzo localhost:4000.

11563f4c7216de81.png

Questa è la home page della UI dell'emulatore locale. Elenca tutti gli emulatori disponibili e ciascuno è etichettato con lo stato attivo o disattivato.

5. L'emulatore Firebase Auth

Il primo emulatore che utilizzerai è l'emulatore di autenticazione. Inizia con l'emulatore di autenticazione facendo clic su "Vai all'emulatore" nella scheda Autenticazione nell'interfaccia utente. Vedrai una pagina simile a questa:

3c1bfded40733189.png

Questa pagina presenta delle somiglianze con la pagina della console web Auth. Contiene una tabella che elenca gli utenti come la console online e ti consente di aggiungerli manualmente. Una grande differenza è che l'unica opzione di metodo di autenticazione disponibile sugli emulatori è tramite email e password. È sufficiente per lo sviluppo locale.

Successivamente, esamineremo la procedura per aggiungere un utente all'emulatore Firebase Auth e poi accedere con questo utente tramite la UI di Flutter.

Aggiungere un utente

Fai clic sul pulsante "Aggiungi utente" e compila il modulo con queste informazioni:

  • Nome visualizzato: Dash
  • Email: dash@email.com
  • Password: dashword

Invia il modulo e vedrai che la tabella ora include un utente. Ora puoi aggiornare il codice per accedere con questo utente.

logged_out_view.dart

L'unico codice nel widget LoggedOutView che deve essere aggiornato si trova nel callback attivato quando un utente preme il pulsante di accesso. Aggiorna il codice in modo che abbia questo aspetto:

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'),
          ),
        ),
      ],
    ),
   ),
  );
 }
}

Il codice aggiornato sostituisce le stringhe TODO con l'email e la password che hai creato nell'emulatore di autenticazione. Nella riga successiva, la riga if(true) è stata sostituita da un codice che verifica se state.user è null. Il codice in AppClass fa più luce su questo aspetto.

app_state.dart

Devono essere aggiornate due parti del codice in AppState. Innanzitutto, assegna al membro della classe AppState.user il tipo User dal pacchetto firebase_auth, anziché il tipo Object.

Secondo, compila il metodo AppState.login come mostrato di seguito:

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

La definizione del tipo per l'utente ora è User?. La classe User proviene da Firebase Auth e fornisce le informazioni necessarie, ad esempio User.displayName, di cui parleremo tra poco.

Questo è il codice di base necessario per accedere a un utente con un indirizzo email e una password in Firebase Auth. Chiama FirebaseAuth per accedere, che restituisce un oggetto Future<UserCredential>. Al termine del futuro, questo codice controlla se è presente un User allegato a UserCredential. Se è presente un utente nell'oggetto credenziale, significa che l'utente ha eseguito l'accesso correttamente e la proprietà AppState.user può essere impostata. In caso contrario, si è verificato un errore e viene stampato.

Tieni presente che l'unica riga di codice in questo metodo specifica per questa app (anziché il codice FirebaseAuth generale) è la chiamata al metodo _listenForEntries, che verrà trattato nel passaggio successivo.

TODO: Action Icon – Reload your app, and then press the Login button when it renders. In questo modo l'app passa a una pagina con il messaggio "Bentornato, [nome della persona]" in alto. L'autenticazione deve funzionare, perché ti ha consentito di accedere a questa pagina, ma è necessario apportare un aggiornamento minore a logged_in_view.dart per visualizzare il nome effettivo dell'utente.

logged_in_view.dart

Modifica la prima riga nel metodo 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(
 // ...

Ora, questa riga recupera displayName dalla proprietà User dell'oggetto AppState. Questo displayName è stato impostato nell'emulatore quando hai definito il primo utente. Ora, quando accedi all'app, dovrebbe essere visualizzato il messaggio "Bentornato, Dash!" anziché TODO.

6. Leggi e scrivi dati nell'emulatore Firestore

Per prima cosa, dai un'occhiata all'emulatore Firestore. Nella home page dell'interfaccia utente dell'emulatore (localhost:4000), fai clic su "Vai all'emulatore" nella scheda Firestore. Dovrebbe avere il seguente aspetto:

Emulatore:

791fce7dc137910a.png

Console Firebase:

e0dde9aea34af050.png

Se hai esperienza con Firestore, noterai che questa pagina è simile alla pagina Firestore della console Firebase. Tuttavia, ci sono alcune differenze notevoli.

  1. Puoi cancellare tutti i dati con un solo tocco di un pulsante. Sarebbe pericoloso con i dati di produzione, ma è utile per un'iterazione rapida. Se stai lavorando a un nuovo progetto e il modello di dati cambia, è facile cancellarlo.
  2. È presente una scheda "Richieste". Questa scheda ti consente di osservare le richieste in arrivo effettuate a questo emulatore. Parleremo di questa scheda in modo più dettagliato tra poco.
  3. Non sono presenti schede per Regole, Indici o Utilizzo. Esiste uno strumento (descritto nella sezione successiva) che aiuta a scrivere regole di sicurezza, ma non puoi impostare regole di sicurezza per l'emulatore locale.

Per riassumere l'elenco, questa versione di Firestore fornisce più strumenti utili durante lo sviluppo e rimuove quelli necessari in produzione.

Scrivi in Firestore

Prima di parlare della scheda "Richieste" nell'emulatore, effettua una richiesta. Ciò richiede aggiornamenti del codice. Inizia collegando il modulo nell'app per scrivere una nuova voce del diario Entry in Firestore.

Il flusso generale per inviare un Entry è il seguente:

  1. L'utente compila il modulo e preme il pulsante Submit
  2. L'interfaccia utente chiama AppState.writeEntryToFirebase
  3. AppState.writeEntryToFirebase aggiunge una voce a Firebase

Nessuna parte del codice coinvolta nel passaggio 1 o 2 deve essere modificata. L'unico codice da aggiungere per il passaggio 3 verrà aggiunto nella classe AppState. Apporta la seguente modifica a AppState.writeEntryToFirebase.

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

Il codice nel metodo writeEntryToFirebase recupera un riferimento alla raccolta denominata "Entries" in Firestore. Aggiunge quindi una nuova voce, che deve essere di tipo Map<String, String>.

In questo caso, la raccolta "Entries" in Firestore non esisteva, quindi Firestore ne ha creata una.

Dopo aver aggiunto il codice, ricarica a caldo o riavvia l'app, accedi e vai alla visualizzazione EntryForm. Puoi compilare il modulo con i Strings che preferisci. Il campo Data accetta qualsiasi stringa, in quanto è stato semplificato per questo codelab. Non ha una convalida rigorosa e non si preoccupa in alcun modo degli oggetti DateTime.)

Premi Invia sul modulo. Non succederà nulla nell'app, ma potrai vedere la nuova voce nell'interfaccia utente dell'emulatore.

La scheda Richieste nell'emulatore Firestore

Nella UI, vai all'emulatore Firestore e guarda la scheda "Dati". Dovresti vedere che ora esiste una raccolta alla radice del database chiamata "Entries". Dovresti trovare un documento contenente le stesse informazioni che hai inserito nel modulo.

a978fb34fb8a83da.png

Ciò conferma che l'elemento AppState.writeEntryToFirestore ha funzionato e ora puoi esplorare ulteriormente la richiesta nella scheda Richieste. Fai clic su questa scheda.

Richieste dell'emulatore Firestore

Qui dovresti vedere un elenco simile a questo:

f0b37f0341639035.png

Puoi fare clic su uno qualsiasi di questi elementi dell'elenco e visualizzare molte informazioni utili. Fai clic sulla voce di elenco CREATE corrispondente alla tua richiesta per creare una nuova voce di diario. Vedrai una nuova tabella simile alla seguente:

385d62152e99aad4.png

Come accennato, l'emulatore Firestore fornisce strumenti per sviluppare le regole di sicurezza della tua app. Questa visualizzazione mostra esattamente la riga delle regole di sicurezza superata (o non superata, se è questo il caso) dalla richiesta. In un'app più solida, le regole di sicurezza possono crescere e avere più controlli di autorizzazione. Questa visualizzazione viene utilizzata per scrivere ed eseguire il debug di queste regole di autorizzazione.

Fornisce inoltre un modo semplice per esaminare ogni parte di questa richiesta, inclusi i metadati e i dati di autenticazione. Questi dati vengono utilizzati per scrivere regole di autorizzazione complesse.

Lettura da Firestore

Firestore utilizza la sincronizzazione per inviare i dati aggiornati ai dispositivi connessi. Nel codice Flutter, puoi ascoltare (o abbonarti) a raccolte e documenti Firestore e il tuo codice riceverà una notifica ogni volta che i dati cambiano. In questa app, l'ascolto degli aggiornamenti di Firestore viene eseguito nel metodo denominato AppState._listenForEntries.

Questo codice funziona in combinazione con StreamController e Stream chiamati rispettivamente AppState._entriesStreamController e AppState.entries. Il codice è già scritto, così come tutto il codice necessario nell'interfaccia utente per visualizzare i dati di Firestore.

Aggiorna il metodo _listenForEntries in modo che corrisponda al codice riportato di seguito:

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

Questo codice ascolta la raccolta "Entries" in Firestore. Quando Firestore comunica a questo client che sono presenti nuovi dati, li trasmette e il codice in _listenForEntries trasforma tutti i documenti secondari in un oggetto che la nostra app può utilizzare (Entry). Quindi, aggiunge queste voci a StreamController chiamato _entriesStreamController (che l'interfaccia utente sta ascoltando). Questo codice è l'unico aggiornamento richiesto.

Infine, ricorda che il metodo AppState.logIn effettua una chiamata a _listenForEntries, che inizia il processo di ascolto dopo che un utente ha eseguito l'accesso.

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

Ora esegui l'app. Dovrebbe avere il seguente aspetto:

b8a31c7a8900331.gif

7. Esportare e importare i dati nell'emulatore

Gli emulatori Firebase supportano l'importazione e l'esportazione dei dati. L'utilizzo delle importazioni e delle esportazioni ti consente di continuare lo sviluppo con gli stessi dati quando fai una pausa e poi riprendi. Puoi anche eseguire il commit dei file di dati in Git e gli altri sviluppatori con cui collabori avranno gli stessi dati su cui lavorare.

Esportare i dati dell'emulatore

Innanzitutto, esporta i dati dell'emulatore che hai già. Mentre gli emulatori sono ancora in esecuzione, apri una nuova finestra del terminale ed esegui questo comando:

firebase emulators:export ./emulators_data

.emulators_data è un argomento che indica a Firebase dove esportare i dati. Se la directory non esiste, viene creata. Puoi utilizzare il nome che preferisci per la directory.

Quando esegui questo comando, vedrai questo output nel terminale in cui l'hai eseguito:

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

Se passi alla finestra del terminale in cui sono in esecuzione gli emulatori, vedrai questo output:

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

Infine, se guardi nella directory del progetto, dovresti vedere una directory chiamata ./emulators_data, che contiene file JSON, tra gli altri file di metadati, con i dati che hai salvato.

Importare i dati dell'emulatore

Ora puoi importare questi dati nell'ambito del flusso di lavoro di sviluppo e riprendere da dove avevi interrotto.

Innanzitutto, interrompi gli emulatori se sono in esecuzione premendo CTRL+C nel terminale.

Successivamente, esegui il comando emulators:start che hai già visto, ma con un flag che indica i dati da importare:

firebase emulators:start --import ./emulators_data

Quando gli emulatori sono attivi, vai all'interfaccia utente dell'emulatore all'indirizzo localhost:4000 e dovresti visualizzare gli stessi dati con cui lavoravi in precedenza.

Esportare automaticamente i dati alla chiusura degli emulatori

Puoi anche esportare i dati automaticamente quando esci dagli emulatori, anziché ricordarti di esportarli al termine di ogni sessione di sviluppo.

Quando avvii gli emulatori, esegui il comando emulators:start con due flag aggiuntivi.

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

Voilà! I tuoi dati verranno ora salvati e ricaricati ogni volta che utilizzi gli emulatori per questo progetto. Puoi anche specificare una directory diversa come argomento di –export-on-exit flag, ma per impostazione predefinita verrà utilizzata la directory passata a –import.

Puoi anche utilizzare una qualsiasi combinazione di queste opzioni. Questa è la nota della documentazione: la directory di esportazione può essere specificata con questo flag: firebase emulators:start --export-on-exit=./saved-data. Se viene utilizzato --import, il percorso di esportazione è impostato sullo stesso valore; ad esempio: firebase emulators:start --import=./data-path --export-on-exit. Infine, se vuoi, passa percorsi di directory diversi ai flag --import e --export-on-exit.

8. Complimenti!

Hai completato Inizia a utilizzare l'emulatore Firebase e Flutter. Puoi trovare il codice completato per questo codelab nella directory "complete" su GitHub: Flutter Codelabs

Argomenti trattati

  • Configurazione di un'app Flutter per l'utilizzo di Firebase
  • Configurazione di un progetto Firebase
  • Interfaccia a riga di comando FlutterFire
  • Interfaccia a riga di comando di Firebase
  • Emulatore Firebase Authentication
  • Emulatore Firebase Firestore
  • Importazione ed esportazione dei dati dell'emulatore

Passaggi successivi

Scopri di più

Sparky è orgoglioso di te.

2a0ad195769368b1.gif