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

如何按类别过滤分组Flow<List<Object>>并修复StateFlow收集为空问题

代码问题分析

  • 核心问题:Flow 是冷流,仅调用 map 方法只是构建了流的转换链,没有触发流的收集,所以 filterCarByCategory 中的转换逻辑完全没有执行,_mutableStateFlowCarList 一直保持初始的空列表值。
  • 代码笔误:取分类名称时调用了 categories.getValue(key),但你定义的分类字典变量名是 categoryList,该代码无法正常编译。
  • 逻辑隐患:全局定义了可变列表 mutableCarList,如果流多次发射数据,会重复往列表中追加元素,导致数据重复冗余。
  • 实现冗余:不需要手动循环遍历分类过滤,Kotlin 标准库提供了 groupBy 方法可以直接完成列表按字段分组。

修正后的实现方案

ViewModel 代码

class HomeViewModel: ViewModel() {
    data class Car(val id: Int, val name: String, val category: Int)
    data class CarsByCategory(val categoryId:Int, val categoryName: String, val carList: List<Car>)

    private val categoryMap = mapOf<Int, String>(1 to "Audi", 2 to "BMW", 3 to "Chevrolet", 4 to "Dodge", 5 to "Others")

    // 直接通过流转换得到结果StateFlow,不需要手动维护MutableStateFlow
    val filteredCarList: StateFlow<List<CarsByCategory>> = getAllCarsAsFlow()
        .map { carList ->
            // 先按分类id分组
            val groupedCars = carList.groupBy { it.category }
            // 遍历分类字典生成最终的分类列表,保证分类顺序和字典一致
            categoryMap.map { (categoryId, categoryName) ->
                CarsByCategory(
                    categoryId = categoryId,
                    categoryName = categoryName,
                    carList = groupedCars[categoryId].orEmpty()
                )
            }
        }
        // 把冷流转成热的StateFlow,绑定viewModelScope生命周期
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = emptyList()
        )

    private fun getAllCarsAsFlow(): Flow<List<Car>> {
        val cars = listOf(
            Car(id = 1, name = "A1", category = 1),
            Car(id = 2, name = "A2", category = 1),
            Car(id = 3, name = "BMW X1", category = 2),
            Car(id = 4, name = "BMW X7", category = 2),
            Car(id = 5, name = "M Roadster", category = 2),
            Car(id = 6, name = "Bolt EUV", category = 3),
            Car(id = 7, name = "Blazer", category = 3),
            Car(id = 8, name = "Challenger", category = 4),
            Car(id = 9, name = "Neon", category = 4),
            Car(id = 10, name = "Frontier", category = 5)
        )
        return flowOf(cars)
    }
}

Fragment 代码优化建议

推荐使用更安全的 repeatOnLifecycle 替代 launchWhenStarted 收集流,避免后台不必要的资源消耗:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    lifecycleScope.launch {
        repeatOnLifecycle(Lifecycle.State.STARTED) {
            homeViewModel.filteredCarList.collect {
                Log.d(TAG, "onViewCreated: ${it.size}")
                // 这里拿到的列表大小为5,对应定义的5个分类
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 18:54:03