#android #kotlin-coroutines #android-jetpack-compose
#Android #android-jetpack-compose #kotlin-сопрограммы #kotlin-поток состояний
Вопрос:
Существует функция collectAsState()
, применимая к StateFlow
свойству, чтобы наблюдать его в Composable
.
Для composable требуется a StateFlow
, потому StateFlow
что гарантирует начальное значение. A Flow
не поставляется с такой гарантией.
Теперь, как поступить, если у меня есть StateFlow
свойство, но я хочу применить оператор (например map
) перед сбором Flow
в Composable
?
Вот пример:
Допустим, репозиторий предоставляет StateFlow<MyClass>
val myClassStateFlow: StateFlow<MyClass>
data class MyClass(val a: String)
… и модель представления зависит от репозитория и хочет предоставить только свойство a
its Composable
…
val aFlow = myClassState.Flow.map { it.a } // <- this is of type Flow<String>
map
Оператор изменяет тип с StateFlow<MyClass>
на Flow<String>
.
- Оправдано ли это семантически, что
aFlow
больше не имеет начального значения? В конце концов, его первое излучение получено из начального значенияmyClassStateFlow
. Flow
В какой-то момент требуется преобразовать обратноStateFlow
в. Какое более идиоматичное место для этого?- В модели представления с использованием
stateIn()
? Как будет выглядеть код? - В composable использовать
collectAsState(initial: MyClass)
и придумать начальное значение (хотяmyClassStateFlow
имело начальное значение)?
- В модели представления с использованием
Ответ №1:
Смотрите Этот выпуск на GitHub
В настоящее время нет встроенного способа преобразования StateFlow
s, только Flow
s. Но вы можете написать свой собственный.
В итоге я решил использовать пример из этого сообщения.
Сначала создайте понятие a DerivedStateFlow
.
class DerivedStateFlow<T>(
private val getValue: () -> T,
private val flow: Flow<T>
) : StateFlow<T> {
override val replayCache: List<T>
get () = listOf(value)
override val value: T
get () = getValue()
@InternalCoroutinesApi
override suspend fun collect(collector: FlowCollector<T>) {
flow.collect(collector)
}
}
Затем StateFlow
map
включите расширение, подобное текущему расширению на Flow
fun <T1, R> StateFlow<T1>.mapState(transform: (a: T1) -> R): StateFlow<R> {
return DerivedStateFlow(
getValue = { transform(this.value) },
flow = this.map { a -> transform(a) }
)
}
Теперь в вашем репозитории или ViewModel вы можете использовать его, как показано ниже.
class MyViewModel( ... ) {
private val originalStateFlow:StateFlow<SomeT> = ...
val someStateFlowtoExposeToCompose =
originalStateFlow
.mapState { item ->
yourTransform(item)
}
}
Теперь вы можете использовать его так, как ожидаете, в Compose без какой-либо специальной работы, поскольку он возвращает a StateFlow
.