简体   繁体   English

在 Composable 中收集转换后的 StateFlow

[英]Collect transformed StateFlow in Composable

There is function collectAsState() applicable to a StateFlow property in order to observe it in a Composable .有适用于StateFlow属性的 function collectAsState()以便在Composable中观察它。

A composable requires a StateFlow because StateFlow guarantees an initial value.可组合项需要StateFlow ,因为StateFlow保证了初始值。 A Flow doesn't come with that guarantee. Flow没有这种保证。

Now, what is the way to go if I have a StateFlow property but I want to apply an operator (like map ) before collecting the Flow in the Composable ?现在,如果我有StateFlow属性但我想在收集Composable中的Flow之前应用一个运算符(如map ),那么 go 的方法是什么?

Here an example:这里有一个例子:

Let's say a repository exposes a StateFlow<MyClass>假设存储库公开了StateFlow<MyClass>

val myClassStateFlow: StateFlow<MyClass>

data class MyClass(val a: String)

... and a view model has a dependency on the repository and wants to expose only the property a to its Composable ... ...和一个视图 model 依赖于存储库,并且只想将属性a公开给它的Composable ...

val aFlow = myClassState.Flow.map { it.a } // <- this is of type Flow<String>

The map operator changes the type from StateFlow<MyClass> to Flow<String> . map运算符将类型从StateFlow<MyClass>更改为Flow<String>

  1. Is it semantically justified that aFlow has no initial value anymore? aFlow不再具有初始值在语义上是否合理? After all its first emission is derived from the initial value of myClassStateFlow .毕竟它的第一次发射是从myClassStateFlow的初始值派生的。
  2. It's required to convert Flow back into StateFlow at some point.在某些时候需要将Flow转换回StateFlow Which is the more idiomatic place for this?哪个是更惯用的地方?
    1. In the view model using stateIn() ?在视图 model 中使用stateIn() How would the code look like?代码会是什么样子?
    2. In the composable using collectAsState(initial: MyClass) and come up with an initial value (although myClassStateFlow had an initial value)?在可组合中使用collectAsState(initial: MyClass)并得出一个初始值(尽管myClassStateFlow有一个初始值)?

See this issue on GitHub请参阅GitHub 上的此问题

Currently there is no built-in way to transform StateFlow s, only Flow s.目前没有内置的方法来转换StateFlow s,只有Flow s。 But you can write your own.但是你可以自己写。

Way I ended up solving was to use the example in that post.我最终解决的方法是使用该帖子中的示例。

First create a notion of a DerivedStateFlow .首先创建一个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)
    }
}

Then have an extension on StateFlow like the current map extension on Flow然后在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) }
    )
}

Now in your Repository or ViewModel, you can use it as below.现在在您的 Repository 或 ViewModel 中,您可以按如下方式使用它。


class MyViewModel( ... ) {
    private val originalStateFlow:StateFlow<SomeT>  = ...
    
    val someStateFlowtoExposeToCompose = 
        originalStateFlow
        .mapState { item -> 
            yourTransform(item)
        }
}

Now you can consume it as you expect in Compose without any special work, since it returns a StateFlow .现在,您无需任何特殊工作即可在 Compose 中按照您的预期使用它,因为它返回一个StateFlow

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM