You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Composable未触发重组:ViewModel中MutableStateFlow更新不生效

问题:ViewModel更新MutableStateFlow后Composable数据未同步

在ViewModel中更新MutableStateFlow的值后,Composable中的值并未同步更新。ViewModel内已通过tryEmit更新数据,但Composable里的LazyRow仅在首次加载时展示数据;切换到其他Fragment再返回后,此处数据从未更新,断点显示ViewModel中的值已更新,但Composable内的dummyData.list始终无变化。


ViewModel代码

private val _dummyData = MutableStateFlow(DummyData())
val dummyData = _dummyData.asStateFlow()

init {
    // 获取最新数据,我也尝试过在Fragment的onResume()中执行此操作
    getDummyData()
}

private fun getDummyData() {
    myService.grabData { data ->
        val list = mutableStateListOf<Dummy>()
        data.forEach {
            list.add(it)
        }

        val dummy = DummyData(
            list = list,
        )

        _dummyData.tryEmit(dummy)
    }
}

DummyData代码

data class DummyData(
    val list: SnapshotStateList<Dummy> = mutableStateListOf(),
    // 视图可见性为条件性的,基于多种因素,添加此字段用于说明使用方式
    val displayView: Boolean = false,
)

data class Dummy(val message: String, val url: String)

Fragment代码

val dummyData by viewModel.dummyData.collectAsStateWithLifecycle()

// 将数据传入Composable函数

Composable代码

LazyRow(
    state = rememberLazyListState(),
    modifier = Modifier,
) {
    // 在此显示对象列表,仅首次加载时展示
    // 断点显示ViewModel中的值已更新,但切换到其他Fragment再返回后,此处数据从未更新
    val data = dummyData.list
    items(data) { item ->
        // 执行相关操作,与问题无关,在items(data)处断点查看数据,发现从未更新
    }
}

解决方案

问题根源

你使用SnapshotStateList作为DummyData的成员,虽然它本身是可观察集合,但DummyData是data class:

  1. 当你调用tryEmit发送新的DummyData实例时,Composable能通过collectAsStateWithLifecycle感知到变化;但如果后续仅修改现有DummyData实例内的SnapshotStateList,StateFlow不会发射新事件,Composable也就不会触发重组。
  2. 切换Fragment返回后数据未更新,是因为Fragment重建时collectAsStateWithLifecycle重新订阅,但ViewModel内的_dummyData没有重新发射最新的DummyData实例,导致Composable拿到旧的list引用。

修复方案

方案1:改用普通List(推荐)

把DummyData中的SnapshotStateList替换为普通List,每次更新数据时创建新的DummyData实例并发射,确保StateFlow能通知订阅者触发重组:

修改DummyData:

data class DummyData(
    val list: List<Dummy> = emptyList(),
    val displayView: Boolean = false,
)

修改ViewModel的getDummyData:

private fun getDummyData() {
    myService.grabData { data ->
        val dummy = DummyData(
            list = data.toList(),
        )
        _dummyData.tryEmit(dummy)
    }
}

方案2:保留SnapshotStateList,每次更新发射新实例

如果需要SnapshotStateList的局部更新能力,每次修改list后必须创建新的DummyData实例并发射:

fun updateList(newItems: List<Dummy>) {
    val currentData = _dummyData.value
    val newList = mutableStateListOf<Dummy>().apply { addAll(currentData.list) }
    newList.addAll(newItems)
    _dummyData.tryEmit(currentData.copy(list = newList))
}

方案3:在Composable中监听List变化

如果不想修改ViewModel和数据类,可在Composable中通过remember绑定dummyData,确保list变化时触发重组:

LazyRow(
    state = rememberLazyListState(),
    modifier = Modifier,
) {
    val data = remember(dummyData) { dummyData.list }
    items(data) { item ->
        // 执行相关操作
    }
}

内容的提问来源于stack exchange,提问作者Lunchbox

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 16:31:01