为何ViewModel Flow更新后otp变量未同步?技术求助
调用resetText方法后OTP值未更新的原因及解决方案
问题详情
调用resetText方法后,OTP的值没有更新,操作步骤:
- 在TextField中输入文本;
- 点击“Reset text”按钮。
这是Bug还是代码存在问题?
ViewModel代码
@HiltViewModel class MyViewModel @Inject constructor() : ViewModel() { val state = MutableStateFlow(SomeState()) fun resetText() { state.update { it.copy(text = null) } } fun changeText() { state.update { it.copy(text = Math.random().toString()) } } }
State类代码
data class SomeState( val text: String? = null, )
View代码
@Composable fun SomeView(modifier: Modifier = Modifier) { val viewModel = hiltViewModel<MyViewModel>() val state by viewModel.state.collectAsState() var otp by rememberSaveable(state.text) { mutableStateOf(state.text) } Column( modifier = modifier .padding(horizontal = 16.dp) .background(Color.White) .fillMaxSize(), verticalArrangement = Arrangement.SpaceEvenly, horizontalAlignment = Alignment.CenterHorizontally, ) { TextField( value = otp.orEmpty(), onValueChange = { otp = it } ) Button(onClick = { viewModel.resetText() }) { Text(text = "reset text") } Button(onClick = { viewModel.changeText() }) { Text(text = "Change text") } } }
临时非最优解决方案
用户通过添加延迟操作临时解决问题,但并非最佳方案:
fun resetText() { viewModelScope.launch { state.update { it.copy(text = "") } delay(100) state.update { it.copy(text = null) } } }
问题原因
问题出在View层的otp变量定义:
var otp by rememberSaveable(state.text) { mutableStateOf(state.text) }
rememberSaveable以state.text作为依赖键,初始状态下state.text就是null,调用resetText将state.text设为null时,键值没有发生变化,因此rememberSaveable不会重新初始化otp,导致otp仍然保留用户输入的内容,无法被重置。
临时方案有效的原因是:先将state.text设为空字符串(键值变化,触发rememberSaveable重新初始化otp为空),延迟后再设为null,此时otp已经被重置为空,通过otp.orEmpty()显示为空内容。
正确解决方案
方式一:同步ViewModel状态到本地otp
移除rememberSaveable的键依赖,通过LaunchedEffect监听state.text的变化,主动更新otp:
@Composable fun SomeView(modifier: Modifier = Modifier) { val viewModel = hiltViewModel<MyViewModel>() val state by viewModel.state.collectAsState() var otp by rememberSaveable { mutableStateOf(state.text) } // 监听state.text变化,同步更新otp LaunchedEffect(state.text) { otp = state.text } Column( modifier = modifier .padding(horizontal = 16.dp) .background(Color.White) .fillMaxSize(), verticalArrangement = Arrangement.SpaceEvenly, horizontalAlignment = Alignment.CenterHorizontally, ) { TextField( value = otp.orEmpty(), onValueChange = { otp = it } ) Button(onClick = { viewModel.resetText() }) { Text(text = "reset text") } Button(onClick = { viewModel.changeText() }) { Text(text = "Change text") } } }
方式二:统一使用ViewModel维护状态
将TextField的输入直接同步到ViewModel的state中,避免本地状态与ViewModel状态不一致的问题,更符合MVVM架构:
@Composable fun SomeView(modifier: Modifier = Modifier) { val viewModel = hiltViewModel<MyViewModel>() val state by viewModel.state.collectAsState() Column( modifier = modifier .padding(horizontal = 16.dp) .background(Color.White) .fillMaxSize(), verticalArrangement = Arrangement.SpaceEvenly, horizontalAlignment = Alignment.CenterHorizontally, ) { TextField( value = state.text.orEmpty(), onValueChange = { viewModel.state.update { currentState -> currentState.copy(text = it) } } ) Button(onClick = { viewModel.resetText() }) { Text(text = "reset text") } Button(onClick = { viewModel.changeText() }) { Text(text = "Change text") } } }
内容的提问来源于stack exchange,提问作者Kostya1375
相关产品推荐
相关产品推荐

