3

collectAsState()适用于StateFlow属性的功能,以便在 中观察它Composable

可组合项需要 aStateFlow因为StateFlow保证初始值。AFlow不附带该保证。

现在,如果我有一个StateFlow属性,但我想map在收集之前应用一个运算符(如 )Flow,该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>

  1. aFlow不再有初始值在语义上是否合理?毕竟它的第一次发射是从 的初始值导出的myClassStateFlow
  2. 它需要在某个时候转换Flow回。StateFlow哪个是更惯用的地方?
    1. 在视图模型中使用stateIn()?代码会是什么样子?
    2. 在可组合中使用collectAsState(initial: MyClass)并得出一个初始值(虽然myClassStateFlow有一个初始值)?
4

1 回答 1

1

在 GitHub 上查看此问题

目前没有内置的方法来转换StateFlows,只有Flows。但是你可以自己写。

我最终解决的方法是使用该帖子中的示例。

首先创建一个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) }
    )
}

现在在您的 Repository 或 ViewModel 中,您可以按如下方式使用它。


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

现在您可以在 Compose 中按预期使用它而无需任何特殊工作,因为它返回一个StateFlow.

于 2021-12-16T11:04:31.000 回答