6.1 为什么状态管理是 Compose 的核心
Compose 的核心公式:
ini
UI = f(State)
UI 是状态的函数------当状态变化,UI 自动更新。这意味着在 Compose 中,状态管理的质量直接决定应用的可靠性、性能和可维护性。
但什么是"状态"?
状态:任何可能随时间变化的值。比如:用户的输入文本、网络请求的加载状态、列表中的数据项、开关的选中状态。
6.2 mutableStateOf ------ 最基础的状态容器
kotlin
@Composable
fun Counter() {
// count 是一个 MutableState<Int> 对象
val count = remember { mutableStateOf(0) }
Column {
Text("Count: ${count.value}") // 读取 count.value
Button(onClick = { count.value++ }) { // 修改 count.value
Text("Increment")
}
}
}
等价的 Kotlin 属性委托写法
kotlin
// 通过 by 委托,省略 .value
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Column {
Text("Count: $count")
Button(onClick = { count++ }) {
Text("Increment")
}
}
}
by与=的区别 :var count by mutableStateOf(0)使用 Kotlin 属性委托,每次访问自动委托给状态的 getter/setter。推荐使用by写法,代码更简洁。
mutableIntStateOf ------ 原始类型优化
csharp
// 用 mutableIntStateOf 替代 mutableStateOf<Int>,避免装箱
var count by remember { mutableIntStateOf(0) }
// 同理
var flag by remember { mutableBooleanStateOf(false) }
var value by remember { mutableFloatStateOf(0f) }
对于频繁更新的数值类型,使用原始类型变体能减少装箱拆箱开销。
6.3 remember ------ 记忆化的关键
remember 的作用:在重组中保持值不被重置。
kotlin
@Composable
fun WithoutRemember() {
// 每次重组都会创建新的 state,从 0 开始
var count = mutableStateOf(0)
Text("$count") // 点按钮,count 变 1,触发了重组,但又变回 0
Button(onClick = { count.value++ }) {
Text("Click")
}
}
@Composable
fun WithRemember() {
// remember 在首次组合时创建,后续重组跳过
var count by remember { mutableStateOf(0) }
Text("$count") // 正常累加
Button(onClick = { count++ }) {
Text("Click")
}
}
remember 的依赖键
kotlin
// 传入 keys 参数 ------ 当 keys 变化时重新计算
@Composable
fun Greeting(userId: String) {
val user = remember(userId) { // userId 变化时重新请求
viewModel.fetchUser(userId)
}
Text("Hello, ${user.name}")
}
rememberSaveable ------ 跨进程保存
scss
// 记住状态,且能在 Activity 被销毁重建后恢复(如旋转屏幕)
var text by rememberSaveable { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it }
)
rememberSaveable 通过 Bundle 保存状态,默认支持基本类型和 Serializable/Parcelable,自定义类型需要自定义 Saver。
6.4 State 的观察与生命周期
kotlin
// State 对象的生命周期
@Composable
fun Parent() {
var show by remember { mutableStateOf(true) }
if (show) {
Child() // Child 被加入组合树
} else {
// Child 被移除组合树
// Child 内的所有 remember 状态也会被自动清理
}
Button(onClick = { show = !show }) {
Text("Toggle")
}
}
@Composable
fun Child() {
// 这个状态的生命周期与 Child 的组合生命周期绑定
val localState = remember { mutableStateOf("local") }
Text(localState.value)
}
关键理解 :
remember存储的状态在 Composable 函数离开组合树(Disposal) 时被自动清理。不需要像 View 系统中手动释放资源。
6.5 mutableStateListOf / mutableStateMapOf
对于列表和 Map 类型的变更,普通的 mutableStateOf 可能无法精确检测内部元素变化:
csharp
// ❌ 不起作用:list 引用没变,只是内部元素变了
var list by remember { mutableStateOf(listOf(1, 2, 3)) }
list.add(4) // 即使添加了元素,Compose 也不会感知
scss
// ✅ 使用 mutableStateListOf
val list = remember { mutableStateListOf(1, 2, 3) }
Button(onClick = { list.add(list.size + 1) }) { // ✅ 触发重组
Text("Add: ${list.joinToString()}")
}
Button(onClick = { list[0] = 99 }) { // ✅ 修改元素也触发重组
Text("Modify first")
}
mutableStateListOf 创建的 SnapshotStateList 在增删改元素时都会触发重组,而不需要替换整个 List 引用。
同样的还有 mutableStateMapOf:
arduino
val map = remember { mutableStateMapOf<String, Int>() }
map["key"] = 1 // ✅ 触发重组
map.remove("key") // ✅ 触发重组
6.6 StateFlow / Flow 集成
在真实项目中,状态通常来自 ViewModel 和 Repository 层,而非直接定义在 Composable 中。
kotlin
// ViewModel 层
class MainViewModel : ViewModel() {
// 方式一:StateFlow
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
// 方式二:Compose 的 mutableStateOf(不推荐在 ViewModel 中使用)
var composeState by mutableStateOf(UiState())
private set
}
// Composable 层
@Composable
fun MainScreen(viewModel: MainViewModel = viewModel()) {
// 方式一:collectAsState() ------ 将 StateFlow 转为 Compose State
val uiState by viewModel.uiState.collectAsState()
// 方式二:collectAsStateWithLifecycle()(推荐------感知生命周期)
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
// 使用
when (uiState) {
is Loading -> LoadingIndicator()
is Success -> Content(uiState.data)
is Error -> ErrorView(uiState.message)
}
}
collectAsStateWithLifecycle ------ 推荐的做法
scss
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.0")
kotlin
@Composable
fun MainScreen() {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
// ↑ 当 Lifecycle 低于 CREATED 时停止收集,回到 RESUMED 时继续
// 防止后台状态持续更新 UI,浪费资源
}
何时用哪个 ?如果状态来自 ViewModel,优先用
collectAsStateWithLifecycle;如果是 Composable 内部的临时状态,用remember+mutableStateOf。
6.7 produceState ------ 将非 Compose 状态转为 Compose State
kotlin
@Composable
fun ImageLoader(
url: String,
modifier: Modifier = Modifier
) {
// 将协程(网络请求/数据库)转换为 Compose State
val bitmap by produceState<Bitmap?>(initialValue = null, url) {
val image = loadImage(url) // suspend 函数
value = image
}
if (bitmap != null) {
Image(bitmap = bitmap, contentDescription = null, modifier = modifier)
} else {
CircularProgressIndicator(modifier = modifier)
}
}
produceState 的等价实现(理解原理):
kotlin
@Composable
fun <T> produceState(
initialValue: T,
key: Any?,
producer: suspend CoroutineScope.() -> T
): State<T> {
val result = remember { mutableStateOf(initialValue) }
LaunchedEffect(key) {
result.value = producer()
}
return result
}
6.8 本章练习
- 实现一个
TodoInput组件:包含一个 TextField 和一个 Button,点击 Button 将输入文本添加到列表中 - 优化练习1:使用
rememberSaveable让输入框内容在屏幕旋转后不丢失 - 创建一个 ViewModel,暴露
StateFlow<UiState>,在 Composable 中用collectAsStateWithLifecycle()收集 - 用
mutableStateListOf实现一个可增删的 Todo List
scss
// 💡 练习 4 参考
@Composable
fun TodoList() {
val items = remember { mutableStateListOf<String>() }
var input by remember { mutableStateOf("") }
Column(modifier = Modifier.padding(16.dp)) {
Row {
TextField(
value = input,
onValueChange = { input = it },
modifier = Modifier.weight(1f)
)
Button(
onClick = {
if (input.isNotBlank()) {
items.add(input)
input = ""
}
}
) { Text("Add") }
}
LazyColumn {
itemsIndexed(items) { index, item ->
Row {
Text(item, modifier = Modifier.weight(1f))
IconButton(onClick = { items.removeAt(index) }) {
Icon(Icons.Default.Delete, "Delete")
}
}
}
}
}
}
6.9 本章小结
| 概念 | 作用 | 生命周期 |
|---|---|---|
mutableStateOf |
基础状态容器 | 受 remember 控制 |
remember |
在重组中保持值 | Composable 在组合中时 |
rememberSaveable |
跨进程/旋转保存状态 | 通过 Bundle 持久化 |
mutableStateListOf |
可观测的列表 | 同 remember |
StateFlow |
异步数据流 | 由 collectAsState 控制 |
collectAsStateWithLifecycle |
生命周期感知的 StateFlow 收集 | 跟随 Lifecycle |
produceState |
将异步数据转为 State | 受 key 和组合生命周期控制 |
下一篇:状态提升与单向数据流------架构设计的核心模式。