在 Composable 中收集转换后的 StateFlow
Collect transformed StateFlow in Composable
有函数collectAsState()
适用于StateFlow
属性以便在Composable
.
中观察它
可组合项需要 StateFlow
,因为 StateFlow
保证初始值。 Flow
没有这种保证。
现在,如果我有 StateFlow
属性 但我想在收集 Flow
之前应用运算符(如 map
),该怎么办在 Composable
?
举个例子:
假设一个存储库公开了一个 StateFlow<MyClass>
val myClassStateFlow: StateFlow<MyClass>
data class MyClass(val a: String)
... 并且视图模型依赖于存储库并且只想将 属性 a
公开给它的 Composable
...
val aFlow = myClassState.Flow.map { it.a } // <- this is of type Flow<String>
map
运算符将类型从 StateFlow<MyClass>
更改为 Flow<String>
。
-
aFlow
不再具有初始值在语义上是否合理?毕竟它的第一次发射是从 myClassStateFlow
. 的初始值导出的
- 需要在某个时候将
Flow
转换回 StateFlow
。这是哪个更惯用的地方?
- 在视图模型中使用
stateIn()
?代码会是什么样子?
- 在可组合项中使用
collectAsState(initial: MyClass)
并得出一个初始值(尽管 myClassStateFlow
有一个初始值)?
目前没有内置的方法来转换 StateFlow
s,只有 Flow
s。但是你可以自己写。
我最终解决的方法是使用 post 中的示例。
首先创建一个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
上进行扩展,就像在 Flow
上的当前 map
扩展一样
fun <T1, R> StateFlow<T1>.mapState(transform: (a: T1) -> R): StateFlow<R> {
return DerivedStateFlow(
getValue = { transform(this.value) },
flow = this.map { a -> transform(a) }
)
}
现在在您的 Repository 或 ViewModel 中,您可以按如下方式使用它。
class MyViewModel( ... ) {
private val originalStateFlow:StateFlow<SomeT> = ...
val someStateFlowtoExposeToCompose =
originalStateFlow
.mapState { item ->
yourTransform(item)
}
}
现在您可以在 Compose 中按预期使用它而无需任何特殊工作,因为它 returns StateFlow
.
有函数collectAsState()
适用于StateFlow
属性以便在Composable
.
可组合项需要 StateFlow
,因为 StateFlow
保证初始值。 Flow
没有这种保证。
现在,如果我有 StateFlow
属性 但我想在收集 Flow
之前应用运算符(如 map
),该怎么办在 Composable
?
举个例子:
假设一个存储库公开了一个 StateFlow<MyClass>
val myClassStateFlow: StateFlow<MyClass>
data class MyClass(val a: String)
... 并且视图模型依赖于存储库并且只想将 属性 a
公开给它的 Composable
...
val aFlow = myClassState.Flow.map { it.a } // <- this is of type Flow<String>
map
运算符将类型从 StateFlow<MyClass>
更改为 Flow<String>
。
-
aFlow
不再具有初始值在语义上是否合理?毕竟它的第一次发射是从myClassStateFlow
. 的初始值导出的
- 需要在某个时候将
Flow
转换回StateFlow
。这是哪个更惯用的地方?- 在视图模型中使用
stateIn()
?代码会是什么样子? - 在可组合项中使用
collectAsState(initial: MyClass)
并得出一个初始值(尽管myClassStateFlow
有一个初始值)?
- 在视图模型中使用
目前没有内置的方法来转换 StateFlow
s,只有 Flow
s。但是你可以自己写。
我最终解决的方法是使用 post 中的示例。
首先创建一个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
上进行扩展,就像在 Flow
map
扩展一样
fun <T1, R> StateFlow<T1>.mapState(transform: (a: T1) -> R): StateFlow<R> {
return DerivedStateFlow(
getValue = { transform(this.value) },
flow = this.map { a -> transform(a) }
)
}
现在在您的 Repository 或 ViewModel 中,您可以按如下方式使用它。
class MyViewModel( ... ) {
private val originalStateFlow:StateFlow<SomeT> = ...
val someStateFlowtoExposeToCompose =
originalStateFlow
.mapState { item ->
yourTransform(item)
}
}
现在您可以在 Compose 中按预期使用它而无需任何特殊工作,因为它 returns StateFlow
.