kotlin 同时使用mutableStateOf和Flow,但不能更改UI组件状态

k2fxgqgv  于 2023-08-06  发布在  Kotlin
关注(0)|答案(1)|浏览(224)

我在ViewModel中有一个变量,它是一个名为 isRefreshing 的布尔值。在UI组件中,当我单击按钮时,onClick会调用viewmodel中的函数 refreshList,并更改 isRefreshing 的值。在 refreshList 的开始,isrefreshing 将被设置为true,而在函数的结尾,isrefreshing 将被设置为false。当 isRefreshing 为true时,组件将为CircularProgressIndicator。但是,当我单击该按钮时,组件将更改为CircularProgressIndicator,但不会更改回原始组件。* 详细代码如下。*
在我的ViewModel中,我有一个如下所示的flag变量:

var isRefreshing by mutableStateOf(false)
    private set

字符串
组件使用 isRefreshing 如下:

if (songVM.isRefreshing) {
    CircularProgressIndicator(color = MaterialTheme.colorScheme.onPrimary)
} else {
    Image(
        imageVector = Icons.Default.Refresh,
        contentDescription = null,
    )
}


ViewModel有另一个函数,isRefreshing 的值只在此函数中更改:

suspend fun refreshList() {
        isRefreshing = true

        // Other Codes

        getAllBySelectOrder(0)
        isRefreshing = false
    }


getAllBySelectOrder()定义如下:

private suspend fun getAllBySelectOrder(type: Int) {
        val flow = when (type) {
            0 -> songsRepository.getAllOrderById()
            1 -> songsRepository.getAllOrderBySinger()
            else -> songsRepository.getAllOrderBySong()
        }
        combine(flow) { flowArr ->
            SongsViewState(flowArr[0])
        }.collect { _songsState.value = it }
}


其他相关代码:

private val _songsState = MutableStateFlow(SongsViewState())
data class SongsViewState(
    val songs: List<SongBean> = emptyList()
)


我测试了代码,发现关键点是 getAllBySelectOrder()。如果我删除这个函数,一切都将正常工作,但当我添加这个函数,它不能工作。我问了GPT,它说问题是由 * 收集 * 引起的。我不知道怎么修。请帮帮我先谢了。

d7v8vwbk

d7v8vwbk1#

大多数Flows都是无限的,这意味着如果你对它们调用collect(),调用collect()的函数将永远不会返回。它只是挂起在调用collect()的地方。
我假设来自存储库的流是无限的,并且无限期地监视数据库,因此每当数据库中的某些内容发生更改时,它们都会发出一个新项。因此,您需要做的是关闭第一项上的isRefreshing,然后允许继续收集流。
另外,我认为不应该将refreshList()设置为suspend函数。从UI端调用它确实很不方便,而且也没有必要,因为Compose的行为是React式的--调用者不需要等待它完成。
因此,将这些放在一起,我们将refreshList()更改为非挂起。getAllBySelectOrder被更改,因此它启动一个协程,并在收集某些内容时关闭isLoading。它应该是罚款,它只是关闭它的每一个项目收集。您需要做的另一件事是取消此函数中以前的任何流收集,以便在使用不同参数调用getAllBySelectOrder()时不会收集过时的流。

fun refreshList() {
    isRefreshing = true

        // Other Codes

    getAllBySelectOrder(0)
}

private var getAllBySelectOrderJob: Job? = null

private fun getAllBySelectOrder(type: Int) {
    getAllBySelectOrderJob?.cancel()
    getAllBySelectOrderJob = viewModelScope.launch {
        when (type) {
            0 -> songsRepository.getAllOrderById()
            1 -> songsRepository.getAllOrderBySinger()
            else -> songsRepository.getAllOrderBySong()
        }.collect { 
            _songsState.value = SongsViewState(it)
            isRefreshing = false
        }
    }
}

// Or, if you're familiar with launchIn, I think this is easier to read:

private fun getAllBySelectOrder(type: Int) {
    val flow = when (type) {
            0 -> songsRepository.getAllOrderById()
            1 -> songsRepository.getAllOrderBySinger()
            else -> songsRepository.getAllOrderBySong()
        }.onEach { 
            _songsState.value = SongsViewState(it)
            isRefreshing = false
        }

    getAllBySelectOrderJob?.cancel()
    getAllBySelectOrderJob = flow.launchIn(viewModelScope)
}

字符串

相关问题