四、ViewModel + StateFlow + 状态持久化

四、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、项目完整结构重构

  • 相关依赖包(第一篇中已经全部导入)

    kotlin 复制代码
    dependencies {
        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状态数据类

    kotlin 复制代码
    data class NoteUIState(
        val notes:List<Note> = emptyList(),
        val isLoading: Boolean = false,
        val error:String? = null
    )
  • 创建NoteViewModel类

    kotlin 复制代码
    package 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管理状态

    kotlin 复制代码
    package 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()
                        )
                    }
                }
            }
        }
    }
  • 启动查看结果

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

相关推荐
alexhilton17 小时前
Compose中的ContentScale:终极可视化指南
android·kotlin·android jetpack
jzlhll12318 小时前
kotlin Flow first() last()总结
开发语言·前端·kotlin
符哥20081 天前
充电桩 WiFi 局域网配网(Android/Kotlin)流程、指令及实例说明文档
android·开发语言·kotlin
大傻^1 天前
SpringAI2.0 Null Safety 实战:JSpecify 注解体系与 Kotlin 互操作
android·开发语言·人工智能·kotlin·springai
jzlhll1231 天前
Kotlin Mutex vs Java ReentrantLock vs synchronized
java·开发语言·kotlin
Kapaseker1 天前
一杯 Kotlin 美式品味 object 声明
android·kotlin
俩个逗号。。1 天前
Kotlin 扩展函数详解
开发语言·kotlin
XiaoLeisj2 天前
Android Jetpack 页面架构实战:从 LiveData、ViewModel 到 DataBinding 的生命周期管理与数据绑定
android·java·架构·android jetpack·livedata·viewmodel·databinding
su1ka1112 天前
Kotlin(3)基本语法
kotlin
su1ka1112 天前
Kotlin(4)面向对象
kotlin