如何按类别过滤分组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
相关产品推荐
相关产品推荐

