Estoy tratando de actualizar la fuente de un flujo en Kotlin y no estoy seguro de si este es el enfoque correcto y si es posible con Flow.
Tengo una base de datos que contiene publicaciones para un usuario y esto me devuelve un Flow<List<Post>> . Ahora, cuando selecciono a otro usuario, quiero que el flujo de la base de datos me devuelva las publicaciones del usuario recién seleccionado:
lateinit var userPosts: Flow<List<Post>> private set fun getPostsForUser(user: User) { userPosts = database.getAllPostsForUser(user) }Pero el flujo nunca se actualiza con los datos del nuevo usuario seleccionado. ¿Sigue siendo Flow la opción correcta en este caso y, en caso afirmativo, cómo puedo actualizar el flujo con las nuevas publicaciones?
Sé cómo hacerlo manualmente con la obtención de datos de la base de datos y su emisión mediante LiveData, pero me gustaría evitar el manejo de la actualización de las publicaciones cada vez que el usuario publica algo nuevo o se elimina una publicación.
Creo que tal vez esté recopilando un flujo y luego configurando un usuario, que cambia el flujo en la propiedad, pero no cualquier flujo anterior existente que ya se esté recopilando. No estoy seguro de cómo explicar lo que está pasando.
Puede ser propenso a errores tener una propiedad de flujo pública que dependa de alguna otra función que tome un parámetro. Puede devolver un Flujo directamente desde la función para que no haya ambigüedad sobre el comportamiento. El fragmento solicita un Flujo para un Usuario específico y lo obtiene inmediatamente.
distinctUntilChanged() evitará que emita una lista sin cambios que resulte de otros cambios en el repositorio.
fun getPostsForUser(user: User) Flow<List<Post>> = database.getAllPostsForUser(user).distinctUntilChanged()Si quieres usar tu patrón, creo que podrías hacerlo así. Esto permite que solo haya un Flujo, por lo que es seguro comenzar a recolectarlo temprano. Cambiar el usuario cambiará los valores que está publicando. Aunque esto es más complicado que el anterior, tiene la ventaja de que no requiere una actualización de datos en las rotaciones de pantalla y otros cambios de configuración.
private val mutableUserPosts = MutableStateFlow<List<Post>>(emptyList()) val userPosts: Flow<List<Post>> = mutableUserPosts private var userPostsJob: Job? = null var user: User? = null set(value) { field = value userPostsJob?.cancel() value ?: return userPostsJob = database.getAllPostsForUser(value) .onEach { mutableUserPosts.emit(it) } .launchIn(viewModelScope) } O, como sugiere Joffrey, es más simple con un flujo de usuario si no le importa usar la función API inestable flatMapLatest . La propiedad user aquí podría eliminarse si no le importa exponer un flujo mutable público donde el valor debe establecerse externamente. O si usa este patrón repetidamente, puede crear funciones de extensión de operador para StateFlow/MutableStateFlow para usarlo como un delegado de propiedad.
private val userFlow = MutableStateFlow<User?>(null) var user: User? get() = userFlow.value set(value) { userFlow.value = value } val userPosts: Flow<List<Post>> = userFlow.flatMapLatest { user -> if (user == null) emptyFlow() else database.getAllPostsForUser(user) }Esto podría ser útil para alguien...
Cuando recopila un flujo aguas abajo y, en algunas situaciones, necesita un flujo actualizado o un flujo completamente diferente al anterior. Utilice la función flatmapLatest() .
Ejemplo:
Hay un flujo de resultados de búsqueda en una aplicación. Cuando un usuario ingresa un flujo de texto de búsqueda, automáticamente debe actualizarse con los datos más recientes de acuerdo con los caracteres buscados.
Coloque el texto de la consulta en un StateFlow como,
private var _userSearchText = MutableStateFlow("") val userSearchText = _userSearchText.asStateFlow()a medida que el usuario ingresa texto, simplemente actualice el Stateflow como,
fun setUserNameSearchText(data: String) { _userSearchText.value = data }y tu flujo recolectando como,
val response = userSearchText.flatMapLatest { searchRepository.getResults(it) .cachedIn(viewModelScope) } Stateflow activará las llamadas a la API automáticamente. Se notificará a todos los observadores sobre las mismas.
PD: Abierto a mejorar...