第 14 课2026.07.30
Android 和 iOS 页面通常都要处理加载中、成功和失败。如果两端各自编写一套状态转换,行为容易不一致。可以在 commonMain 中建立状态持有者,把数据层结果转换为统一状态,再让平台 UI 负责渲染。
第 14 课:构建共享状态持有者
- 日期:2026-07-30
- 课程序号:14
- 知识点:把 Repository 结果转换为可观察的共享 UI 状态
用途
Android 和 iOS 页面通常都要处理加载中、成功和失败。如果两端各自编写一套状态转换,行为容易不一致。可以在 commonMain 中建立状态持有者,把数据层结果转换为统一状态,再让平台 UI 负责渲染。
核心概念
- 使用密封类型列出所有合法页面状态。
- 使用私有
MutableStateFlow更新状态,对外暴露只读StateFlow。 - 状态持有者调用 Repository,并把
DataResult映射为 UI 状态。 - UI 不再额外维护
isLoading、data、error三份可能冲突的变量。 - 协程由平台生命周期启动;共享状态持有者不创建全局作用域。
示例代码
先定义完整且互斥的状态:
kotlin
sealed interface UserUiState {
data object Idle : UserUiState
data object Loading : UserUiState
data class Content(val user: User) : UserUiState
data class Error(val error: DataError) : UserUiState
}状态持有者只依赖 Repository:
kotlin
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
class UserStore(
private val repository: UserRepository,
) {
private val _state =
MutableStateFlow<UserUiState>(UserUiState.Idle)
val state: StateFlow<UserUiState> = _state.asStateFlow()
suspend fun load(id: Long) {
_state.value = UserUiState.Loading
_state.value = when (val result = repository.loadUser(id)) {
is DataResult.Success -> {
UserUiState.Content(result.value)
}
is DataResult.Failure -> {
UserUiState.Error(result.error)
}
}
}
}平台调用层在自己的生命周期协程中执行加载:
kotlin
lifecycleScope.launch {
userStore.load(id = 1L)
}UI 收集 state,并穷尽渲染:
kotlin
when (val state = userStore.state.value) {
UserUiState.Idle -> Unit
UserUiState.Loading -> showLoading()
is UserUiState.Content -> showUser(state.user)
is UserUiState.Error -> showError(state.error)
}真实项目还需要根据产品协议明确重复加载、取消和重试语义;不要靠额外 UI 状态锁自行猜测。
5 分钟练习
给 UserStore 增加 clear() 方法,将当前状态恢复为 UserUiState.Idle。
参考答案
kotlin
fun clear() {
_state.value = UserUiState.Idle
}只有明确的业务或页面事件调用 clear() 时才重置状态,不应在 UI 渲染过程中自动修改共享状态。
与上一课的联系
上一课在 Composition Root 中组装 Repository 和用例;本课增加可被平台 UI 消费的共享状态持有者,把“数据源 → Repository → DataResult → UiState → UI”连接成一条完整数据链路。