Desarrollo local de tus apps de Flutter con Firebase Emulator Suite

1. Antes de comenzar

En este codelab, aprenderás a usar Firebase Emulator Suite con Flutter durante el desarrollo local. Aprenderás a usar la autenticación por correo electrónico y contraseña a través de Emulator Suite, y a leer y escribir datos en el emulador de Firestore. Por último, trabajarás con la importación y exportación de datos desde los emuladores para trabajar con los mismos datos simulados cada vez que vuelvas al desarrollo.

Requisitos previos

En este codelab, se presupone que tienes experiencia en Flutter. Si no es así, te recomendamos que primero aprendas los conceptos básicos. Los siguientes vínculos son útiles:

También debes tener cierta experiencia con Firebase, pero no hay problema si nunca agregaste Firebase a un proyecto de Flutter. Si no conoces Firebase console o es la primera vez que usas Firebase, consulta primero los siguientes vínculos:

Qué crearás

En este codelab, se te guiará para que compiles una aplicación de diario simple. La aplicación tendrá una pantalla de acceso y una pantalla que te permitirá leer entradas anteriores del diario y crear nuevas.

cd5c4753bbee8af.png 8cb4d21f656540bf.png

Qué aprenderás

Aprenderás a comenzar a usar Firebase y a integrar y usar Firebase Emulator Suite en tu flujo de trabajo de desarrollo de Flutter. Se tratarán los siguientes temas de Firebase:

Ten en cuenta que estos temas se abordan en la medida en que son necesarios para cubrir Firebase Emulator Suite. En este codelab, nos enfocaremos en agregar un proyecto de Firebase a tu app de Flutter y en el desarrollo con Firebase Emulator Suite. No habrá debates detallados sobre Firebase Authentication ni Firestore. Si no conoces estos temas, te recomendamos que comiences con el codelab de Introducción a Firebase para Flutter.

Requisitos

  • Conocimiento práctico de Flutter y el SDK instalado
  • Editores de texto de IntelliJ JetBrains o VS Code
  • Navegador Google Chrome (o tu otro destino de desarrollo preferido para Flutter) Algunos comandos de terminal de este codelab supondrán que ejecutas tu app en Chrome.

2. Crea y configura un proyecto de Firebase

La primera tarea que deberás completar es crear un proyecto de Firebase en la consola web de Firebase. La mayor parte de este codelab se centrará en Emulator Suite, que usa una IU que se ejecuta de forma local, pero primero debes configurar un proyecto completo de Firebase.

Crea un proyecto de Firebase

  1. Accede a la consola de Firebase con tu Cuenta de Google.
  2. Haz clic en el botón para crear un proyecto nuevo y, luego, ingresa un nombre (por ejemplo, Firebase-Flutter-Codelab).
  3. Haz clic en Continuar.
  4. Si se te solicita, revisa y acepta las Condiciones de Firebase y, luego, haz clic en Continuar.
  5. (Opcional) Habilita la asistencia de IA en Firebase console (llamada "Gemini en Firebase").
  6. Para este codelab, no necesitas Google Analytics, por lo que debes desactivar la opción de Google Analytics.
  7. Haz clic en Crear proyecto, espera a que se aprovisione y, luego, haz clic en Continuar.

Para obtener más información sobre los proyectos de Firebase, consulta Información sobre los proyectos de Firebase.

Configura los productos de Firebase

La app que estás compilando usa dos productos de Firebase que están disponibles para apps de Flutter:

  • Firebase Authentication para permitir que los usuarios accedan a tu app
  • Cloud Firestore, para guardar datos estructurados en la nube y recibir notificaciones instantáneas cuando se modifiquen los datos

Estos dos productos necesitan una configuración especial o deben habilitarse a través de Firebase console.

Habilita Cloud Firestore

La app de Flutter usa Cloud Firestore para guardar las entradas del diario.

Habilita Cloud Firestore:

  1. En la sección Compilación de Firebase console, haz clic en Cloud Firestore.
  2. Haz clic en Crear base de datos. 99e8429832d23fa3.png
  3. Selecciona la opción Comenzar en modo de prueba. Lee la renuncia de responsabilidad sobre las reglas de seguridad. El modo de prueba garantiza que puedas escribir con libertad en la base de datos durante el desarrollo. Haz clic en Siguiente. 6be00e26c72ea032.png
  4. Selecciona la ubicación de tu base de datos (puedes usar la predeterminada). Ten en cuenta que esta ubicación no se podrá cambiar más adelante. 278656eefcfb0216.png
  5. Haz clic en Habilitar.

3. Configura la app de Flutter

Antes de comenzar, deberás descargar el código de inicio e instalar Firebase CLI.

Obtén el código de partida

Clona el repositorio de GitHub desde la línea de comandos:

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

Como alternativa, si tienes instalada la herramienta de CLI de GitHub, haz lo siguiente:

gh repo clone flutter/codelabs flutter-codelabs

El código de muestra se debe clonar en el directorio flutter-codelabs, que contiene el código para una colección de codelabs. El código de este codelab se encuentra en flutter-codelabs/firebase-emulator-suite.

La estructura de directorios en flutter-codelabs/firebase-emulator-suite consta de dos proyectos de Flutter. Uno se llama complete, al que puedes hacer referencia si quieres avanzar o comparar tu propio código. El otro proyecto se llama start.

El código con el que quieres comenzar se encuentra en el directorio flutter-codelabs/firebase-emulator-suite/start. Abre o importa ese directorio en el IDE que prefieras.

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

Instala Firebase CLI

Firebase CLI proporciona herramientas para administrar tus proyectos de Firebase. La CLI es necesaria para usar Emulator Suite, por lo que deberás instalarla.

Existen varias formas de instalar la CLI. Si usas macOS o Linux, la forma más sencilla es ejecutar este comando desde la terminal:

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

Después de instalar la CLI, debes autenticarte con Firebase.

  1. Accede a Firebase con tu Cuenta de Google ejecutando el siguiente comando:
firebase login
  1. Este comando conecta tu máquina local a Firebase y te otorga acceso a tus proyectos de Firebase.
  1. Enumera tus proyectos de Firebase para probar que se instaló correctamente la CLI y que tienes acceso a tu cuenta. Ejecuta el siguiente comando:
firebase projects:list
  1. La lista que se muestra debe ser la misma que los proyectos de Firebase enumerados en Firebase console. Deberías ver al menos firebase-flutter-codelab.

Instala la CLI de FlutterFire

La CLI de FlutterFire se basa en la CLI de Firebase y facilita la integración de un proyecto de Firebase con tu app de Flutter.

Primero, instala la CLI:

dart pub global activate flutterfire_cli

Asegúrate de que se haya instalado la CLI. Ejecuta el siguiente comando en el directorio del proyecto de Flutter y asegúrate de que la CLI genere el menú de ayuda.

flutterfire --help

Usa Firebase CLI y FlutterFire CLI para agregar tu proyecto de Firebase a tu app de Flutter

Con las dos CLIs instaladas, puedes configurar productos individuales de Firebase (como Firestore), descargar los emuladores y agregar Firebase a tu app de Flutter con solo un par de comandos de la terminal.

Primero, ejecuta lo siguiente para completar la configuración de Firebase:

firebase init

Este comando te guiará a través de una serie de preguntas necesarias para configurar tu proyecto. En estas capturas de pantalla, se muestra el flujo:

  1. Cuando se te solicite que selecciones funciones, elige “Firestore” y “Emuladores”. (No hay una opción de Authentication, ya que no usa una configuración que se pueda modificar desde los archivos de tu proyecto de Flutter). fe6401d769be8f53.png
  2. A continuación, cuando se te solicite, selecciona "Usar un proyecto existente".

f11dcab439e6ac1e.png

  1. Ahora, selecciona el proyecto que creaste en un paso anterior: flutter-firebase-codelab.

3bdc0c6934991c25.png

  1. A continuación, se te harán una serie de preguntas sobre cómo nombrar los archivos que se generarán. Te sugiero que presiones "Intro" para cada pregunta y seleccionar la respuesta predeterminada. 9bfa2d507e199c59.png
  2. Por último, deberás configurar los emuladores. Selecciona Firestore y Authentication en la lista, y luego presiona "Intro" para responder cada pregunta sobre los puertos específicos que se usarán para cada emulador. Cuando se te pregunte si quieres usar la IU del emulador, debes seleccionar la opción predeterminada, Sí.

Al final del proceso, deberías ver un resultado similar al de la siguiente captura de pantalla.

Importante: Es posible que tu resultado sea ligeramente diferente al mío, como se muestra en la captura de pantalla a continuación, ya que la pregunta final se establecerá de forma predeterminada en "No" si ya descargaste los emuladores.

8544e41037637b07.png

Configura FlutterFire

A continuación, puedes usar FlutterFire para generar el código de Dart necesario para usar Firebase en tu app de Flutter.

flutterfire configure

Cuando ejecutes este comando, se te pedirá que selecciones el proyecto de Firebase que deseas usar y las plataformas que deseas configurar. En este codelab, los ejemplos usan Flutter Web, pero puedes configurar tu proyecto de Firebase para usar todas las opciones.

En las siguientes capturas de pantalla, se muestran las indicaciones que deberás responder.

619b7aca6dc15472.png 301c9534f594f472.png

En esta captura de pantalla, se muestra el resultado al final del proceso. Si conoces Firebase, notarás que no tuviste que crear aplicaciones en la consola, ya que FlutterFire CLI lo hizo por ti.

12199a85ade30459.png

Agrega paquetes de Firebase a la app de Flutter

El último paso de configuración es agregar los paquetes relevantes de Firebase a tu proyecto de Flutter. En la terminal, asegúrate de estar en la raíz del proyecto de Flutter en flutter-codelabs/firebase-emulator-suite/start. Luego, ejecuta los tres comandos siguientes:

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

Estos son los únicos paquetes que usarás en esta aplicación.

4. Cómo habilitar los emuladores de Firebase

Hasta ahora, la app de Flutter y tu proyecto de Firebase están configurados para poder usar los emuladores, pero aún debes indicarle al código de Flutter que redireccione las solicitudes salientes de Firebase a los puertos locales.

Primero, agrega el código de inicialización de Firebase y el código de configuración del emulador a la función main en 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());
}

Las primeras líneas de código inicializan Firebase. Casi siempre, si trabajas con Firebase en una app de Flutter, debes comenzar llamando a WidgetsFlutterBinding.ensureInitialized y Firebase.initializeApp.

Luego, el código que comienza con la línea if (kDebugMode) le indica a tu app que debe dirigirse a los emuladores en lugar de a un proyecto de Firebase de producción. kDebugMode garantiza que la segmentación para los emuladores solo se realizará si estás en un entorno de desarrollo. Dado que kDebugMode es un valor constante, el compilador de Dart sabe que debe quitar ese bloque de código por completo en el modo de lanzamiento.

Inicia los emuladores

Debes iniciar los emuladores antes de iniciar la app de Flutter. Primero, inicia los emuladores ejecutando lo siguiente en la terminal:

firebase emulators:start

Este comando inicia los emuladores y expone los puertos localhost con los que podemos interactuar. Cuando ejecutes ese comando, deberías ver un resultado similar al siguiente:

bb7181eb70829606.png

En este resultado, se indica qué emuladores se están ejecutando y dónde puedes verlos. Primero, consulta la IU del emulador en localhost:4000.

11563f4c7216de81.png

Esta es la página principal de la IU del emulador local. En ella, se enumeran todos los emuladores disponibles, y cada uno está etiquetado con el estado de encendido o apagado.

5. El emulador de Firebase Auth

El primer emulador que usarás es el emulador de Authentication. Para comenzar con el emulador de Auth, haz clic en "Ir al emulador" en la tarjeta Authentication de la IU. Verás una página como la siguiente:

3c1bfded40733189.png

Esta página es similar a la página de la consola web de Auth. Tiene una tabla en la que se enumeran los usuarios, como la consola en línea, y te permite agregar usuarios de forma manual. Una gran diferencia aquí es que la única opción de método de autenticación disponible en los emuladores es a través de correo electrónico y contraseña. Esto es suficiente para el desarrollo local.

A continuación, verás el proceso para agregar un usuario al emulador de Firebase Auth y, luego, acceder a ese usuario a través de la IU de Flutter.

Agrega un usuario

Haz clic en el botón "Agregar usuario" y completa el formulario con la siguiente información:

  • Nombre visible: Dash
  • Correo electrónico: dash@email.com
  • Contraseña: dashword

Envía el formulario y verás que la tabla ahora incluye un usuario. Ahora puedes actualizar el código para acceder con ese usuario.

logged_out_view.dart

El único código del widget LoggedOutView que se debe actualizar se encuentra en la devolución de llamada que se activa cuando un usuario presiona el botón de acceso. Actualiza el código para que se vea de la siguiente manera:

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

El código actualizado reemplaza las cadenas TODO por el correo electrónico y la contraseña que creaste en el emulador de autenticación. En la siguiente línea, la línea if(true) se reemplazó por código que verifica si state.user es nulo. El código en AppClass arroja más luz sobre este tema.

app_state.dart

Se deben actualizar dos partes del código en AppState. Primero, asigna al miembro de la clase AppState.user el tipo User del paquete firebase_auth, en lugar del tipo Object.

En segundo lugar, completa el método AppState.login como se muestra a continuación:

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 definición de tipo para el usuario ahora es User?. Esa clase User proviene de Firebase Authentication y proporciona la información necesaria, como User.displayName, que se analizará en breve.

Este es el código básico necesario para acceder a un usuario con un correo electrónico y una contraseña en Firebase Auth. Realiza una llamada a FirebaseAuth para acceder, lo que devuelve un objeto Future<UserCredential>. Cuando se completa el futuro, este código verifica si hay un User adjunto al UserCredential. Si hay un usuario en el objeto de credencial, significa que el usuario accedió correctamente y se puede establecer la propiedad AppState.user. Si no hay, se produjo un error y se imprimió.

Ten en cuenta que la única línea de código de este método que es específica de esta app (en lugar de código general de FirebaseAuth) es la llamada al método _listenForEntries, que se abordará en el siguiente paso.

PENDIENTE: Ícono de acción: Vuelve a cargar tu app y, luego, presiona el botón de acceso cuando se renderice. Esto hace que la app navegue a una página que dice "¡Bienvenido/a!" en la parte superior. La autenticación debe funcionar, ya que te permitió navegar a esta página, pero se debe realizar una actualización menor en logged_in_view.dart para mostrar el nombre real del usuario.

logged_in_view.dart

Cambia la primera línea del método 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(
 // ...

Ahora, esta línea toma el displayName de la propiedad User en el objeto AppState. Este displayName se configuró en el emulador cuando definiste tu primer usuario. Ahora, tu app debería mostrar el mensaje "¡Bienvenido de nuevo, Dash!" cuando accedas, en lugar de TODO.

6. Cómo leer y escribir datos en el emulador de Firestore

Primero, consulta el emulador de Firestore. En la página principal de la IU del emulador (localhost:4000), haz clic en "Ir al emulador" en la tarjeta de Firestore. Se verá de la siguiente manera:

Emulador:

791fce7dc137910a.png

Firebase console:

e0dde9aea34af050.png

Si tienes experiencia con Firestore, notarás que esta página es similar a la página de Firestore de Firebase console. Sin embargo, existen algunas diferencias notables.

  1. Puedes borrar todos los datos con solo presionar un botón. Esto sería peligroso con los datos de producción, pero es útil para la iteración rápida. Si estás trabajando en un proyecto nuevo y tu modelo de datos cambia, es fácil borrarlo.
  2. Hay una pestaña "Solicitudes". Esta pestaña te permite ver las solicitudes entrantes que se realizan a este emulador. Analizaré esta pestaña con más detalle en un momento.
  3. No hay pestañas para las reglas, los índices ni el uso. Hay una herramienta (que se analiza en la siguiente sección) que ayuda a escribir reglas de seguridad, pero no puedes establecer reglas de seguridad para el emulador local.

En resumen, esta versión de Firestore proporciona más herramientas útiles durante el desarrollo y quita las herramientas que se necesitan en la producción.

Escribe en Firestore

Antes de analizar la pestaña "Requests" del emulador, primero realiza una solicitud. Esto requiere actualizaciones de código. Comienza por conectar el formulario de la app para escribir un nuevo diario Entry en Firestore.

El flujo general para enviar un Entry es el siguiente:

  1. El usuario completa el formulario y presiona el botón Submit.
  2. La IU llama a AppState.writeEntryToFirebase
  3. AppState.writeEntryToFirebase agrega una entrada a Firebase

No es necesario cambiar nada del código involucrado en los pasos 1 o 2. El único código que se debe agregar para el paso 3 se agregará en la clase AppState. Realiza el siguiente cambio en 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,
   });
 }
 // ...
}

El código del método writeEntryToFirebase toma una referencia a la colección llamada "Entries" en Firestore. Luego, agrega una entrada nueva, que debe ser de tipo Map<String, String>.

En este caso, la colección "Entries" no existía en Firestore, por lo que Firestore creó una.

Con ese código agregado, realiza una recarga en caliente o reinicia tu app, accede y navega a la vista EntryForm. Puedes completar el formulario con el Strings que desees. (El campo Fecha aceptará cualquier cadena, ya que se simplificó para este codelab. No tiene una validación sólida ni se preocupa por los objetos DateTime de ninguna manera).

Presiona Enviar en el formulario. No sucederá nada en la app, pero podrás ver la entrada nueva en la IU del emulador.

La pestaña de solicitudes en el emulador de Firestore

En la IU, navega al emulador de Firestore y observa la pestaña "Datos". Deberías ver que ahora hay una colección en la raíz de tu base de datos llamada "Entradas". Debería haber un documento que contenga la misma información que ingresaste en el formulario.

a978fb34fb8a83da.png

Esto confirma que AppState.writeEntryToFirestore funcionó y, ahora, puedes explorar más la solicitud en la pestaña Requests. Haz clic en esa pestaña ahora.

Solicitudes del emulador de Firestore

Aquí, deberías ver una lista similar a esta:

f0b37f0341639035.png

Puedes hacer clic en cualquiera de esos elementos de la lista y ver bastante información útil. Haz clic en el elemento de lista CREATE que corresponde a tu solicitud para crear una nueva entrada de registro. Verás una tabla nueva como la siguiente:

385d62152e99aad4.png

Como se mencionó, el emulador de Firestore proporciona herramientas para desarrollar las reglas de seguridad de tu app. En esta vista, se muestra exactamente qué línea de tus reglas de seguridad aprobó (o rechazó, si ese fue el caso) esta solicitud. En una app más robusta, las reglas de seguridad pueden crecer y tener varias verificaciones de autorización. Esta vista se usa para ayudar a escribir y depurar esas reglas de autorización.

También proporciona una forma sencilla de inspeccionar cada parte de esta solicitud, incluidos los metadatos y los datos de autenticación. Estos datos se usan para escribir reglas de autorización complejas.

Lectura desde Firestore

Firestore usa la sincronización de datos para enviar datos actualizados a los dispositivos conectados. En el código de Flutter, puedes detectar (o suscribirte a) colecciones y documentos de Firestore, y tu código recibirá notificaciones cada vez que cambien los datos. En esta app, la detección de actualizaciones de Firestore se realiza en el método llamado AppState._listenForEntries.

Este código funciona en conjunto con los StreamController y Stream llamados AppState._entriesStreamController y AppState.entries, respectivamente. Ese código ya está escrito, al igual que todo el código necesario en la IU para mostrar los datos de Firestore.

Actualiza el método _listenForEntries para que coincida con el siguiente código:

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

Este código detecta cambios en la colección "Entries" de Firestore. Cuando Firestore notifica a este cliente que hay datos nuevos, pasa esos datos y el código en _listenForEntries cambia todos sus documentos secundarios a un objeto que nuestra app puede usar (Entry). Luego, agrega esas entradas al StreamController llamado _entriesStreamController (al que la IU está escuchando). Este código es la única actualización necesaria.

Por último, recuerda que el método AppState.logIn llama a _listenForEntries, que comienza el proceso de escucha después de que un usuario accede.

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

Ahora, ejecuta la app. Debería verse así:

b8a31c7a8900331.gif

7. Importa y exporta datos al emulador

Los emuladores de Firebase admiten la importación y exportación de datos. Usar las importaciones y exportaciones te permite continuar con el desarrollo con los mismos datos cuando te tomas un descanso y luego lo reanudas. También puedes confirmar archivos de datos en Git, y los otros desarrolladores con los que trabajes tendrán los mismos datos para trabajar.

Cómo exportar datos del emulador

Primero, exporta los datos del emulador que ya tienes. Mientras los emuladores siguen en ejecución, abre una nueva ventana de terminal y escribe el siguiente comando:

firebase emulators:export ./emulators_data

.emulators_data es un argumento que le indica a Firebase dónde exportar los datos. Si el directorio no existe, se creará. Puedes usar el nombre que quieras para ese directorio.

Cuando ejecutes este comando, verás el siguiente resultado en la terminal en la que lo ejecutaste:

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

Si cambias a la ventana de la terminal en la que se están ejecutando los emuladores, verás este resultado:

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

Por último, si observas el directorio de tu proyecto, deberías ver un directorio llamado ./emulators_data, que contiene archivos JSON, entre otros archivos de metadatos, con los datos que guardaste.

Importa datos del emulador

Ahora puedes importar esos datos como parte de tu flujo de trabajo de desarrollo y comenzar donde lo dejaste.

Primero, detén los emuladores si se están ejecutando presionando CTRL+C en la terminal.

A continuación, ejecuta el comando emulators:start que ya viste, pero con una marca que le indique qué datos importar:

firebase emulators:start --import ./emulators_data

Cuando los emuladores estén en funcionamiento, navega a la IU del emulador en localhost:4000 y deberías ver los mismos datos con los que trabajaste anteriormente.

Exporta datos automáticamente cuando se cierran los emuladores

También puedes exportar datos automáticamente cuando salgas de los emuladores, en lugar de recordar exportar los datos al final de cada sesión de desarrollo.

Cuando inicies los emuladores, ejecuta el comando emulators:start con dos marcas adicionales.

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

¡Listo! Tus datos ahora se guardarán y volverán a cargar cada vez que trabajes con los emuladores de este proyecto. También puedes especificar un directorio diferente como argumento para –export-on-exit flag, pero se establecerá de forma predeterminada el directorio que se pasó a –import.

También puedes usar cualquier combinación de estas opciones. Esta es la nota de la documentación: El directorio de exportación se puede especificar con la marca firebase emulators:start --export-on-exit=./saved-data. Si se usa --import, la ruta de exportación predeterminada es igual; por ejemplo: firebase emulators:start --import=./data-path --export-on-exit. Por último, si lo deseas, pasa diferentes rutas de directorio a las marcas --import y --export-on-exit.

8. ¡Felicitaciones!

Completaste el instructivo Get up and running with Firebase emulator and Flutter. Puedes encontrar el código completo de este codelab en el directorio "complete" en GitHub: Codelabs de Flutter

Temas abordados

  • Cómo configurar una app de Flutter para usar Firebase
  • Configura un proyecto de Firebase
  • CLI de FlutterFire
  • Firebase CLI
  • Emulador de Firebase Authentication
  • Emulador de Firebase Firestore
  • Importar y exportar datos del emulador

Próximos pasos

Más información

Sparky está orgulloso de ti.

2a0ad195769368b1.gif