在Compose环境下ViewModel里mutableStateOf和MutableStateFlow的使用时机

4 阅读2分钟

与传统View开发不同的是在ViewModel里面增加了mutableStateOf,那么我们该如何使用mutableStateOf和MutableStateFlow呢?

结论就是,在逻辑简单的情况下,我们使用mutableStateOf来简化逻辑,如果有复杂数据流我们最好使用StateFlow

mutableStateOf的使用

 //简单返回数据
 var roomResult by mutableStateOf<List<String>?>(null)
//判断加载状态
 var isLoading by mutableStateOf(false)
```
```

在compose里面直接使用

 val rooms = viewModel.roomResult 

但是这里的写法有问题,我们最好不用使用by关键字来初始化变量,这会让这个变量看起来是一个****普通变量 ,并且忽略了它的副作用(这里的副作用就是我们因为这个变量需要额外处理的逻辑)


class RoomViewModel : ViewModel() {

    //私有变量:只有 ViewModel 内部可以修改 (Mutable)
    private val _roomResult = mutableStateOf<List<String>?>(null)
    
    //公有变量:暴露给 Compose 读取 (Read-only State)
    val roomResult: State<List<String>?> = _roomResult

    //加载状态同理
    private val _isLoading = mutableStateOf(false)
    val isLoading: State<Boolean> = _isLoading

    //处理状态的方法
    fun fetchData() {
        //修改时必须显式使用 .value
        //这就是所谓的“不忽略副作用”:每次看到 .value,
        _isLoading.value = true
        
        //模拟异步操作后的副作用处理
        try {
            // ... 假设这里是网络请求
            _roomResult.value = listOf("Room 101", "Room 102")
        } finally {
            //副作用:无论成功失败,最后都要关闭加载状态
            _isLoading.value = false
        }
    }
}

在compose函数里面这样使用

val rooms = viewModel.roomResult.value

MutableStateFlow的使用

//私有MutableStateFlow,用于内部修改(通过调用函数)
private val _selectedWeek = MutableStateFlow<Int?>(SchoolCalendar.getWeekOfTerm())
private val _selectedWeekDayNum = MutableStateFlow<Int?>(getInitialDayOfWeek())
private val _selectedBuildNum = MutableStateFlow<Int?>(null)
private val _selectedSections = MutableStateFlow<List<Int>>(emptyList())//节次是多选


//周次
val selectedWeekSet = _selectedWeek.map { setOfNotNull(it) }

//星期
val selectedWeekDaySet = _selectedWeekDayNum.map { setOfNotNull(it) }

//教学楼
val selectedBuildNumSet = _selectedBuildNum.map { setOfNotNull(it) }

//节次(多选)
val selectedSectionsSet = _selectedSections.map { it.toSet() }

//外部改值
    fun onWeekChange(week: Int) {
        _selectedWeek.value = week
    }

    fun onWeekDayChange(day: Int) {
        _selectedWeekDayNum.value = day
    }

    fun onBuildChange(build: Int) {
        _selectedBuildNum.value = build
    }

    fun toggleSection(section: Int) {
        val current = _selectedSections.value
        _selectedSections.value = if (current.contains(section)) {
            if (current.size > 1) current - section else current
        } else {
            current + section
        }
    }

compose里面的使用

val selectedWeekSet by viewModel.selectedWeekSet.collectAsStateWithLifecycle(
    initialValue = emptySet()
)

val selectedWeekDaySet by viewModel.selectedWeekDaySet.collectAsStateWithLifecycle(
    initialValue = emptySet()
)

val selectedBuildNumSet by viewModel.selectedBuildNumSet.collectAsStateWithLifecycle(
    initialValue = emptySet()
)

val selectedSectionsSet by viewModel.selectedSectionsSet.collectAsStateWithLifecycle(
    initialValue = emptySet()
)

可以看到在compose里面还需要手动collectAsStateWithLifecycle转换