Saltar al contenido

Bloc, Flutter y Navigation

Después de consultar con especialistas en esta materia, programadores de diversas áreas y maestros hemos dado con la respuesta al dilema y la compartimos en esta publicación.

Solución:

Para conseguir el mito de que BLoC es los camino a seguir fuera del camino: no hay una manera perfecta de manejar el estado. Cada arquitectura de gestión estatal resuelve algunos problemas mejor que otros; siempre hay compensaciones y es importante tenerlas en cuenta a la hora de decidirse por una arquitectura.

Generalmente, la buena arquitectura es práctica: Es escalable y extensible y solo requiere una sobrecarga mínima. Debido a que las opiniones de las personas sobre la viabilidad difieren, la arquitectura siempre implica opiniones, así que tome lo siguiente con un grano de sal, ya que expondré mi opinión personal sobre cómo adoptar BLoC para su aplicación.

BLoC es un enfoque muy prometedor para la gestión estatal en Flutter debido a un ingrediente característico: los flujos. Permiten desacoplar la interfaz de usuario de la lógica empresarial y funcionan bien con el enfoque Flutter-ish de reconstruir subárboles completos de widgets una vez que están desactualizados. Así que, naturalmente, todas las comunicaciones desde y hacia el BLoC deberían utilizar streams, ¿verdad?

+----+  Stream   +------+
| UI | --------> | BLoC |
|    | <-------- |      |
+----+   Stream  +------+

Bueno, algo así.

Lo importante a recordar es que La arquitectura de gestión estatal es un medio para lograr un fin.; no debe hacer las cosas por el simple hecho de hacerlo, sino que mantenga la mente abierta y evalúe cuidadosamente los pros y los contras de cada opción. La razón por la que separamos el BLoC de la interfaz de usuario es que el BLoC no necesita preocuparse por cómo está estructurada la interfaz de usuario, solo proporciona algunas transmisiones simples y agradables y lo que suceda con los datos es responsabilidad de la interfaz de usuario.

Pero si bien las transmisiones han demostrado ser una forma fantástica de transportar información desde el BLoC a la interfaz de usuario, agregan una sobrecarga innecesaria en la otra dirección: las transmisiones se diseñaron para transportar flujos continuos de datos (incluso en el nombre), pero la mayoría de los tiempo, la interfaz de usuario simplemente necesita activar eventos únicos en el BLoC. Por eso a veces ves algunos Streamso soluciones hacky similar¹, solo para adherirse a la forma estrictamente BLoC-y de hacer las cosas.

Además, si empujáramos nuevas rutas basadas en el flujo del BLoC, el BLoC básicamente controlaría el flujo de la interfaz de usuario, pero tener un código que controle directamente tanto la interfaz de usuario como la lógica empresarial es exactamente lo que intentamos evitar.

Es por eso que algunos desarrolladores (incluyéndome a mí) simplemente rompen con la solución completamente basada en transmisión y adoptan una forma personalizada de activar eventos en el BLoC desde la interfaz de usuario. Personalmente, simplemente uso llamadas a métodos (que generalmente devuelven Futures) para activar los eventos de BLoC:

+----+   method calls    +------+
| UI | ----------------> | BLoC |
|    | <---------------- |      |
+----+   Stream, Future  +------+

Aquí vuelve el BLoC Streams para datos "en vivo" y Futures como respuestas a llamadas a métodos.

Veamos cómo podría funcionar eso para su ejemplo:

  • El BLoC podría proporcionar una Stream de si el usuario ha iniciado sesión, o incluso un Stream, dónde Account contiene la información de la cuenta del usuario.
  • El BLoC también podría proporcionar un Future signIn(String username, String password) método que no devuelve nada si el inicio de sesión fue exitoso o arroja un error en caso contrario.
  • La interfaz de usuario podría manejar la administración de entrada por sí sola y activar algo como lo siguiente una vez que se presiona el botón de inicio de sesión:
try 
  setState(() => _isLoading = true); // This could display a loading spinner of sorts.
  await Bloc.of(context).signIn(_usernameController.text, _passwordController.text);
  Navigator.of(context).pushReplacement(...); // Push logged in screen.
 catch (e) 
  setState(() => _isLoading = false);
  // TODO: Display the error on the screen.

De esta manera, obtiene una buena separación de preocupaciones:

  • El BLoC realmente hace lo que se supone que debe hacer: manejar la lógica empresarial (en este caso, iniciar la sesión del usuario).
  • La interfaz de usuario solo se preocupa por dos cosas:
    • Visualización de datos de usuario de Streamarena
    • reaccionando a las acciones del usuario activándolas en el BLoC y realizando acciones de IU basadas en el resultado².

Finalmente, quiero señalar que esta es solo una posible solución que evolucionó con el tiempo al probar diferentes formas de manejar el estado en una aplicación compleja. Es importante conocer diferentes puntos de vista sobre cómo podría funcionar la gestión estatal, por lo que te animo a profundizar en ese tema, tal vez mirando la sesión "Pragmatic State Management in Flutter" de Google I / O.

EDITAR: Acabo de encontrar esta arquitectura en las muestras de arquitectura de Brian Egan, donde se llama "Simple BLoC". Si quieres conocer diferentes arquitecturas, te recomiendo que eches un vistazo al repositorio.


¹ Se vuelve aún más feo cuando se trata de proporcionar múltiples argumentos a una acción BLoC, porque entonces necesitaría definir una clase contenedora solo para pasarla al Stream.

² yo hacer Admita que se pone un poco feo al iniciar la aplicación: necesitará algún tipo de pantalla de inicio que solo verifique el flujo de BLoC y redirija al usuario a la pantalla adecuada según si inició sesión o no. Esa excepción a la regla ocurre porque el usuario realizó una acción, iniciar la aplicación, pero el marco de Flutter no nos permite conectarnos directamente a eso (al menos no de manera elegante, hasta donde yo sé).

los BlocListener es el widget que probablemente necesite. Si el estado cambia a (por ejemplo) LoginSuccess, el oyente del bloque puede llamar al habitual Navigate.of(context). Puedes encontrar un ejemplo de BlocListener en acción cerca del final de esta página.

Otra opción es pasar una devolución de llamada al evento.

 BlocProvider.of(context).add(MyEvent(
              data: data,
              onSuccess: () 
                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) 
                    return HomePage();
                  ),
                );
              ));

En primer lugar: si no hay lógica empresarial, no es necesario ir a la clase YourBloc.

Pero de vez en cuando la actividad de algún usuario requería realizar alguna lógica en Bloque político class y luego Bloc class tiene que decidir qué hacer a continuación: simplemente reconstruir widgets o mostrar diálogo o incluso navegar a la siguiente ruta. En tal caso, debe enviar algunos Estado a la interfaz de usuario para finalizar la acción. Entonces aparece otro problema: ¿qué debo hacer con los widgets cuando Bloc envíe State para mostrar tostadas?

Y este es el problema principal de toda esta historia.

Muchas respuestas y artículos recomiendan usar flutter_block. Esta biblioteca tiene BlocBuilder y BlocListener. Con esas clases puedes resolver algunos problemas pero no el 100% de ellos.

En mi caso utilicé BlocConsumer que gestiona BlocBuilder y BlocListener y proporcionar una forma brillante de gestionar los estados.

De la documentación:

BlocConsumer(
  listenWhen: (previous, current) 
    // return true/false to determine whether or not
    // to invoke listener with state
  ,
  listener: (context, state) 
    // do stuff here based on BlocA's state
  ,
  buildWhen: (previous, current) 
    // return true/false to determine whether or not
    // to rebuild the widget with state
  ,
  builder: (context, state) 
    // return widget here based on BlocA's state
  
)

Como puedes ver con BloqueConsumidor puede filtrar estados: puede definir fácilmente estados para reconstruir widgets y estados para mostrar algunas ventanas emergentes o navegar a la siguiente pantalla.

Te mostramos las reseñas y valoraciones de los usuarios

¡Haz clic para puntuar esta entrada!
(Votos: 2 Promedio: 5)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *