Text Field
A text field: one line of text or several that the user types, under a label, with help or an error's message below it, icons at either end, a button that clears it and a count of its characters.
import com.pantrixui.components.inputs.textfield.PantrixTextField
- Three overloads. In the one that takes a
state, the text is read from the state:state.textwhere it is used, andsnapshotFlow { state.text.toString() }to follow it as it is typed. The one that takes avalueand anonValueChangekeeps the text with the caller, andonValueChangedoes the filtering. The one that takes afieldgets its value, its error and its name from its field in the form. - Label and placeholder.
labelis written above the box and is the field's name for a screen reader; a press on the label puts the caret in the box too.placeholdershows while the field is empty. A field with no label gets its name fromcontentDescription. - Supporting text and error.
supportingTextis written under the box.errorturns the border, the label, the supporting text and the caret to thedangerrole; the supporting text is then the message that says what to put right, and a screen reader announces it as the error. - Icons and the clear button.
leadingIconstands before the text andtrailingIconafter it. While the field is not empty,showClearButtonshows a button that empties it: the button leaves the caret in the field and is no stop for Tab. - Length limit. With
maxLengthan edit that would pass the limit is turned down whole, and the count is written under the box ("12/120");showCharacterCount = falsehides the count. - Several lines. With
lineLimits = TextFieldLineLimits.MultiLine(...)the box grows with its text and Enter starts a new line. Where the field is given a height, the box fills it and text that does not fit scrolls inside the box. - Style and colours.
style(TextFieldStyle) changes the box's shape, its dimensions and its border, andcolors(TextFieldColors) its colours: only the fields that are given. A disabled field is in the theme's disabled colours whatever is given. - Text size. What is typed and the placeholder are set in the style's
textRole:body(16sp) on Android and iOS, as in Material 3 and on iOS;label(14sp) on the desktop and the web, as on a page used with a pointer. A select's box and the page jump field are set in it as well. - Reading direction. Each paragraph takes its direction from its own first letter: an email address typed into an Arabic form reads
ada@, not@ada. The text is aligned with the layout, and stays on its label's side.
Examples
At its plainest
The least there is to write: a TextFieldState and a label. The state is created with rememberTextFieldState(), and what is typed is read from it; the line below shows name.text as it is typed.
The label sits above the box and stays there; a press on the label puts the caret in the box too. The box's border is what shows the focus. tagKey sets the field's test tag: TextField_<tagKey>.
val name = rememberTextFieldState()
PantrixTextField(
state = name,
modifier = Modifier.fillMaxWidth(),
label = "Name",
tagKey = "name"
)
PantrixText(
text = if (name.text.isEmpty()) "Nothing typed yet" else "Typed: ${name.text}",
color = PantrixTheme.colors.textSecondary
)
A search field without a label
A search field that has no label: contentDescription tells a screen reader its name. The placeholder says the same, so the screen reader does not read it as well: the field is not named twice while it is empty.
leadingIcon stands before the text. A slot does not size what is put in it; the icon takes the size in the field's style: PantrixTheme.style(TextFieldStyle).iconSize. While the field is not empty, showClearButton shows the button that empties it; the button leaves the caret in the field.
keyboardOptions makes the keyboard's action key "Search", and onKeyboardAction is called when that key is pressed. Type something and press the keyboard's "Search" key, or Enter: the app's snackbar says what was searched for.
val snackbars = LocalPantrixSnackbars.current
val scope = rememberCoroutineScope()
val query = rememberTextFieldState()
PantrixTextField(
state = query,
modifier = Modifier.fillMaxWidth(),
placeholder = "Search orders",
leadingIcon = {
PantrixIcon(
imageVector = PantrixIcons.Search,
contentDescription = null,
size = PantrixTheme.style(TextFieldStyle).iconSize
)
},
showClearButton = true,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
onKeyboardAction = { scope.launch { snackbars?.show(message = "Searched for: ${query.text}", tagKey = "searched") } },
contentDescription = "Search orders",
tagKey = "search"
)
Leading and trailing icons
leadingIcon stands before the text and trailingIcon after it: in a right-to-left layout they change places. Both take the secondary text colour; a text put in a slot, like the "kg" in the second field, is drawn in that colour too.
The clear button stands before trailingIcon and is no stop for Tab: a stop after every field would double the presses it takes to get through a form. A screen reader still reaches the button as a button.
val iconSize = PantrixTheme.style(TextFieldStyle).iconSize
PantrixTextField(
state = rememberTextFieldState(),
modifier = Modifier.fillMaxWidth(),
label = "Username",
leadingIcon = { PantrixIcon(imageVector = PantrixIcons.Person, contentDescription = null, size = iconSize) },
tagKey = "username"
)
PantrixTextField(
state = rememberTextFieldState("72"),
modifier = Modifier.fillMaxWidth(),
label = "Weight",
trailingIcon = { PantrixText(text = "kg") },
showClearButton = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
tagKey = "weight"
)
Supporting text and error
supportingText stands under the box, in a small type. With error = true the border, the label, the supporting text and the caret turn to the danger role; the supporting text is then the message that says what to put right, and a screen reader announces it as the error. Type a few letters: as long as the password is shorter than eight characters the field is in error, and says how many characters are missing.
This field keeps its text as a String, not in a TextFieldState: value is shown, and onValueChange is asked for every change. The password is drawn as dots by visualTransformation. It is kept with remember and not with rememberSaveable: saved state may be written to disk. The password field that has a button to show its text is PantrixSecureTextField.
var password by remember { mutableStateOf("") }
val missing = 8 - password.length
val tooShort = password.isNotEmpty() && missing > 0
PantrixTextField(
value = password,
onValueChange = { password = it },
modifier = Modifier.fillMaxWidth(),
label = "Password",
supportingText = if (tooShort) "Characters missing: $missing" else "At least 8 characters",
error = tooShort,
showClearButton = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
visualTransformation = PasswordVisualTransformation(),
contentType = ContentType.NewPassword,
tagKey = "password"
)
A field whose text is held as a String
The PantrixTextField that takes a value and an onValueChange: the field shows value, and asks onValueChange for every change the user makes. A change the callback does not take is not shown: filtering is done there, and this overload takes no inputTransformation. The text asked for has to come back as value by the next frame, or the field shows the old text and the caret jumps: keep the text in state that the callback writes at once; where the text lives in a flow, pick the overload that takes a state.
This overload takes its lines through singleLine, minLines and maxLines, and is one line unless told otherwise. The field here is given an address of two lines, as if pasted from somewhere else: the field shows the line break as a space, and the second line is not cut off; the text that is held stays as it is.
var address by remember { mutableStateOf("Bağdat Cad. No: 12\nKat 3, Daire 8") }
PantrixTextField(
value = address,
onValueChange = { address = it },
modifier = Modifier.fillMaxWidth(),
label = "Address (one line)",
supportingText = "The line break in the text shows as a space",
showClearButton = true,
tagKey = "address_line"
)
Lines and the action key in the String overload
The overload that keeps its text as a String takes its lines through singleLine, minLines and maxLines, not through lineLimits. The first field is two lines tall and grows to four; past that, the text scrolls inside it.
The keyboard's action key, too, is answered by keyboardActions and not by onKeyboardAction: it says what to do for the action keyboardOptions names. Type something in the second field and press the keyboard's action key: the app's snackbar says what was typed.
val snackbars = LocalPantrixSnackbars.current
val scope = rememberCoroutineScope()
var message by remember { mutableStateOf("") }
var nickname by remember { mutableStateOf("") }
PantrixTextField(
value = message,
onValueChange = { message = it },
modifier = Modifier.fillMaxWidth(),
label = "Message",
singleLine = false,
minLines = 2,
maxLines = 4,
tagKey = "message"
)
PantrixTextField(
value = nickname,
onValueChange = { nickname = it },
modifier = Modifier.fillMaxWidth(),
label = "Nickname",
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(
onDone = { scope.launch { snackbars?.show(message = "Nickname: $nickname") } }
),
tagKey = "nickname"
)
Several lines and a character count
lineLimits = TextFieldLineLimits.MultiLine(minHeightInLines = 3) starts the box three lines tall; the box grows with its text, and Enter starts a new line. Tab does not type a tab, it moves the focus on: otherwise a keyboard would have no way forward out of the field.
With maxLength = 120 an edit that would pass the limit is turned down whole: typing stops at the limit, and a paste that does not fit is not put in at all. Characters are counted as String.length counts them, so an emoji can count as two. The count is written under the box, opposite the supporting text. Text set in code is not cut: where it is over the limit, the user can still make it shorter.
PantrixTextField(
state = rememberTextFieldState(),
modifier = Modifier.fillMaxWidth(),
label = "Note for the courier",
placeholder = "The doorbell does not work, please knock",
supportingText = "Passed on to the courier",
maxLength = 120,
lineLimits = TextFieldLineLimits.MultiLine(minHeightInLines = 3),
tagKey = "note"
)
A field given a height
A height given to the field is the whole field's: Modifier.height(160.dp). The label stays at the top, and the supporting text and the count at the bottom; the box fills the height they leave, and text that does not fit scrolls inside the box. No part of the field runs outside the height it is given.
Press the clear button: with its text gone, the box is still the same height.
PantrixTextField(
state = rememberTextFieldState(INSTRUCTIONS),
modifier = Modifier.fillMaxWidth().height(160.dp),
label = "Delivery instructions",
supportingText = "The field's height is fixed: the text scrolls inside the box",
showClearButton = true,
maxLength = 400,
lineLimits = TextFieldLineLimits.MultiLine(),
tagKey = "instructions"
)
private const val INSTRUCTIONS = "The building's street door is kept locked: ring the bell twice.\n" +
"If nobody answers, leave it at the guard's booth.\n" +
"If the parcel is heavy, take the lift: third floor.\n" +
"There is a dog at the door: it barks but does not bite.\n" +
"I am at home after six in the evening.\n" +
"If you cannot reach me, call the caretaker."
Input and output transformations
inputTransformation sees every change the user makes before it enters the field: the one here turns a change down whole if it holds anything but digits. With maxLength, the transformation runs first and the limit applies to what it lets through.
outputTransformation says how the text is shown, without changing it: the number's spaces are only drawn. Type "5321234567": the box shows "532 123 45 67", while phone.text, which the line below reads, has no spaces.
val phone = rememberTextFieldState()
PantrixTextField(
state = phone,
modifier = Modifier.fillMaxWidth(),
label = "Mobile phone",
placeholder = "5XX XXX XX XX",
supportingText = "Ten digits, without the leading zero",
maxLength = 10,
showCharacterCount = false,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone),
inputTransformation = DigitsOnly,
outputTransformation = PhoneSpaces,
tagKey = "phone"
)
PantrixText(text = "Text held: \"${phone.text}\"", color = PantrixTheme.colors.textSecondary)
private val DigitsOnly = InputTransformation {
if (!asCharSequence().all(Char::isDigit)) revertAllChanges()
}
private val PhoneSpaces = OutputTransformation {
// From the end to the start: each space added later stands to the left of the ones added before it, and does not shift their places.
listOf(8, 6, 3).forEach { at -> if (length > at) insert(at, " ") }
}
A field of numbers and the action key
A field a number is typed in, with something to do once it is typed, asks for its keyboard with numberKeyboardOptions: it gives the keyboard's type and its action key together. On Android the keyboard of numbers has an action key. An iPhone's number pad has none: ten digits and a key that takes one back. There a number typed in a field that asked for KeyboardType.Number cannot be sent; numberKeyboardOptions asks for the keyboard of numbers and punctuation in its place: it opens on its digits and has an action key.
Type a quantity and press the keyboard's action key, or Enter: the app's snackbar says the quantity. The keyboard on an iPhone also has signs and, a press away, letters: the field turns those down (inputTransformation).
val snackbars = LocalPantrixSnackbars.current
val scope = rememberCoroutineScope()
val quantity = rememberTextFieldState()
PantrixTextField(
state = quantity,
modifier = Modifier.fillMaxWidth(),
label = "Quantity",
maxLength = 4,
showCharacterCount = false,
keyboardOptions = numberKeyboardOptions(ImeAction.Done),
onKeyboardAction = {
scope.launch { snackbars?.show(message = "Quantity: ${quantity.text}", tagKey = "quantity") }
},
inputTransformation = DigitsOnly,
tagKey = "quantity"
)
private val DigitsOnly = InputTransformation {
if (!asCharSequence().all(Char::isDigit)) revertAllChanges()
}
Fields bound to a form
The PantrixTextField that takes a field gets its value, its error and its name from its field in the form. No tagKey is given: the field's id is the automation key (TextField_<id>). When the focus leaves an empty field, the rule's message takes the place of the supporting text; that is when the error first shows.
In a field given form, the keyboard's action key submits the form. In the first field the "Next" key moves the focus to the next field; in the second the action key submits the form through the "Save" button, and where a field is in error the focus goes to it. Leave both fields empty and press "Save".
val snackbars = LocalPantrixSnackbars.current
val scope = rememberCoroutineScope()
val form = rememberFormState()
PantrixTextField(
field = form.field("full_name", initial = "", Rules.notBlank("Enter your name")),
modifier = Modifier.fillMaxWidth(),
form = form,
label = "Full name",
supportingText = "As it appears on your card",
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words, imeAction = ImeAction.Next),
contentType = ContentType.PersonFullName
)
PantrixTextField(
field = form.field("email", initial = "", Rules.matches(EMAIL, "Enter a valid email address")),
modifier = Modifier.fillMaxWidth(),
form = form,
label = "Email",
placeholder = "name@example.com",
showClearButton = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
contentType = ContentType.EmailAddress
)
PantrixButton(
text = "Save",
onClick = {
scope.launch { form.submit { snackbars?.show(message = "Details saved", tagKey = "profile_saved") } }
},
form = form,
tagKey = "save_profile"
)
private val EMAIL = Regex("""\S+@\S+\.\S+""")
Disabled and read-only
enabled = false draws the field in the theme's disabled colours: the field takes no focus, cannot be typed into, and a screen reader is told it is disabled.
readOnly = true only stops the text being changed: the field can be focused, and its text selected and copied.
PantrixTextField(
state = rememberTextFieldState("Pantrix A.Ş."),
modifier = Modifier.fillMaxWidth(),
enabled = false,
label = "Disabled",
supportingText = "Set by your organisation",
tagKey = "company"
)
PantrixTextField(
state = rememberTextFieldState("SIP-2026-0914"),
modifier = Modifier.fillMaxWidth(),
readOnly = true,
label = "Read-only",
supportingText = "Cannot be changed; can be selected and copied",
tagKey = "order_no"
)
Its own colours
The colours given with colors are used, and those left out stay the theme's roles. The first field's colours are not written as fixed values but taken from the theme's success role: they are right in the dark theme too. The box's fill is the role's soft tone; the text and the icon are in the colour that reads on that tone; the border and the caret are the role's own colour. The label, the supporting text and the error colour are not given: they stay as the theme has them.
The second field is given only two colours, both the app's own purple: the border while the field has the focus, and the caret. Press the field, type something and select it: the selection's handles and its highlight take the caret's colour too.
val success = PantrixTheme.colors.success
PantrixTextField(
state = rememberTextFieldState("PANTRIX10"),
modifier = Modifier.fillMaxWidth(),
label = "Discount code",
supportingText = "Code valid: 10% off",
leadingIcon = {
PantrixIcon(
imageVector = PantrixIcons.Check,
contentDescription = null,
size = PantrixTheme.style(TextFieldStyle).iconSize
)
},
showClearButton = true,
colors = TextFieldColors(
container = success.container,
border = success.color,
hoveredBorder = success.color,
focusedBorder = success.color,
text = success.onContainer,
icon = success.onContainer,
cursor = success.color
),
tagKey = "coupon"
)
val own = ownColors()
PantrixTextField(
state = rememberTextFieldState(),
modifier = Modifier.fillMaxWidth(),
label = "Invitation code",
placeholder = "PX-0000",
colors = TextFieldColors(focusedBorder = own.color, cursor = own.color),
tagKey = "own_colors"
)
Its own style
style is this call's own style: only the fields it sets change, and the rest comes from the theme's and the platform's style.
The first field is a fully round box: TextFieldStyle(shape = ShapeRole.Full). The second takes the same style but holds several lines: the curve of a fully round box would run into the first line of its text, so the field is drawn with ShapeRole.Large. In the third, height raises the box's least height; the one line stands in the middle of the box.
PantrixTextField(
state = rememberTextFieldState(),
modifier = Modifier.fillMaxWidth(),
placeholder = "Search products",
showClearButton = true,
style = TextFieldStyle(shape = ShapeRole.Full),
contentDescription = "Search products",
tagKey = "pill"
)
PantrixTextField(
state = rememberTextFieldState(),
modifier = Modifier.fillMaxWidth(),
label = "Comment",
lineLimits = TextFieldLineLimits.MultiLine(minHeightInLines = 2),
style = TextFieldStyle(shape = ShapeRole.Full),
tagKey = "pill_comment"
)
PantrixTextField(
state = rememberTextFieldState(),
modifier = Modifier.fillMaxWidth(),
label = "Title",
style = TextFieldStyle(height = 56.dp),
tagKey = "tall"
)
Watching the interactions from outside
interactionSource hoists the field's interactions: its focus, its hover and its presses. Without one, the field keeps a source of its own. The line below reads from the same source: press the field, then bring the pointer over the box.
It is the box that reports the pointer: a pointer over the label or over the supporting text does not count.
val interactions = remember { MutableInteractionSource() }
val focused by interactions.collectIsFocusedAsState()
val hovered by interactions.collectIsHoveredAsState()
PantrixTextField(
state = rememberTextFieldState(),
modifier = Modifier.fillMaxWidth(),
label = "Subject",
supportingText = "The pointer counts only while it is over the box",
interactionSource = interactions,
tagKey = "observed"
)
PantrixText(
text = "Focused: ${if (focused) "yes" else "no"} · Pointer over it: ${if (hovered) "yes" else "no"}",
color = PantrixTheme.colors.textSecondary
)
API
state
A text field: one line of text or several that the user types, under a label, with help or an error's message below it, icons at either end, a button that clears it and a count of its characters.
| Parameter | Type | Default | What it is |
|---|---|---|---|
state | TextFieldState | the text and the selection. Create it with rememberTextFieldState(), and read what was typed from it: state.text where the text is used, or snapshotFlow { state.text.toString() } to follow it as it changes. | |
modifier | Modifier | Modifier | the modifier of the whole field: its label, its box and what is under it. fillMaxWidth() makes the box as wide as its parent. A height is the whole field's as well: the box fills what the label and the supporting text leave of it, with one line in its middle and several from its top, and a text too long for the box scrolls in it. |
enabled | Boolean | true | whether the field can be typed into. One that cannot takes no focus, is drawn in the disabled colours and says so. |
readOnly | Boolean | false | whether the text can only be read: it can still be focused, selected and copied. |
label | String? | null | what the field is for, written above the box: the field's name for a screen reader. |
placeholder | String? | null | shown in the box while it is empty. It is no label: it goes as soon as the user types. |
supportingText | String? | null | help under the box, in a smaller type. With error it is the message that says what to put right, which a screen reader announces as the error. |
error | Boolean | false | whether the text is wrong: the border, the label, the supporting text and the caret turn to the danger role, and a screen reader is told. |
leadingIcon | (@Composable () -> Unit)? | null | what stands before the text, in the secondary text colour. A slot does not size what is put in it: PantrixIcon(size = PantrixTheme.style(TextFieldStyle).iconSize) is the platform's size. |
trailingIcon | (@Composable () -> Unit)? | null | what stands after the text, and after the clear button. |
showClearButton | Boolean | false | shows a button that empties the field, while the field can be typed into and is not empty. It leaves the caret in the field. It is no stop for Tab, since a stop after every field doubles the presses through a form, and a screen reader still reaches it as a button. |
maxLength | Int? | null | the most characters the user can enter, counted as String.length counts them, so that an emoji can count as two. An edit that would pass the limit is turned down whole: typing stops at the limit, and a paste that does not fit is not put in. Text set in code is not cut, and the user can still make it shorter. It has to be positive. |
showCharacterCount | Boolean | true | shows count/maxLength under the box while maxLength is set. |
lineLimits | TextFieldLineLimits | TextFieldLineLimits.SingleLine | one line, or TextFieldLineLimits.MultiLine, in which the box grows with its text and Enter starts a new line. Past its most lines the text scrolls, and a thin bar at the box's end shows how far. Its icons and its clear button stand at its first line. Tab moves the focus on from either: a field of several lines does not type a tab, which would leave a keyboard no way forward out of it. |
keyboardOptions | KeyboardOptions | KeyboardOptions.Default | the keyboard's type, its capitals, its corrections and its action key. A field of numbers that has an action takes numberKeyboardOptions(imeAction): an iPhone's number pad has no key for one. A field of several lines that names an action loses the key for a new line on Android, whose keyboard shows the action in its place: such a field leaves the action unset, or is a PantrixTextArea. |
onKeyboardAction | KeyboardActionHandler? | null | called for the keyboard's action key: the key a software keyboard shows for the action keyboardOptions names, and Enter on a hardware keyboard in a field of one line. It is called only for a named action, since the default one never reaches a handler. Null keeps what the platform does: Next moves the focus on, and Done closes the keyboard. |
inputTransformation | InputTransformation? | null | what is done to the text as the user types it. With maxLength, it comes first and the limit applies to what it lets through. |
outputTransformation | OutputTransformation? | null | how the text is shown, without changing it: a phone number's spaces. |
contentType | ContentType? | null | what the field holds, for the platform to fill it in: ContentType.EmailAddress and the like. |
style | TextFieldStyle? | null | this call's own style: over every enclosing one and under the density. Only the fields it sets change, on every platform. |
colors | TextFieldColors? | null | this call's own colours, in place of the theme's roles: the ones it sets are used, and the rest stay the theme's. A disabled field is drawn in the theme's disabled colours whatever is given. |
contentDescription | String? | null | what a screen reader says for the field: its name where it has no label, as a search field has none. A placeholder that says the same is then left to the eye, so that the field is not named twice while it is empty. |
tagKey | String? | null | the field's automation key: the field is TextField_<tagKey>, and its clear button TextFieldClear_<tagKey>. The field reports that it took the focus and that it was cleared, and never its text. |
interactionSource | MutableInteractionSource? | null | the field's interactions, hoisted: its focus, its hover and its presses. It keeps a source of its own when this is null. |
value
A text field whose text is a String its caller holds: value is what it shows, and onValueChange is asked for every change the user makes.
| Parameter | Type | Default | What it is |
|---|---|---|---|
value | String | the text the field shows. | |
onValueChange | (String) -> Unit | asked for each change the user makes, with the whole new text, and by the clear button with an empty one. | |
modifier | Modifier | Modifier | the modifier of the whole field: its label, its box and what is under it. |
enabled | Boolean | true | whether the field can be typed into. One that cannot takes no focus, is drawn in the disabled colours and says so. |
readOnly | Boolean | false | whether the text can only be read: it can still be focused, selected and copied. |
label | String? | null | what the field is for, written above the box: the field's name for a screen reader. |
placeholder | String? | null | shown in the box while it is empty. |
supportingText | String? | null | help under the box. With error it is the message that says what to put right, which a screen reader announces as the error. |
error | Boolean | false | whether the text is wrong: the border, the label, the supporting text and the caret turn to the danger role, and a screen reader is told. |
leadingIcon | (@Composable () -> Unit)? | null | what stands before the text. A slot does not size what is put in it. |
trailingIcon | (@Composable () -> Unit)? | null | what stands after the text, and after the clear button. |
showClearButton | Boolean | false | shows a button that asks for an empty text, while the field can be typed into and is not empty. It leaves the caret in the field, and is no stop for Tab. |
maxLength | Int? | null | the most characters the user can enter, counted as String.length counts them. An edit that would pass the limit is turned down whole, before onValueChange is asked. A value over the limit is shown as it is, and the user can still make it shorter. It has to be positive. |
showCharacterCount | Boolean | true | shows count/maxLength under the box while maxLength is set. |
singleLine | Boolean | true | whether the field holds one line, which scrolls sideways. It does unless told otherwise, as the TextFieldState overload does, where Foundation's own text field starts with several. A line break in value is then shown as a space, as that overload shows it, and value keeps it. |
minLines | Int | 1 | the least lines the box is tall, in a field of several. |
maxLines | Int | if (singleLine) 1 else Int.MAX_VALUE | the most lines the box grows to before its text scrolls, in a field of several. |
keyboardOptions | KeyboardOptions | KeyboardOptions.Default | the keyboard's type, its capitals, its corrections and its action key. |
keyboardActions | KeyboardActions | KeyboardActions.Default | what the keyboard's action key does, for the action keyboardOptions names. Without one for that action the platform does what it does: Next moves the focus on, and Done closes the keyboard. |
visualTransformation | VisualTransformation | VisualTransformation.None | how the text is shown, without changing it: PasswordVisualTransformation() draws a dot for each character. |
contentType | ContentType? | null | what the field holds, for the platform to fill it in: ContentType.Password and the like. |
style | TextFieldStyle? | null | this call's own style: over every enclosing one and under the density. Only the fields it sets change, on every platform. |
colors | TextFieldColors? | null | this call's own colours, in place of the theme's roles: the ones it sets are used, and the rest stay the theme's. A disabled field is drawn in the theme's disabled colours whatever is given. |
contentDescription | String? | null | what a screen reader says for the field: its name where it has no label. A placeholder that says the same is then left to the eye. |
tagKey | String? | null | the field's automation key: the field is TextField_<tagKey>, and its clear button TextFieldClear_<tagKey>. The field reports that it took the focus and that it was cleared, and never its text. |
interactionSource | MutableInteractionSource? | null | the field's interactions, hoisted: its focus, its hover and its presses. It keeps a source of its own when this is null. |
field
A text field bound to a form's field: its text, its error and its name come from field.
| Parameter | Type | Default | What it is |
|---|---|---|---|
field | FormField<String> | the field the text field shows and changes. | |
modifier | Modifier | Modifier | the modifier of the whole field. |
form | FormState? | null | given, the keyboard's action key submits the form through the button registered with it: the form is checked, and then submitted or the focus sent to its first field in error. An action of Next or Previous moves the focus as it always does. A field of one line that names no action gets ImeAction.Done, since the default action never reaches a handler and Enter would submit nothing. A field of several lines is left alone: there Enter starts a new line. An onKeyboardAction of the caller's own takes the place of the submit. |
enabled | Boolean | true | whether the field can be typed into. |
readOnly | Boolean | false | whether the text can only be read. |
label | String? | null | what the field is for, written above the box: the field's name for a screen reader. |
placeholder | String? | null | shown in the box while it is empty. |
supportingText | String? | null | help under the box, while the field has no error to show there. |
leadingIcon | (@Composable () -> Unit)? | null | what stands before the text. |
trailingIcon | (@Composable () -> Unit)? | null | what stands after the text, and after the clear button. |
showClearButton | Boolean | false | shows a button that empties the field, while it can be typed into and is not empty. |
maxLength | Int? | null | the most characters the user can enter. It has to be positive. |
showCharacterCount | Boolean | true | shows count/maxLength under the box while maxLength is set. |
lineLimits | TextFieldLineLimits | TextFieldLineLimits.SingleLine | one line, or TextFieldLineLimits.MultiLine. Tab moves the focus on from either. |
keyboardOptions | KeyboardOptions | KeyboardOptions.Default | the keyboard's type, its capitals, its corrections and its action key. |
onKeyboardAction | KeyboardActionHandler? | null | called for the keyboard's action key, in place of the form's submit. |
inputTransformation | InputTransformation? | null | what is done to the text as the user types it. |
outputTransformation | OutputTransformation? | null | how the text is shown, without changing it. |
contentType | ContentType? | null | what the field holds, for the platform to fill it in. |
style | TextFieldStyle? | null | this call's own style: over every enclosing one and under the density. Only the fields it sets change, on every platform. |
colors | TextFieldColors? | null | this call's own colours, in place of the theme's roles: the ones it sets are used, and the rest stay the theme's. A disabled field is drawn in the theme's disabled colours whatever is given. |
contentDescription | String? | null | what a screen reader says for the field where it has no label. |
interactionSource | MutableInteractionSource? | null | the field's interactions, hoisted. It keeps a source of its own when this is null. |
TextFieldStyle
PantrixTextField's box: its shape, how tall and how wide it is at the least, the room around its text, its icons and its border.
| Parameter | Type | Default | What it is |
|---|---|---|---|
shape | ShapeSpec | ShapeSpec.Unspecified | The box's shape. A fully round one, ShapeRole.Full, is a pill only while the field holds one line: a field of several lines takes ShapeRole.Large instead, since a capsule's curve runs into the first line of its text. |
height | Dp | Dp.Unspecified | The least the box is tall. |
minWidth | Dp | Dp.Unspecified | The least the box is wide, which keeps an empty field from growing as the user types into it. |
horizontalPadding | Dp | Dp.Unspecified | The room between the box's edge and its text, where no icon is. |
verticalPadding | Dp | Dp.Unspecified | The room above the first line of the text and below the last. |
iconPadding | Dp | Dp.Unspecified | The room around an icon in its slot. |
iconSize | Dp | Dp.Unspecified | The size an icon in a slot should take. A slot does not size what is put in it. |
borderWidth | Dp | Dp.Unspecified | How thick the box's border is. |
focusedBorderWidth | Dp | Dp.Unspecified | How thick the border is while the field has the focus: the border is what shows it. |
textRole | TypeRole? | null | The role of the type scale the text is typed in, and its placeholder set in: the body's, 16sp, on Android and iOS, as Material 3 and iOS type a field, and the label's, 14sp, on the desktop and the web, as a page under a pointer does. A select's box and the fields made on this style take it as well. |
TextFieldColors
What PantrixTextField is painted with, for a field that does not take the theme's: a colour that is set is used, and one that is left out stays the theme's role. A product gives the colours it changes and no others:
| Parameter | Type | Default | What it is |
|---|---|---|---|
container | Color | Color.Unspecified | The box's fill. The theme's surface. |
border | Color | Color.Unspecified | The box's border at rest. The theme's border. |
hoveredBorder | Color | Color.Unspecified | The border under a pointer. The theme's borderHover, a notch stronger than border. |
focusedBorder | Color | Color.Unspecified | The border while the field has the focus. The theme's primary. |
error | Color | Color.Unspecified | What says the text is wrong: the border, the label, the message under the box, the caret, and the count once it is past its limit. The theme's danger. |
text | Color | Color.Unspecified | The typed text. The theme's textPrimary. |
label | Color | Color.Unspecified | The label above the box. Left out, it is drawn as any text is where the field stands, since it is outside the box on whatever the field is put on: in textPrimary on the theme's own ground, and in the content colour of a fill. |
placeholder | Color | Color.Unspecified | The placeholder. The theme's textSecondary. |
supportingText | Color | Color.Unspecified | The help under the box, and the count. The theme's textSecondary. |
icon | Color | Color.Unspecified | What an icon in a slot is drawn in unless it sets a tint of its own, and the clear button's cross. The theme's textSecondary. |
cursor | Color | Color.Unspecified | The caret, and with it the handles and the highlight of a selection. The theme's primary. |
scrollBar | Color | Color.Unspecified | The bar that shows how far the text of a box of several lines is scrolled. The theme's borderStrong: it is a mark beside the text, and the box's soft edge would not be seen there. |
Accessibility
- Keyboard and transformations.
keyboardOptionspicks the keyboard's type and its action key, andonKeyboardActionis called when that key is pressed. A field a number is typed in that has an action asks for its keyboard withnumberKeyboardOptions: an iPhone's number pad has no action key.inputTransformationchanges what is typed, or turns it down, before it enters the field;outputTransformationsays how the text is shown, without changing it.contentTypetells the platform what the field holds, so that the platform can fill it in. - Focus, disabled and read-only. The box's border is what shows the focus: in the
primarycolour and thicker, and no ring is drawn as well. A field under a dialog or a sheet keeps its focus, but is drawn at rest and without its caret. A field withenabled = falsetakes no focus; areadOnlyone can be focused, and its text selected and copied.