Desarrollo local para 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 con correo electrónico y contraseña mediante Emulator Suite, y a leer y escribir datos en el emulador de Firestore. Por último, importarás y exportarás datos desde los emuladores para trabajar con los mismos datos falsos cada vez que regreses 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 experiencia en Firebase, pero no hay problema si nunca agregaste Firebase a un proyecto de Flutter. Si no estás familiarizado con Firebase console o si recién comienzas a usar Firebase, consulta primero los siguientes vínculos:

Qué crearás

Este codelab te guiará para que compiles una aplicación simple de Journaling. La aplicación tendrá una pantalla de acceso y una pantalla que te permitirá leer las entradas de diario anteriores y crear otras 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 utilizar Firebase Emulator Suite. Este codelab se enfoca en agregar un proyecto de Firebase a tu app de Flutter y desarrollar con Firebase Emulator Suite. No habrá debates detallados sobre Firebase Authentication ni Firestore. Si no conoces estos temas, te recomendamos comenzar con el codelab Conoce Firebase para Flutter.

Requisitos

  • Tener conocimientos prácticos de Flutter y la instalación del SDK
  • Editores de texto Intellij JetBrains o VS Code
  • El navegador Google Chrome (o tu segmento de desarrollo preferido para Flutter). En algunos comandos de terminal de este codelab, se supone 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. Gran parte de este codelab se enfocará en Emulator Suite, que usa una IU que se ejecuta localmente, pero primero debes configurar un proyecto completo de Firebase.

Crea un proyecto de Firebase

  1. Accede a Firebase console.
  2. En Firebase console, haz clic en Agregar proyecto (o Crear un proyecto) y escribe un nombre para tu proyecto de Firebase (por ejemplo, "Firebase-Flutter-Codelab").

fe6aeab3b91965ed.png

  1. Haz clic para avanzar por las opciones de creación del proyecto. Acepta las condiciones de Firebase si se te solicita. Omite la configuración de Google Analytics, ya que no utilizarás este servicio para esta aplicación.

d1fcec48bf251eaa.png

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

La app que compilas usa dos productos de Firebase que están disponibles para las 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 cambien los datos

Estos dos productos necesitan una configuración especial o se deben habilitar con Firebase console.

Habilite Cloud Firestore

La app de Flutter usa Cloud Firestore para guardar las entradas de 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 puede cambiar más adelante. 278656eefcfb0216.png
  5. Haz clic en Habilitar.

3. Configura la app de Flutter

Deberás descargar el código de partida y, luego, instalar Firebase CLI antes de comenzar.

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

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

gh repo clone flutter/codelabs flutter-codelabs

El código de muestra debe clonarse 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 es de dos proyectos de Flutter. Uno se llama complete, al que puedes referirte si deseas omitirlo o hacer referencias cruzadas de 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 a tu IDE preferido.

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

Instala Firebase CLI

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

Hay varias formas de instalar la CLI. La manera más sencilla, si usas MacOS o Linux, es ejecutar este comando desde tu 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. Crea una lista de tus proyectos de Firebase para probar que la CLI esté instalada correctamente y tenga 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 Firebase CLI y facilita la integración de un proyecto de Firebase a 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 dentro del directorio del proyecto de Flutter y asegúrate de que la CLI muestre 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 CLI instaladas, puedes configurar productos individuales de Firebase (como Firestore), descargar los emuladores y agregar Firebase a tu app de Flutter con solo algunos comandos de terminal.

Primero, ejecuta el siguiente comando para finalizar la configuración de Firebase:

firebase init

Este comando te guiará a través de una serie de preguntas necesarias para configurar tu proyecto. Estas capturas de pantalla muestran el flujo:

  1. Cuando se te solicite que selecciones funciones, selecciona “Firestore” y “Emuladores”. No incluye la opción Authentication, ya que no usa una configuración que se pueda modificar a partir de los archivos de tu proyecto de Flutter. fe6401d769be8f53.png
  2. Luego, selecciona "Usar un proyecto existente" cuando se te solicite.

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á una serie de preguntas sobre cómo asignar nombres a los archivos que se generarán. Te recomendamos presionar “Intro” en cada pregunta para seleccionar la configuración predeterminada. 9bfa2d507e199c59.png
  2. Por último, deberás configurar los emuladores. Selecciona Firestore y Authentication en la lista y, luego, presiona "Intro" para cada pregunta sobre los puertos específicos que se usarán para cada emulador. Debes seleccionar la opción predeterminada, Yes, cuando se te pregunte si deseas usar la IU del emulador.

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

Importante: Es posible que tu resultado sea un poco diferente al mío, como se muestra en la siguiente captura de pantalla, 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 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 quieres usar y las plataformas que quieres configurar. En los ejemplos de este codelab, se usa Flutter Web, pero puedes configurar tu proyecto de Firebase para usar todas las opciones.

Las siguientes capturas de pantalla 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 estás familiarizado con Firebase, notarás que no tuviste que crear aplicaciones en la consola y que la CLI de FlutterFire lo hizo por ti.

12199a85ade30459.png

Agrega paquetes de Firebase a la app de Flutter

El paso final de la configuración es agregar los paquetes de Firebase relevantes 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 utilizarás en esta aplicación.

4. Habilita los emuladores de Firebase

Hasta ahora, la app de Flutter y tu proyecto de Firebase están configurados para poder usar los emuladores, pero igualmente 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 en todos los casos, si trabajas con Firebase en una app de Flutter, debes llamar a WidgetsFlutterBinding.ensureInitialized y Firebase.initializeApp para comenzar.

A continuación, el código que comienza con la línea if (kDebugMode) le indica a tu app que se oriente a los emuladores en lugar de a un proyecto de producción de Firebase. kDebugMode garantiza que la orientación a los emuladores solo ocurra si estás en un entorno de desarrollo. Como 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 el siguiente comando 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

Este resultado te indica qué emuladores están en ejecución y adónde puedes acceder para verlos. Primero, consulta la IU del emulador en localhost:4000.

11563f4c7216de81.png

Esta es la página de inicio de la IU del emulador local. Enumera todos los emuladores disponibles, y cada uno está etiquetado con el estado activado o desactivado.

5. El emulador de Firebase Auth

El primer emulador que usarás es el de Authentication. Para comenzar con el emulador de Auth, haz clic en “Ir al emulador” en la tarjeta de autenticación de la IU y verás una página similar a la siguiente:

3c1bfded40733189.png

Esta página tiene similitudes con la página de la consola web de Auth. Tiene una tabla que enumera los usuarios, como la consola en línea, y te permite agregarlos manualmente. Una gran diferencia 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, repasarás el proceso para agregar un usuario al emulador de Firebase Auth y, luego, iniciar sesión con 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: Guion
  • 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 que debe actualizarse en el widget LoggedOutView es 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 con el correo electrónico y la contraseña que creaste en el emulador de autenticación. En la siguiente línea, se reemplazó la línea if(true) por un código que verifica si state.user es nulo. El código de AppClass es más claro 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.

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

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

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

TODO: Ícono de acción: vuelve a cargar tu app y, luego, presiona el botón Acceder cuando se procese. Esto hace que la app navegue a una página que dice "¡Te damos la bienvenida de nuevo, persona!" en la parte superior. La autenticación debe funcionar, ya que te permite navegar a esta página, pero se debe realizar una actualización menor en logged_in_view.dart para que se muestre el nombre real del usuario.

logged_in_view.dart

Cambia la primera línea en el 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, en lugar de TODO, tu app debería mostrar el mensaje "Welcome back, Dash!" cuando accedas.

6. Lee y escribe datos en el emulador de Firestore

Primero, revisa el emulador de Firestore. En la página principal de la IU del emulador (localhost:4000), haz clic en "Go to emulator" 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 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 datos de producción, pero es útil para una iteración rápida. Si estás trabajando en un proyecto nuevo y tu modelo de datos cambia, es fácil despejar.
  2. Hay una pestaña "Solicitudes". Esta pestaña te permite ver las solicitudes entrantes realizadas a este emulador. Analizaremos esta pestaña con más detalle más adelante.
  3. No hay pestañas para Reglas, Índices ni 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.

Para resumir, esta versión de Firestore proporciona más herramientas útiles durante el desarrollo y quita las herramientas necesarias en la producción.

Escribe en Firestore

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

El flujo de alto nivel para enviar un Entry es el siguiente:

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

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

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

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

Una vez agregado el código, vuelve a cargar o reinicia tu app en caliente, accede y navega a la vista EntryForm. Puedes completar el formulario con el Strings que quieras. (El campo Fecha tomará 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 puedes ver la nueva entrada en la IU del emulador.

La pestaña Solicitudes en el emulador de Firestore

En la IU, navega al emulador de Firestore y consulta la pestaña “Datos”. Deberías ver que ahora hay una Colección en la raíz de tu base de datos llamada “Entradas”. Debe tener un documento con la misma información que ingresaste en el formulario.

854360866666445.png

Esto confirma que AppState.writeEntryToFirestore funcionó. Ahora puedes explorar más a fondo la solicitud en la pestaña Solicitudes. Haz clic en esa pestaña ahora.

Solicitudes del emulador de Firestore

Aquí, deberías ver una lista similar a la siguiente:

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 la lista CREATE que corresponda a tu solicitud para crear una nueva entrada de diario. Verás una tabla nueva con el siguiente aspecto:

385d62152e99aad4.png

Como mencionamos, el emulador de Firestore proporciona herramientas para desarrollar las reglas de seguridad de tu app. Esta vista muestra exactamente qué línea de las reglas de seguridad aprobó esta solicitud (o falló, si ese fue el caso). En una app más sólida, 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 manera fácil 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.

Lee desde Firestore

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

Este código funciona en conjunto con 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 escucha la colección "Entradas" en Firestore. Cuando Firestore notifica a este cliente que hay datos nuevos, los pasa y el código de _listenForEntries cambia todos sus documentos secundarios a un objeto que nuestra app puede usar (Entry). Luego, agrega esas entradas a la StreamController llamada _entriesStreamController (que la IU escucha). Este código es la única actualización obligatoria.

Por último, recuerda que el método AppState.logIn realiza una llamada a _listenForEntries, que inicia 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 de la siguiente manera:

b8a31c7a8900331.gif

7. Importa y exporta datos al emulador

Los emuladores de Firebase admiten la importación y exportación de datos. El uso de las importaciones y exportaciones te permite continuar el desarrollo con los mismos datos cuando te tomas un descanso del desarrollo y, luego, lo reanudas. También puedes confirmar los archivos de datos en Git; de este modo, los desarrolladores con los que trabajas también tendrán los mismos datos.

Cómo exportar datos del emulador

Primero, exporta los datos del emulador que ya tienes. Mientras se están ejecutando los emuladores, abre una nueva ventana de terminal y, luego, ingresa el siguiente comando:

firebase emulators:export ./emulators_data

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

Cuando ejecutes este comando, verás este resultado en la terminal donde ejecutaste el comando:

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 terminal donde se ejecutan los emuladores, verás el siguiente 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 buscas en 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.

Cómo importar datos del emulador

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

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

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

firebase emulators:start --import ./emulators_data

Cuando los emuladores estén activos, navega a la IU del emulador en localhost:4000. Deberías ver los mismos datos con los que estabas trabajando anteriormente.

Cómo exportar datos automáticamente al cerrar los emuladores

También puedes exportar datos automáticamente cuando sales 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

¡Ya está! Tus datos se guardarán y se volverán a cargar cada vez que trabajes con los emuladores de este proyecto. También puedes especificar un directorio diferente como argumento de –export-on-exit flag, pero lo hará de forma predeterminada en el directorio que se pasa a –import.

También puedes usar cualquier combinación de estas opciones. Esta es la nota de los documentos: 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 la misma. Por ejemplo: firebase emulators:start --import=./data-path --export-on-exit. Por último, si lo deseas, pasa diferentes rutas de acceso del directorio a las marcas --import y --export-on-exit.

8. ¡Felicitaciones!

Completaste la capacitación para comenzar a usar el emulador de Firebase y Flutter. Puedes encontrar el código completo de este codelab en el directorio "complete" de 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
  • Cómo importar y exportar datos del emulador

Próximos pasos

Más información

Sparky está orgulloso de ti

2a0ad195769368b1.gif