110. FLOW en Android Compose¶
Flujos en kotlin Conceptos: * hot y cold flows
Representan flujos de datos producidos de modo asíncrono .
Disponemos de dos tipos de flow hot flow y cold flow.
(TODO) Recordar el modelo productor-consumidor
-
Cold flows No producen valores hasta que existe un consumidor:
putno devuelve nada hasta que no usamos, por ejemplo,collect(),first(),toList() -
Hot flows produce valores en cuanto estén disponibles independientemente si se consumen o no. Se utilizan con:
- Actualizaciones en tiempo real, por ejemplo cuando una base de datos se insertan nuevos datos
- Sensores de datos en tiempo real. Por ejemplo los datos de GPS
Flow esta diseñado para funcionar con corrutinas y por tanto recibir actualizacioes de múltiples fuentes sin bloqueos.
Se pueden ver los Flow como tuberías de flujo de datos desde los productores a los consumidores-
Puede existir un elemento intermedio que transforme los datos al vuelo.
El Productor emite los datos. En el intermediario se puedenn transformar y en el Consumidor se recolectan
Aquí solamente nos vamos a fijar en un especialización de Flow: StateFlow
110.1 STATE FLOW¶
La adaptación de FLOW a Compose y al ciclo de vida de Android da lugar a StateFlow como derivado de Flow. El StateFlow mantiene un único valor conectado al estado que estamos observando ( método stateflow.value). Similar a la programación reactiva.
Disponemos de un MutablaStateFlow (podemos actualizar) y StateFlow que es inmutable.
Supongamos que queremos mostrar en el IU una lista de users de forma que muestre cuando un usuario pasa de activo a inactivo y al revés.
class FeedViewModel : ViewModel() {
val activeUsers = flow<List<String>> {
emit(listOf("user1", "user2", "user"))
}
}
Este flow se convierte en un StateFlow invocando el método .stateIn()
class FeedViewModel : ViewModel() {
val activeUsers = flow<List<String>> {
emit(listOf("user1", "user2", "user"))
}
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
}
stateIn() admite tres parámetros:
- CoroutineScope: Scope donde se ejecuta el flow. Entre los candidatos que podemos usar
viewModelScopees el que tenemos más a mano en un ViewModel. - Sharing method (cómo compartir)
SharingStartedes el Interfaz usado para definir el comportamiento de corrutinas compartidas. Dependiendo del problema podemos usar: SharingStarted.WhileSubscribedla Corrutina seguirá activa mientras exista al menos un recolector de datosSharingStarted.Eagerly. La corrutina se inicia independientemente de que existan recolectores o noSharingStarted.LazilyLa corrutina se inicia solamente cuando exista un recolector.- Valores por defecto. En este caso una lista vacia
110.2 Recolectar flows¶
Para recolectar datos en Flow podemos llamar el método collect(). Pero cuando estamos con Compose necesitamos tener en cuenta el ciclo de vida Android.
Google proporciona la función collectAsState() para recolectar datos del flow y convertirlos en estados.
Cada vez que se produzca un nuevo dato, se recolecta y actualiza el estado lo que provoca la recomposicion en el IU.
Por ejemplo , en el @Composable nos subscribimos al flow para actualizar los estados:
@Composable
fun UserFeedScreen(userViewModel: UserViewModel) {
val searchText by userViewModel.searchTextState.collectAsStateWithLifecycle()
val clients by userViewModel.clients.collectAsState()
ClientRoute(
clients,
searchText = searchText,
searchWidgetState = clientViewModel.searchWidgetState.value,
onTextChange = { clientViewModel.updateSearchTextState(newValue = it) },
onSearchTrigger = { clientViewModel.updateSearchWidgetState(newValue = SearchWidgetState.OPENED) },
onCloseClick = { clientViewModel.updateSearchWidgetState(newValue = SearchWidgetState.CLOSED) },)
}
SEGUIR https://decode.agency/article/kotlin-flows-guide/
110.3 Resumen de uso de StateFlow en Compose¶
Como ejemplo queremos una Activity con una pantalla con un color de fondo que cambie cada vez que se pulsa:
class SimpleViewModel: ViewModel(){
private val _color = MutableStateFlow(0xFFFFFFFF)
val color = _color.asStateFlow()
var composeColor: Long by mutableStateOf(0xFFFFFFFF)
private set // no se puede cambiar
fun cambiarColor(){
val color = Random.nextLong(0xFFFFFFFF)
_color.value = color
composeColor = color
}
}
@Composable
fun PantallaFlow(simpleVM : SimpleViewModel ,) {
val composeColor = simpleVM.composeColor
val flowColor by simpleVM.color.collectAsState()
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(flowColor)) //composeColor
.clickable {simpleVM.cambiarColor()}
)
}
El funcionamiento es el mismo con uno y con otro.
110.4 Apendice¶
Versión 0.5 12-12-23