Search code examples
androidkotlinandroid-roomandroid-jetpack-composeandroid-jetpack

How to add/remove characters inside TextField?


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?


Solution

  • 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
        )
    }