Может кто-нибудь сказать мне, почему значение otp не обновляется resetText?
Шаги:
Это ошибка или какие-то проблемы с моим кодом?
Модель представления
@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()) }
}
}
Государственный класс
data class SomeState(
val text: String? = null,
)
Вид
@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")
}
}
}
P.S. Я заставил это работать, изменив resetText на
fun resetText() {
viewModelScope.launch {
state.update { it.copy(text = "") }
delay(100)
state.update { it.copy(text = null) }
}
}
Но это явно плохой вариант.
Я предполагаю, что вы видите, что когда вы вводите текст вручную, он не сбрасывается при вызове метода сброса. Это связано с тем, что вы сбрасываете модель, а не локальную копию данных в otp. Это в целом нарушает принцип единого владельца государства, поскольку вводит копию, которую затем необходимо синхронизировать вручную.
Рассмотрите возможность использования только одной копии данных. Например,
@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.updateText(it) }
)
Button(onClick = { viewModel.resetText() }) {
Text(text = "reset text")
}
Button(onClick = { viewModel.changeText() }) {
Text(text = "Change text")
}
}
}
В качестве альтернативы рассмотрите возможность обновления копии состояния при сбросе модели.
Button(onClick = {
viewModel.resetText()
otp = "" // Also reset the local copy of the state
}) {
Text(text = "reset text")
}
Обратите внимание, что значение otp никуда не денется. В этом случае вам потребуется добавить действие «Отправить» или «Применить», которое обновит модель.
С вашим потоком происходит следующее:
null при создании экземпляра модели представления.otp. Значение потока остается неизменным, поэтому оно по-прежнему равно нулю.resetText. Значение потока установлено на null, но поскольку оно уже было нулевым, поток не интерпретирует это как изменение и не уведомляет свои сборщики, поэтому в вашем компонуемом объекте ничего не происходит.Если вы хотите сбросить текстовое поле, вам следует установить otp на ноль и не менять состояние:
Button(onClick = { otp = null }) {
Text(text = "reset text")
}