Estoy tratando de seguir las pautas oficiales para migrar de LiveData a Flow/StateFlow con Compose, según estos artículos:
Una forma más segura de recopilar flujos de las IU de Android
Migración de LiveData a Kotlin's Flow
Estoy tratando de seguir lo que se recomienda en el primer artículo, en la colección Safe Flow en la sección Jetpack Compose cerca del final.
En Compose, los efectos secundarios deben realizarse en un entorno controlado. Para eso, usa LaunchedEffect para crear una rutina que siga el ciclo de vida del componible. En su bloque, puede llamar a suspender Lifecycle.repeatOnLifecycle si lo necesita para volver a iniciar un bloque de código cuando el ciclo de vida del host se encuentra en un estado determinado.
Me las arreglé para usar .flowWithLifecycle() de esta manera para asegurarme de que el flujo no se emite cuando la aplicación pasa a segundo plano:
@Composable fun MyScreen() { val lifecycleOwner = LocalLifecycleOwner.current val someState = remember(viewModel.someFlow, lifecycleOwner) { viewModel.someFlow .flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED) .stateIn( scope = viewModel.viewModelScope, started = SharingStarted.WhileSubscribed(5000), initialValue = null ) }.collectAsState() }Encuentro esto muy "boilerplatey" - debe haber algo mejor. Me gustaría tener StateFlow en ViewModel, en lugar de Flow que se convierte a StateFLow en @Composable, y usar .repeatOnLifeCycle() , para poder usar múltiples .collectAsState() con menos repeticiones.
Cuando trato de usar .collectAsState() dentro de una corrutina (LaunchedEffect), obviamente obtengo un error acerca de que .collectAsState() debe llamarse desde el contexto de la función @Composable.
¿Cómo puedo lograr una funcionalidad similar a la de .collectAsState(), pero dentro de .repeatOnLifecycle()? ¿Tengo que usar .collect() en StateFlow y luego ajustar el valor con State? ¿No hay nada con menos repetitivo que eso?
Después de leer algunos artículos más, incluyendo
Cosas que debe saber sobre los operadores shareIn y stateIn de Flow
Historia de diseño de la API repeatOnLifecycle
Cada vez estoy más convencido de usar .flowWithLifecycle() de esta manera en lugar de repeatOnLifecycle() + ProduceState(). Acabo de hacer esta función de extensión para aliviar el repetitivo al recopilar un flujo como estado de un @Composable:
@Composable fun <T> Flow<T>.flowWithLifecycleStateInAndCollectAsState( scope: CoroutineScope, initial: T? = null, context: CoroutineContext = EmptyCoroutineContext, ): State<T?> { val lifecycleOwner = LocalLifecycleOwner.current return remember(this, lifecycleOwner) { this .flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED) .stateIn( scope = scope, started = SharingStarted.WhileSubscribed(5000), initialValue = initial ) }.collectAsState(context) }Esto se usaría así en un @Composable:
@Composable fun SomeScreen() { //... val someState = viewModel.someFlow .flowWithLifecycleStateInAndCollectAsState( scope = viewModel.viewModelScope //or the composable's scope ) //... }Marcaré esto como una solución aceptada en caso de que ayude a alguien más, a menos que alguien me informe de una mejor manera en los próximos días.
Sobre la base de la respuesta de OP, puede ser un poco más liviano al no pasar por StateFlow internamente, si no le importa el comportamiento de WhileSubscribed(5000) .
@Composable fun <T> Flow<T>.toStateWhenStarted(initialValue: T): State<T> { val lifecycleOwner = LocalLifecycleOwner.current return produceState(initialValue = initialValue, this, lifecycleOwner) { lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { collect { value = it } } } }