为何封装状态后Jetpack Compose无法触发重组?
问题:StateFlow封装data class后点击按钮无UI更新?
正常工作的代码
以下代码运行符合预期,点击按钮3次后文本显示"clicks: xxx":
class StateViewModel : ViewModel() { var state = MutableStateFlow("clicks: ") } class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { PlaygroundTheme { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { Greeting() } } } } } @Composable fun Greeting(svm : StateViewModel = viewModel()) { var state = svm.state.collectAsState() Text( text = state.value ) Button(onClick = { svm.state.value += "x" }) { Text( text = "click me" ) } }
无效果的代码
将状态封装为data class后,点击按钮UI无任何变化:
data class State(var text: String = "clicks count: ") { } class StateViewModel : ViewModel() { var state = MutableStateFlow(State()) } class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { PlaygroundTheme { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { Greeting() } } } } } @Composable fun Greeting(svm : StateViewModel = viewModel()) { var state = svm.state.collectAsState() Text( text = state.value.text ) Button(onClick = { svm.state.value.text += "x" }) { Text( text = "click me" ) } }
原因解析
核心原因是StateFlow只在值的引用发生变化时才会发射更新通知,而非监听对象内部属性的变更:
- 第一个例子中,
String是不可变类型,svm.state.value += "x"会创建新的String对象,StateFlow检测到引用变化,随即通知Composable更新UI。 - 第二个例子里,你仅修改了
State对象内部的text属性,但StateFlow持有的始终是同一个State对象的引用。即便data class自动生成了equals()方法,StateFlow默认通过引用相等判断值是否变化——只要引用没换,它就认为状态无变更,不会触发UI更新。
修复方案
通过data class的copy()方法创建新对象,替换原有引用:
Button(onClick = { svm.state.value = svm.state.value.copy(text = svm.state.value.text + "x") }) { Text(text = "click me") }
copy()会生成全新的State实例,此时StateFlow检测到引用变化,就会通知Composable更新UI。
内容的提问来源于stack exchange,提问作者Tomasz Garbus
相关产品推荐
相关产品推荐

