Contenido del curso
Fundamentos de BLoC y Firebase
Navegación y Autenticación
- 10

BlocProvider y RepositoryProvider en Flutter
06:24 min - 11

Pantalla de login con BlocListener en Flutter
11:12 min - 12

Creación y Validación de Formularios en Flutter con Firebase
17:43 min - 13

Rutas en Flutter con GoRouter y Firebase Auth
15:46 min - 14

Conectar botón de login con Firebase Auth
11:34 min - 15

Cómo implementar logout con Firebase y GoRouter
10:30 min
Diseño Avanzado en iOS
Integración con Firestore usando BLoC
- 18

Modelo e repositório Firestore para BLoC
Viendo ahora - 19

Manejo de Estados y Eventos en Flutter con Bloc para Finanzas
09:57 min - 20

Agregar transacciones en Firebase con Flutter Bloc
06:41 min - 21

Eliminar Transacciones con Firebase en Flutter
05:29 min - 22

MultiBlocProvider con auth y transacciones en Flutter
06:06 min
Funcionalidades con BLoC
- 23

BlocBuilder para mostrar datos en Flutter
15:13 min - 24

Integración de Gráficas y Estados en Aplicaciones Flutter
11:50 min - 25

Creación de Listados Dinámicos en Aplicaciones Flutter
18:25 min - 26

Configuración de Balance y Estados en Pantalla de Wallet
07:12 min - 27

Lista de Transacciones en Aplicaciones Móviles
04:02 min - 28

Agregar Transacciones en Firebase con Flutter y Bloc
10:55 min - 29

Eliminar transacciones en Flutter con BLoC y Firebase
03:57 min
Testing
Modelo e repositório Firestore para BLoC
Resumen
Conectar Flutter con Firestore requiere una arquitectura clara que separe el modelo de datos del repositorio que comunica con la base. Aquí aprenderás a crear un model IncomeExpense y un repository que hace fetch de transacciones desde Cloud Firestore, listo para integrarse con BLoC.
Cómo defines el modelo IncomeExpense en Flutter
El modelo es la representación en Dart de cada documento que vive en Firestore. Tu archivo income_expense_model.dart dentro de la carpeta models debe reflejar exactamente los campos que ya configuraste en la colección.
Qué propiedades necesita la clase
La clase IncomeExpense arranca con cinco propiedades alineadas con los campos del documento en Firestore [01:00]:
idde tipoString, opcional, porque Firestore lo autogenera al crear el documento.amountde tipoint, marcado comofinalpara almacenar el monto.descriptionde tipoStringpara el detalle de la transacción.datede tipoDateTime, que mapea desde unTimestampde Firestore.typede tipoString, que distingue entre income y expense.
En el constructor, id queda como this.id sin requerirlo, mientras que el resto se marca con required this.amount, required this.description, required this.date y required this.type.
¿Por qué el id no es requerido en el modelo? Porque Firestore lo genera automáticamente cuando creas un documento nuevo. Solo lo recibes al leer datos, no al escribirlos.
Cómo usar factory fromJson y toJson con Timestamp
Para que el modelo viaje entre Dart y Firestore necesitas dos puentes: un factory que recibe un Map<String, dynamic> y un método toJson que devuelve otro Map<String, dynamic> [03:30].
El truco está en el campo date. Firestore lo entrega como Timestamp, no como DateTime, así que el casteo se hace así: (json['date'] as Timestamp).toDate(). Para usar el tipo Timestamp debes importarlo desde la librería cloud_firestore.
El método toJson simplemente retorna un mapa con las claves amount, description, date y type igualadas a las variables de la instancia.
Cómo creas el repositorio que conecta con Firestore
El repositorio centraliza toda la comunicación con la base de datos. Crea el archivo income_expense_repository.dart y dentro define una clase con una propiedad FirebaseFirestore firestore [06:00].
La instalación del paquete se hace desde la terminal con:
bash flutter pub add cloud_firestore
Después corres el comando de configuración de FlutterFire para regenerar el archivo firebase_options.dart y dejar listo el proyecto.
La inicialización del repositorio queda así, instanciando Firestore por defecto:
dart class IncomeExpenseRepository { final FirebaseFirestore firestore;
IncomeExpenseRepository({FirebaseFirestore? firestore}) : firestore = firestore ?? FirebaseFirestore.instance; }
¿Para qué sirve un repository en Flutter? Es la capa que aísla la lógica de acceso a datos. Tu UI y tu BLoC nunca hablan directo con Firestore, sino con el repositorio, lo que facilita testear y cambiar el origen de datos.
Cómo implementar fetchTransactions de forma asíncrona
La función fetchTransactions es asíncrona y devuelve un Future<List<IncomeExpense>>. Dentro de un bloque try/catch haces la consulta a la colección transactions, que debe llamarse exactamente igual que en Firestore [09:00].
El flujo es directo:
- Ejecutas
await firestore.collection('transactions').get()y guardas la respuesta en una variablequery. - Recorres
query.docs.map((doc) => ...)para construir cada objetoIncomeExpense. - Dentro del map asignas
id: doc.id,amount: doc['amount'],date: (doc['date'] as Timestamp).toDate(),description: doc['description']ytype: doc['type']. - Cierras con
.toList()para devolver una lista concreta.
En el catch lanzas una excepción con un mensaje claro: throw Exception('Error fetching data on FetchTransactions: $e'). Así, cuando algo falle, sabrás exactamente en qué función ocurrió.
Por qué este patrón prepara el terreno para BLoC
Con el modelo tipado y el repositorio aislado, BLoC solo se preocupa de orquestar estados y eventos. El modelo garantiza que los datos lleguen con tipos correctos en Dart, y el repositorio asegura que la conversión entre Timestamp y DateTime ocurra una sola vez, en un solo lugar.
Esta separación es la que permite que mañana, si decides migrar de Firestore a otra base, solo tengas que reescribir el repositorio sin tocar tu UI ni tu lógica de negocio.
¿Ya tienes lista tu colección transactions en Firestore? Cuéntame en los comentarios qué campos agregaste a tu modelo.