Flows de Kotlin para implementar búsquedas en tiempo real

A
Antonio Leiva
3 min lectura

En Android, los Flows de Kotlin son una manera de representar secuencias de datos asincrónicas que emiten valores de forma continua.

Estos Flows pueden ser útiles en situaciones en las que deseamos escuchar eventos y procesar los resultados de forma asíncrona, como en el caso de una búsqueda en tiempo real.

Implementar búsqueda al escribir sobre un EditText

Para implementar una búsqueda en tiempo real en Android que escuche los eventos de un EditText y evite que se produzcan muchas consultas de búsqueda en un corto período de tiempo, podemos utilizar la función debounce de Kotlin.

Esta función nos permite filtrar eventos que lleguen demasiado rápido uno detrás de otro, permitiéndonos especificar un tiempo mínimo que debe transcurrir entre eventos para que sean emitidos al Flow.

Para utilizar el debounce con los Flows de Kotlin, podemos seguir los siguientes pasos:

Creamos una función de extensión para escuchar los cambios

Creamos una función de extensión sobre la clase EditText que devuelva un Flow con los cambios de texto del EditText.

Podemos hacer esto utilizando el método callbackFlow de Kotlin, que nos permite crear un Flow que se alimenta de callbacks.

fun EditText.textChanges(): Flow<CharSequence> {
    return callbackFlow {
        val textWatcher = object : TextWatcher {
            override fun afterTextChanged(s: Editable?) = Unit

            override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                // este método se llama cuando el texto está cambiando
                trySend(s) // emitimos el valor al Flow
            }
        }
        addTextChangedListener(textWatcher)
        awaitClose {
            removeTextChangedListener(textWatcher)
        }
    }
}

Emitir valores solo si ha pasado cierto tiempo

Ahora podemos utilizar la función de extensión que hemos creado para obtener un Flow con los cambios de texto del EditText.

A continuación, aplicamos la función debounce al Flow para especificar un tiempo mínimo que debe transcurrir entre eventos para que sean emitidos.

val editText = EditText(context)

editText.textChanges()
    .debounce(500) // espere 500ms sin eventos para emitir el valor
    .onEach { text ->
        println("Texto actual: $text")
    }
    .launchIn(uiScope)

Con esto, cada vez que el usuario cambie el texto del EditText, se emitirá un evento al Flow.

Sin embargo, si el usuario escribe rápidamente y produce varios cambios de texto en menos de 500ms, solo se emitirá el último evento después de que hayan pasado 500ms sin nuevos cambios de texto.

Por último, podemos utilizar el Flow para realizar una búsqueda en tiempo real utilizando una API de búsqueda o cualquier otra lógica de procesamiento que necesitemos. Por ejemplo:

val editText = EditText(context)

editText.textChanges()
    .debounce(500) // espere 500ms sin eventos para emitir el valor
    .map { text -> text.toString() } // convertimos el CharSequence a String
    .filter { text -> text.isNotEmpty() } // filtramos los valores vacíos
    .distinctUntilChanged() // evitamos hacer búsquedas repetidas
    .flatMapLatest { query ->
        searchApi.search(query) // hacemos la búsqueda utilizando la API de búsqueda
            .asFlow() // convertimos el resultado a un Flow
    }
    .onEach { result ->
        println("Resultado de la búsqueda: $result")
    }
    .launchIn(uiScope)

Con esto, cada vez que el usuario escriba en el EditText y hayan pasado 500ms sin nuevos cambios de texto, se realizará una búsqueda utilizando la API de búsqueda y se mostrará el resultado en la consola.

Conclusión

En conclusión, los Flows de Kotlin son una herramienta útil para representar secuencias de datos asincrónicas en Android y pueden ser utilizados para implementar funcionalidades como la búsqueda en tiempo real.

Al utilizar la función debounce, podemos filtrar eventos que lleguen demasiado rápido uno detrás de otro y especificar un tiempo mínimo que debe transcurrir entre eventos para que sean emitidos al Flow.

De esta manera, podemos evitar que se produzcan muchas consultas de búsqueda en un corto período de tiempo y mejorar el rendimiento de nuestra aplicación.