Acciones y/o Transiciones Después de un StreamBuilder

Eduardo Graciano
Comunidad Flutter
Published in
3 min readApr 2, 2019
Flutter YouTube Channel

Hola a todos, en los últimos días estuve buscando una manera “correcta” o por lo menos una que me gustara para manejar acciones o transiciones a otras pantallas, cuando el StreamBuilder lanza su evento.

Antes de continuar, les comento que uso una especie de MVVM junto con el patrón BLoC, esto haciendo uso de la estructura de datos dentro de una clase Resources la cual contiene un estado, al final de este post se encuentra el repositorio de GitHub donde podrán ver todo a mayor detalle.

class Resource<T> {
final Status status;
final T data;
final String message;

bool get hasData => data != null;

Resource.success(this.data)
: status = Status.SUCCESS,
message = null;

Resource.loading()
: status = Status.LOADING,
data = null,
message = null;

Resource.error(this.message)
: status = Status.ERROR,
data = null;

Resource.idle()
: status = Status.IDLE,
data = null,
message = null;
}

enum Status { LOADING, SUCCESS, ERROR, IDLE }

El Porque…

Cuando usas un StreamBuilder , escuchas los distintos eventos o estados lanzados por el Stream. El StreamBuilder recibe ese evento y crea un Widget.

Como se puede ver, en el StreamBuilder retornamos distintos Widgets dependiendo del estado del Stream o contenido dentro del mismo.

Ahora digamos que queremos mostrar algún tipo de alerta al Usuario, por ejemplo, un Snackbar para hacerlo rápido. Si agregamos que se muestre uno en el case de Status.ERROR, ni bien ejecutemos la app y tengamos un error, en la terminal Flutter nos va a lanzar un error. Un Exception que va a contener un mensaje parecido a este:

setState() or markNeedsBuild() called during build.

Eso básicamente no dice que no podemos mostrar el Snackbar, un Dialog o hacer una transición a otra pantalla, porque Flutter esta construyendo la vista.

Primera solución parcial

Lo que el error nos dice es que no podemos realizar alguna “acción” mientras se esta construyendo la vista, por lo que podemos pensar en hacer algo después. Podemos agregar un delay.

Importando “import: dart:async’’ podemos usar un Timer.

_timer = new Timer(const Duration(milliseconds: 100), () {
// show the Snackbar
});
//...@override
void dispose() {
super.dispose();
_timer.cancel(); => No olvides cancelar tu timer.
}

No es una solución muy elegante tal vez, pero funciona (No olvides que se usaría en un StatefulWidget).

Otra seria utilizar un Future.

Future.delayed(const Duration(milliseconds: 100), () {
// show the Snackbar
});

Me parece mejor, me agrada es un poco más, es mas mmm… modular, pero aun podemos mejorarlo, sigamos adelante...

Una solución más optima

Las soluciones expuestas anteriormente funcionan pero se siente “sucio”, ya que la idea es que StreamBuilder solo cree widgets. Entonces al pensar que el StreamBuilder solo “escucha” los eventos del Stream, lo que podríamos hacer es justamente eso.

void _signInActionListener(SignInBloc signInBloc, BuildContext context) {
var onData = (state) {
if (state.status == Status.ERROR) {
final snackBar = SnackBar(content: Text('Login error'));
Scaffold.of(context).showSnackBar(snackBar);
} else if (state.status == Status.SUCCESS) {
cleanNavigateTo(ScreenNavigationContainer(), context);
}
};
signInBloc.listen.listen(onData);
}
//in a util.dart like
void cleanNavigateTo(Widget screen, BuildContext context) {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => screen));
}

Llamo a esta función durante la fase de ‘Building’. Haciendo esto, el StreamBuilder sólo crea widgets y nosotros nos encargamos de las acciones sin entorpecer el proceso de creación de vistas.

Conclusión

Cuando usamos el StreamBuilder por primera vez, tratamos de poner todo dentro de este, olvidándonos de que debería solo crear widgets dependiendo de un estado o actualizar un widget con nueva información. Entonces podemos decir que aplicando el principio de Responsabilidad Única, donde el StreamBuilder solo crea widgets y a parte escuchamos el Stream para realizar otras acciones, creo que es un mejor camino.

Credito: exceptionnotfound.net - “Solo porque puedes, no significa de deberías”

Gracias por leer, sigamos aprendiendo y mejorando juntos, deja tus comentarios o sugerencias .

Nos vemos en la próxima.

Puedes encontrar el código de ejemplo donde se muestra esta solución: https://github.com/gedu/streambuilder_handling

--

--