I have a Room database with a table that holds users. Each user has an ID and a name. My goal is to change the name of the user based on the ID. Here is what I have tried.
I have an UsersScreen that contains a LazyColumn where I display all users. It works fine. Each row is represented by a user. On user click, I navigate forward to UpdateUserNameScreen by passing the ID of the user. In this screen I'm calling getUser(id)
function to return the desired user. Here is my code:
fun UpdateUserNameScreen(
userId: Int,
viewModel: UserViewModel = hiltViewModel()
) {
var name by remember { mutableStateOf("") }
Scaffold(
topBar = {
//TopBar content
},
content = {
viewModel.getUser(userId) //Get the user.
TextField(
value = viewModel.userState.value.name, //Set user name.
onValueChange = { name = it },
)
Button(
onClick = {
val user = User(userId, name)
viewModel.updateUser(user)
navController.popBackStack()
}
) {
Text("Save")
}
}
)
}
When I open this screen, I see the name of the user added in the TextField, which is fine. However, when I try to add change the name, no character can be added or deleted. How can change the name of the user inside the TextField?
CodePudding user response:
After TextField
trigger onValueChange
it's expected that you modify value passed into value
.
If you want to edit your view model property, you can do something like this, expecting User
to be a data class:
onValueChange = { viewModel.userState.value = viewModel.userState.value.copy(name = it) },
Usually you don't wanna edit view model state directly, you can create a setter like this:
// view model
var userState by mutableStateOf(User(0, ""))
private set
fun updateUserName(name: String) {
userState = userState.copy(name = name)
// e.g. add validation
}
// view
onValueChange = viewModel::updateUserName
An other option is that you actually wanna edit a local variable, which should be initialized with user name - so you can then make a request with new name and only update user value when request succeed.
In this case you can initialize name
with user name and use name
both for value
and setValue
:
class VM: ViewModel() {
var userState by mutableStateOf(User(0, ""))
private set
}
@Composable
fun TestScreen() {
val viewModel = viewModel<VM>()
val (name, setName) = remember(viewModel.userState.name) { mutableStateOf(viewModel.userState.name) }
TextField(
value = name,
onValueChange = setName
)
}