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. Scoprirai come utilizzare l'autenticazione email e password tramite Emulator Suite e come leggere e scrivere dati nell'emulatore Firestore. Infine, lavorerai con l'importazione e l'esportazione dei dati dagli emulatori per utilizzare gli stessi dati simulati ogni volta che torni allo sviluppo.

Prerequisiti

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

Dovresti anche avere un po' di esperienza con Firebase, ma non è un problema se non hai mai aggiunto Firebase a un progetto Flutter. Se non hai dimestichezza con la Console Firebase o se non hai mai utilizzato Firebase, consulta prima i seguenti link:

Che cosa creerai

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

cd5c4753bbee8af.png 8cb4d21f656540bf.png

Obiettivi didattici

Scoprirai come iniziare a utilizzare Firebase e come integrare e utilizzare Firebase Emulator Suite nel tuo flusso di lavoro di sviluppo Flutter. Verranno trattati questi argomenti relativi a Firebase:

Tieni presente che questi argomenti sono trattati nella misura in cui sono necessari per la suite di emulatori Firebase. Questo codelab è incentrato sull'aggiunta di un progetto Firebase alla tua app Flutter e sullo sviluppo tramite Firebase Emulator Suite. Non ci saranno discussioni approfondite su Firebase Authentication o Firestore. Se non hai dimestichezza con questi argomenti, ti consigliamo di iniziare con il codelab su Firebase per 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 la tua 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 localmente, ma devi prima configurare un progetto Firebase completo.

Crea un progetto Firebase

  1. Accedi alla console Firebase.
  2. Nella console Firebase, fai clic su Aggiungi progetto (o Crea un progetto) e inserisci un nome per il progetto Firebase (ad esempio "Firebase-Flutter-Codelab").

fe6aeab3b91965ed.png

  1. Fai clic sulle opzioni di creazione del progetto. Accetta i termini di Firebase, se richiesto. Salta la configurazione di Google Analytics perché non utilizzerai Analytics per questa app.

d1fcec48bf251eaa.png

Per saperne di più sui progetti Firebase, consulta Informazioni sui progetti 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 i dati strutturati sul cloud e ricevere una notifica istantanea quando i dati cambiano.

Questi due prodotti richiedono una configurazione speciale o devono essere abilitati 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 Crea 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 il disclaimer relativo 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 località per il database (puoi semplicemente utilizzare quella predefinita). Tieni presente che questa località non può 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 di avvio e installare l'interfaccia a riga di comando di Firebase.

Ottieni 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 cli di GitHub:

gh repo clone flutter/codelabs flutter-codelabs

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

La struttura di directory in flutter-codelabs/firebase-emulator-suite è composta da due progetti Flutter. Uno si chiama complete, a cui puoi fare riferimento se vuoi saltare 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 nel tuo IDE preferito.

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

Installa l'interfaccia a riga di comando di Firebase

L'interfaccia a riga di comando di Firebase fornisce strumenti per gestire i 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 l'interfaccia a riga di comando, 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 garantisce l'accesso ai tuoi progetti Firebase.
  1. Verifica che l'interfaccia a riga di comando sia installata correttamente e abbia accesso al tuo account elencando i tuoi progetti Firebase. Esegui questo comando:
firebase projects:list
  1. L'elenco visualizzato dovrebbe corrispondere a quello dei progetti Firebase elencati nella Console Firebase. Dovresti vedere 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 l'interfaccia a riga di comando sia stata installata. Esegui il seguente comando nella directory del progetto Flutter e assicurati che l'interfaccia a riga di comando stampi il menu di aiuto.

flutterfire --help

Utilizza Firebase CLI e FlutterFire CLI per aggiungere il progetto Firebase all'app Flutter

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

Per prima cosa, completa la configurazione di Firebase eseguendo il comando seguente:

firebase init

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

  1. Quando ti viene chiesto di selezionare le funzionalità, seleziona "Firestore" ed "Emulatori". Non è disponibile un'opzione di autenticazione, perché non utilizza una configurazione modificabile dai file di progetto Flutter. fe6401d769be8f53.png
  2. Quindi, 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 autenticazione dall'elenco, quindi 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 Sì.

Al termine del processo, dovresti vedere un output simile allo screenshot seguente.

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

8544e41037637b07.png

Configurare FlutterFire

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

flutterfire configure

Quando viene eseguito, 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 tuo progetto Firebase in modo da utilizzare tutte le opzioni.

Gli screenshot seguenti mostrano i prompt a cui dovrai rispondere.

619b7aca6dc15472.png 301c9534f594f472.png

Questo screenshot mostra l'output al termine del processo. Se hai dimestichezza con Firebase, noterai che non hai dovuto creare applicazioni nella console, perché è stato fatto per te dall'interfaccia a riga di comando FlutterFire.

12199a85ade30459.png

Aggiungere i pacchetti Firebase all'app Flutter

Il passaggio di configurazione finale consiste nell'aggiungere i pacchetti Firebase pertinenti al progetto Flutter. Nel terminale, assicurati di trovarti nella directory principale del progetto Flutter in flutter-codelabs/firebase-emulator-suite/start. Poi, esegui i tre comandi che seguono:

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. Attivazione degli emulatori Firebase

Finora, l'app Flutter e il progetto Firebase sono configurati per poter utilizzare gli emulatori, ma devi comunque dire 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 universalmente, se lavori con Firebase in un'app Flutter, ti consigliamo di 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 assicura 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 del tutto 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 con loro. Quando esegui il comando, dovresti visualizzare un output simile al seguente:

bb7181eb70829606.png

Questo output indica quali emulatori sono in esecuzione e dove puoi visualizzarli. Innanzitutto, controlla la UI dell'emulatore all'indirizzo localhost:4000.

11563f4c7216de81.png

Questa è la home page dell'interfaccia utente dell'emulatore locale. Vengono elencati tutti gli emulatori disponibili e ciascuno è contrassegnato con lo stato attivo o disattivato.

5. Emulatore Firebase Auth

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

3c1bfded40733189.png

Questa pagina è simile alla pagina della console web Auth. Contiene una tabella che elenca gli utenti, come nella console online, e ti consente di aggiungerne altri manualmente. Una grande differenza è che l'unico metodo di autenticazione disponibile sugli emulatori è tramite email e password. Questo è sufficiente per lo sviluppo locale.

A questo punto, analizzerai la procedura per aggiungere un utente all'emulatore Firebase Auth e per poi accedere all'utente tramite la UI di Flutter.

Aggiungere un utente

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

  • Nome visualizzato: Dash
  • Indirizzo 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 quell'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 il seguente 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 controlla se state.user è null. Il codice in AppClass chiarisce ulteriormente la questione.

app_state.dart

Due parti del codice in AppState devono essere aggiornate. Innanzitutto, assegna al membro di classe AppState.user il tipo User del pacchetto firebase_auth anziché il tipo Object.

In secondo luogo, 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 utente ora è User?. La classe User proviene da Firebase Auth e fornisce le informazioni necessarie, come User.displayName, che verrà discussa a breve.

Questo è il codice di base necessario per accedere a un utente con indirizzo email e password in Firebase Auth. Esegue una chiamata a FirebaseAuth per accedere, che restituisce un oggetto Future<UserCredential>. Al termine del futuro, questo codice controlla se è presente un User collegato a UserCredential. Se nell'oggetto credenziali è presente un utente, significa che un utente ha eseguito l'accesso ed è possibile impostare la proprietà AppState.user. 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é per il codice generale di FirebaseAuth) è la chiamata al metodo _listenForEntries, che verrà illustrata nel passaggio successivo.

DA FARE: Icona azione: ricarica la tua app e premi il pulsante Accedi quando viene visualizzata. L'app viene indirizzata a una pagina con il messaggio "Ti diamo il benvenuto, Persona!" in alto. L'autenticazione deve funzionare, perché è possibile accedere a questa pagina, ma è necessario apportare un piccolo aggiornamento a logged_in_view.dart per visualizzare il nome effettivo dell'utente.

logged_in_view.ARROW

Modifica la prima riga del 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. Quando accedi, l'app dovrebbe ora mostrare il messaggio "Ti diamo il benvenuto, Dash!" anziché TODO.

6. Leggere e scrivere 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 degne di nota.

  1. Puoi cancellare tutti i dati con un solo tocco. Questo sarebbe pericoloso con i dati di produzione, ma è utile per un'iterazione rapida. Se stai lavorando a un nuovo progetto e il tuo modello dei dati cambia, è facile eliminarlo.
  2. C'è la scheda "Richieste". Questa scheda ti consente di controllare le richieste in arrivo inviate a questo emulatore. Parleremo di questa scheda più in dettaglio 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 riepilogare, questa versione di Firestore fornisce più strumenti utili durante lo sviluppo e rimuove quelli necessari in produzione.

Scrivere in Firestore

Prima di esaminare la scheda "Richieste" nell'emulatore, effettua una richiesta. Questa operazione richiede aggiornamenti del codice. Inizia collegando il modulo nell'app per scrivere un nuovo diario Entry in Firestore.

Il flusso generale per inviare un Entry è:

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

Non è necessario modificare il codice utilizzato nel passaggio 1 o 2. L'unico codice che dovrà essere aggiunto per il passaggio 3 verrà aggiunto al corso 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 "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 l'app o riavviala, accedi e vai alla visualizzazione EntryForm. Puoi compilare il modulo con qualsiasi Strings che preferisci. Il campo Data accetta qualsiasi stringa, in quanto è stato semplificato per questo codelab. Non ha una convalida rigorosa né si preoccupa in alcun modo degli oggetti DateTime.

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

La scheda Richieste nell'emulatore Firestore

Nell'interfaccia utente, vai all'emulatore Firestore e controlla la scheda "Dati". Ora dovresti vedere una raccolta nella radice del database chiamata "Entries". Dovresti trovare un documento contenente le stesse informazioni che hai inserito nel modulo.

a978fb34fb8a83da.png

Ciò conferma che AppState.writeEntryToFirestore ha funzionato e ora puoi esaminare ulteriormente la richiesta nella scheda Richieste. Ora fai clic sulla scheda.

Richieste di emulatori Firestore

Dovresti vedere un elenco simile al seguente:

f0b37f0341639035.png

Puoi fare clic su uno degli elementi dell'elenco per visualizzare una serie di informazioni utili. Fai clic sull'elemento dell'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 in cui questa richiesta è passata (o non è riuscita, se è stato così). In un'app più solida, le regole di sicurezza possono crescere e avere più controlli di autorizzazione. Questa vista viene utilizzata per scrivere e eseguire il debug di queste regole di autorizzazione.

Fornisce inoltre un modo semplice per ispezionare 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 dei dati per inviare i dati aggiornati ai dispositivi connessi. Nel codice Flutter, puoi ascoltare (o iscriverti) a raccolte e documenti Firestore e il codice riceverà una notifica ogni volta che i dati cambiano. In questa app, l'ascolto degli aggiornamenti di Firestore viene eseguito nel metodo chiamato AppState._listenForEntries.

Questo codice funziona rispettivamente in combinazione con StreamController e Stream, denominati AppState._entriesStreamController e AppState.entries. Questo 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 disponibili nuovi dati, li passa e il codice in _listenForEntries trasforma tutti i suoi documenti secondari in un oggetto utilizzabile dalla nostra app (Entry). Poi aggiunge queste voci alla raccolta StreamController denominata _entriesStreamController (che l'interfaccia utente ascolta). Questo codice è l'unico aggiornamento richiesto.

Infine, ricorda che il metodo AppState.logIn effettua una chiamata a _listenForEntries, che avvia la procedura 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. L'app dovrebbe essere simile al seguente:

b8a31c7a8900331.gif

7. Esporta e importa dati nell'emulatore

Gli emulatori Firebase supportano l'importazione e l'esportazione dei dati. L'uso delle importazioni ed esportazioni ti consente di continuare lo sviluppo con gli stessi dati quando prendi una pausa dallo sviluppo 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 con 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 e inserisci il seguente 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 lo 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 cerchi nella directory del progetto, dovresti vedere una directory chiamata ./emulators_data, che contiene JSON file, tra gli altri file di metadati, con i dati che hai salvato.

Importa 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.

Quindi, esegui il comando emulators:start che hai già visto, ma con un flag che indica quali dati importare:

firebase emulators:start --import ./emulators_data

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

Esportare automaticamente i dati quando si chiudono gli 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

Ecco fatto. I dati verranno salvati e ricaricati ogni volta che lavori con gli emulatori per questo progetto. Puoi anche specificare una directory diversa come argomento per –export-on-exit flag, ma utilizzerà per impostazione predefinita la directory passata a –import.

Puoi anche utilizzare qualsiasi combinazione di queste opzioni. Questa è la nota dalla documentazione: è possibile specificare la directory di esportazione con questo flag: firebase emulators:start --export-on-exit=./saved-data. Se viene utilizzato --import, il percorso di esportazione predefinito è lo stesso, 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 la sezione Inizia a utilizzare Firebase Emulator e Flutter. Puoi trovare il codice completo di questo codelab nella directory "complete" su GitHub: Flutter Codelabs

Argomenti trattati

  • Configurare 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