Usar debounceTime de RxDart y Bloc para esperar a que un usuario termine de escribir

Story disponible en Français: https://arnaudelub.medium.com/using-debouncetime-from-rxdart-and-bloc-to-wait-for-a-user-to-end-typing-7a389c506a34

Story disponible en español: https://arnaudelub.medium.com/usar-debouncetime-de-rxdart-y-bloc-para-esperar-a-que-un-usuario-termine-de-escribir-147c91243329

Hola Happy coders, aquí estamos hoy para hablar sobre Bloc (¡otra vez!) Y debounceTime del increíble paquete RxDart (https://pub.dev/packages/rxdart). Si te estas preguntando qué es debounceTime, revisemos la documentación para eso (https://pub.dev/documentation/rxdart/latest/rx/DebounceExtensions/debounceTime.html):

Transforms a Stream so that will only emit items from the source sequence whenever the time span defined by duration passes, without the source sequence emitting another item.

This time span start after the last debounced event was emitted.

debounceTime filters out items emitted by the source Stream that are rapidly followed by another emitted item.

Los 3 párrafos son muy importantes de entender y lo analizaremos siguiendo un ejemplo.

Ok, vamos allá!

Entonces, el ejemplo, digamos que tienes un TextFormField y desea consultar tu base de datos de acuerdo con el texto que el usuario está ingresando en ella. Si estas usando Firebase, definitivamente no quieres consultar tu colección cada vez que el usuario entra un nuevo carácter, le costará mucho en su cuota diaria de 50K lecturas gratuitas. ¿Cómo podemos solucionarlo? Primero, veremos cómo implementar este use case sin debounceTime para que puedas ver dónde está el problema:

Ok, ¿ya has encontrado el problema? ¿No? dale otra mirada entonces …

¿Ya esta? Correcto, en la función onChanged de TextFormField, estamos llamando a nuestro evento CityWatcherEvent.watchTenNext(value) y en la clase CityWatcherBloc, podemos ver que cada vez que se recibe este evento estamos escuchando el Stream devuelto por _cityFacade.watchAll(value), lo que significa que por cada letra que escribe el usuario estamos consultando la base de datos … ¡NO ES BUENO!

Ahora que tenemos el problema, retrocedamos un paso y recordemos los 3 párrafos del documento que mencioné anteriormente y vamos a tomar el segundo:

This time span start after the last debounced event was emitted.

significa que cada vez que haya un evento “debounced”, se reiniciará el Timer, lo cual es bueno para nosotros porque queremos esperar a que el usuario termine de escribir, no hacer la llamada a nuestra base de datos después de X segundos. ahora, en el primero:

Transforms a Stream so that will only emit items from the source sequence whenever the time span defined by duration passes, without the source sequence emitting another item.

lo que significa que si no hay ningún elemento nuevo emitido, en nuestro caso, el evento se retrasará con Duration(), que es perfecto para nosotros.

Arreglamos esto!

Si echas un vistazo al repositorio de Bloc, verás este método:

Stream<Transition<Event, State>> transformEvents(Stream<Event> events,TransitionFunction<Event, State> transitionFn,  ) {
return events.asyncExpand(transitionFn);
}

justo aqui: https://github.com/felangel/bloc/blob/master/packages/bloc/lib/src/bloc.dart línea 171. Este método parece perfecto para hacer lo que pretendemos. Ahora puedes simplemente “override” este método dentro de nuestro city_watcher_bloc.dart que está extendiendo Bloc e implementar nuestro debounceTime en él. Hay una cosa a tener en cuenta primero, NO QUEREMOS usar debounceTime para cada tipo de evento, en nuestro caso _WatchTenNext y _CityReceived, es por eso que nuestro método transformEvents tiene que devolver un Merge de nuestros Streams con MergeStream (https: // pub.dev/documentation/rxdart/latest/rx/MergeStream-class.html), los “debounced” y los no “debounced” (si puede ayudarte a comprender cómo funciona Merge, puedes encontrar un diagrama interactivo aquí: https : //rxmarbles.com/#merge):

Y eso es todo, ahora, cuando un usuario está escribiendo dentro de TextFormField, el evento _watchTenNext se activará solo 500 ms después de que el usuario haya terminado de escribir. Normalmente uso 300ms o 500ms, no menos, de lo contrario, si el usuario escribe lentamente, el evento se activará incluso si no ha terminado de ingresar el valor en la entrada.

Obviamente, Bloc no es la única forma de lograrlo, pero en mi sincera opinión, creo que es una forma elegante de hacerlo.

Si tienes otra solución para esto, no dudes en compartirla en un comentario.

Happy coding a todos!

Flutter Developer enthousiast since 2018

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store