TextField and remember
Sprout's add-plant screen has a name field. A first attempt often looks like this, and typing into it does absolutely nothing:
TextField(value = "", onValueChange = { })Nothing is broken. The field is doing exactly what it was told. Understanding why is the key to all Compose input.
The rule
The field shows your state and reports edits; you write them back.
A TextField does not own its text. It has two halves:
value— what to display. It always shows exactly this.onValueChange— called with the new text when the user types. It is a request, not a change.
So you need state that holds the text, survives recomposition, and triggers a redraw when it changes:
var name by remember { mutableStateOf("") }
TextField(
value = name,
onValueChange = { name = it },
label = { Text("Plant name") }
)mutableStateOf("")creates observable state: when it changes, readers recompose.rememberkeeps that state across recompositions. Without it, each recomposition would create a fresh empty state.rememberSaveabledoes the same and also survives rotation, as you saw in the lifecycle section. For typed text, prefer it.
Because the value passes through your code, you can also shape it, for example onValueChange = { name = it.take(30) } to cap the length.
When state is kept in a parent and passed down as value plus a callback, the child is stateless and easy to reuse and preview. This pattern is called state hoisting.
The shortcut
Trace one keystroke as a loop: type → report → write → redraw.
Type
The user presses M. The field does not change yet.
If the loop is broken anywhere (no state, no remember, or an empty callback), the field appears frozen.
Drills
Answer fast — trust the rule.
Why does TextField(value = "", onValueChange = { }) never show typed text?
What does remember do in remember { mutableStateOf("") }?
Which line correctly updates the state from the field?
Sprout's name text should survive rotation. Which should you use?
Remember
- The field shows your state and reports edits; you write them back.
remember { mutableStateOf(...) }holds it;rememberSaveablealso survives rotation.- A frozen field means the type → report → write → redraw loop is broken.
