四、ViewModel + StateFlow + 状态持久化
将状态从Composable组件中分离出来,单独处理数据。
UI与数据处理逻辑分离
今天的内容比较少(主要是今天有点忙 -。-!)
1、为什么需要ViewModel
- ViewModel生命周期创建于Activity启动,销毁与Activity真正的finish,不会因为中途状态改变而被销毁重建(例如屏幕旋转,切换页面等)
- 状态分散在多个Composable中,难以复用测试。
- 无法在多个Composable中共享状态。
- 要做到数据与UI的分离,让逻辑更加清晰,代码结构更加健壮。
2、StateFlow是什么
-
是Kotlin协程中的热流(Hot Flow)
-
始终持有最新状态值
-
支持多个观察者
-
完美适配Compose的状态驱动UI
KOTLIN//用于ViewModel内部更新数据 private val _uiState = MutableStateFlow(NoteUIState(isLoading = true)) // 只读,暴露给UI读取 val uiState: StateFlow<NoteUiState> = _uiState.asStateFlow()MutableStateFlow → 内部修改
StateFlow → 外部只读(封装性)
3、collectAsState():链接Flow与Compose
kotlin
val uiState by viewModel.uiState.collectAsState()
- 自动在Composable进入时收集(collect)Flow
- 自动在退出时取消收集
- 当Flow发出新值时 -> 触发重组(Recomposition)
4、项目完整结构重构
-
相关依赖包(第一篇中已经全部导入)
kotlindependencies { implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7") implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0") } -
定义UI状态数据类
kotlindata class NoteUIState( val notes:List<Note> = emptyList(), val isLoading: Boolean = false, val error:String? = null ) -
创建NoteViewModel类
kotlinpackage com.jiahengfei.ktdm import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch /** * Create by zFox from AndroidStudio2022.01 * 日期:2026/1/8 20:01 * 描述:ViewModel数据模型类 */ class NoteViewModel: ViewModel() { private val _uiState = MutableStateFlow(NoteUIState(isLoading = true)) val uiState: StateFlow<NoteUIState> = _uiState.asStateFlow() init { loadNotes() } private fun loadNotes() { viewModelScope.launch { delay(2000) val mockNotes = listOf( Note(1, "协程进阶", "学会了 ViewModel + StateFlow", "2026-01-08", listOf("Compose")), Note(2, "状态管理", "UI 状态现在由 ViewModel 驱动", "2026-01-08", listOf("MVVM")), Note(3, "协程进阶2", "学会了 ViewModel + StateFlow", "2026-01-08", listOf("Compose")), Note(4, "状态管理2", "UI 状态现在由 ViewModel 驱动", "2026-01-08", listOf("MVVM")) ) _uiState.value = NoteUIState( notes = mockNotes, isLoading = false ) } } } -
更新笔记列表NoteListScreen,使用ViewModel管理状态
kotlinpackage com.jiahengfei.ktdm import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.withContext /** * Create by zFox from AndroidStudio2022.01 * 日期:2026/1/7 19:10 * 描述:笔记本列表 */ /** * 挂起函数 */ suspend fun requestNotes(){ return withContext(Dispatchers.IO){ delay(2000) } } @Composable fun NoteListScreen( modifier: Modifier = Modifier, showSnackBar: (String) -> Unit, viewModel: NoteViewModel = viewModel() ) { val uiState by viewModel.uiState.collectAsState() Box( modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { if (uiState.isLoading) { CircularProgressIndicator() } else { //显示笔记本列表 LazyColumn( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(8.dp), contentPadding = PaddingValues(16.dp) ) { items(uiState.notes) { note -> NoteCard( note = note, showSnackBar, modifier = Modifier.fillParentMaxWidth() ) } } } } } -
启动查看结果

这就是第四天的全部内容啦,明天我们将切换到实战项目进行编写边学。