Curso de Flutter con Firebase

MultiBlocProvider con auth y transacciones en Flutter

Curso de Flutter con Firebase

Contenido del curso

MultiBlocProvider con auth y transacciones en Flutter

Resumen

Cuando tu app Flutter necesita manejar más de un flujo de estado, como autenticación y transacciones, un solo BlocProvider se queda corto. La solución pasa por usar MultiBlocProvider y MultiRepositoryProvider, dos widgets que permiten registrar varios blocs y repositorios en el árbol de la aplicación para que cualquier pantalla acceda a ellos sin fricción.

Esta guía está pensada para desarrolladores Flutter que ya trabajan con el patrón BLoC y quieren escalar su arquitectura cuando aparece un segundo dominio de negocio, por ejemplo un módulo de ingresos y gastos junto al de auth.

Por qué pasar de BlocProvider a MultiBlocProvider

Un BlocProvider simple solo expone un bloc hacia abajo en el árbol de widgets. En cuanto necesitas escuchar AuthBloc y IncomeExpenseBloc al mismo tiempo, anidar providers se vuelve ruidoso y difícil de leer.

El MultiBlocProvider acepta una lista de providers, así que puedes declarar todos los blocs en un solo lugar y mantener el child limpio para desplegar las rutas de la aplicación.

¿Qué hace un MultiBlocProvider? Combina varios BlocProvider en una sola lista, evitando anidamientos. Recibe un arreglo en providers y un child único donde vive el resto de la app.

Cómo registrar AuthBloc e IncomeExpenseBloc juntos

Dentro de MultiBlocProvider declaras la lista providers con cada bloc creado a partir de su repositorio correspondiente. La clave está en obtener el repositorio con RepositoryProvider.of<T>(context) para que el bloc reciba la dependencia ya inyectada.

La estructura queda así en pseudocódigo:

dart MultiBlocProvider( providers: [ BlocProvider<AuthBloc>( create: (context) => AuthBloc( RepositoryProvider.of<AuthRepository>(context), ), ), BlocProvider<IncomeExpenseBloc>( create: (context) => IncomeExpenseBloc( RepositoryProvider.of<IncomeExpenseRepository>(context), )..add(TransactionsEvent()), ), ], child: MyApp(), )

En el caso de IncomeExpenseBloc se dispara desde el inicio el evento add de transactions, porque es el que más se va a utilizar al cargar la app. Esa llamada inicial precarga las transacciones para que la UI ya tenga datos cuando se renderice.

Cómo agrupar repositorios con MultiRepositoryProvider

Los blocs dependen de repositorios, así que estos deben existir antes en el árbol. Aquí entra MultiRepositoryProvider, que sigue exactamente la misma lógica: una lista de RepositoryProvider y un child.

El orden importa: primero envuelves la app con MultiRepositoryProvider, y dentro colocas el MultiBlocProvider. Así los blocs pueden leer los repositorios vía context sin errores.

¿Por qué separar repositorios y blocs en dos providers distintos? Porque los repositorios manejan acceso a datos (API, base local) y los blocs manejan estado de UI. Separarlos respeta el principio de responsabilidad única y facilita testear cada capa.

Estructura de los repositorios de auth y transacciones

Dentro de la lista declaras un RepositoryProvider por cada fuente de datos. En este flujo se registran AuthRepository para el módulo de autenticación e IncomeExpenseRepository para el módulo de finanzas.

La forma esperada es:

dart MultiRepositoryProvider( providers: [ RepositoryProvider<AuthRepository>( create: (context) => AuthRepository(), ), RepositoryProvider<IncomeExpenseRepository>( create: (context) => IncomeExpenseRepository(), ), ], child: MultiBlocProvider( // providers de blocs child: MaterialApp(...), ), )

Cada RepositoryProvider usa su propio create, por eso el create general que tenías en el RepositoryProvider único desaparece cuando migras al multi.

Cómo queda el árbol final de providers

Con ambos multi providers configurados, la app queda con dos capas claras de inyección de dependencias.

  • Capa externa: MultiRepositoryProvider con AuthRepository e IncomeExpenseRepository.
  • Capa intermedia: MultiBlocProvider con AuthBloc e IncomeExpenseBloc, ambos consumiendo los repositorios anteriores.
  • Capa interna: el child, que despliega todas las rutas o el widget raíz de la aplicación.

De esta forma cualquier pantalla puede llamar a context.read<AuthBloc>() o context.read<IncomeExpenseBloc>() sin importar dónde esté ubicada en el árbol. Los blocs escuchan eventos como add para transacciones y mantienen el estado sincronizado con la UI.

¿Cuándo conviene usar MultiBlocProvider en lugar de varios BlocProvider anidados? Siempre que tengas dos o más blocs globales. Mejora legibilidad, evita anidación profunda y centraliza la configuración de estado en un único punto.

Con esta arquitectura ya puedes consumir la información de autenticación y de transacciones desde cualquier vista. ¿Tú cómo organizas tus providers cuando agregas un tercer módulo a la app? Cuéntame en los comentarios.