androidkotlinandroid-jetpack-composetextfield

Using Jetpack Compose, how do you store the value of a text field into a dynamically create class


I've created a test application for Android using Jetpack Compose. The application consists of a column that holds dynamically created text fields. Each text field has an instance of a class that contains the properties of that particular text field. The class is dynamically created along with the text field.

The problem arises when the user tries to enter text into the text field. None of the text appears, the text field remains empty. This problem only occurs when the variable that holds the value is stored inside of a dynamically created instance of a class that is created inside of a composable. If I created a single instance outside of a composable then it would work fine.

Here is the code for the class the holds the value. For simplicity it only has one property, In a real app it would have more.

class TextFieldLogic{
   var value by mutableStateOf(TextFieldValue(""))
}

Here is the code for the list of text fields

var textFieldList by mutableStateOf(listOf<TextFieldLogic>())

Button(onClick = { textFieldList += TextFieldLogic() }) { Text("+") }

Column {
    textFieldList.forEach { text ->
        TextField(
            value = text.value,
            onValueChange = { text.value = it },
        )
    }
}

That's all for the code. As stated above, none of the keystrokes appear in the text field when using this method. If anyone has a solution I'd love to hear it.


Solution

  • I cannot reproduce the problem you describe, but there is another major issue preventing clicking the button to add a new TextField: On every recomposition a completely new list is created because you didn't remember the list the first time. Android Studio actually marks this as an error, stating:

    Creating a state object during composition without using remember

    Just wrap the list in a remember and clicking the button will now actually work as expected:

    var textFieldList by remember { mutableStateOf(listOf<TextFieldLogic>()) }
    

    The TextFields then also behave as they should. Whenever you enter anything in the TextField the onValueChange property updates the TextFieldValue of the according list entry and the UI is recomposed, displaying the newly entered text.