Date Text Field
A text field for a day: the day is typed in digits, or chosen from a calendar that a button at the box's end opens.
import com.pantrixui.components.date.datetextfield.PantrixDateTextField
- Digits alone. The field takes digits alone; as each part is whole its mark comes by itself: in Turkish
18102026becomes18.10.2026, in American English10182026becomes10/18/2026. A phone opens its keyboard of digits. Backspace after a mark deletes the digit before it. - A pasted day. A day written out is read as it is written: in the field's order first, and else with the year first, as in ISO 8601. In Turkish
2026-10-18becomes18.10.2026too; a year of two digits is one of the hundred years around today (18.10.26, 2026). A text not read so comes in as its digits. - Format.
formatgives the order of the parts and the mark between them; by default it comes from the theme's language and region: Turkish and GermanGG.AA.YYYY, Britain and ArabicDD/MM/YYYY, the USMM/DD/YYYY, a language not known ISO 8601 (YYYY-MM-DD). While empty the field shows the pattern; a screen reader hears it as the order of the parts ("Day, Month, Year"). - Value.
onValueChangeis called each time the field's day changes: a whole day the calendar takes, or null where the field holds nothing, half a day, digits that make no day or a day it does not take. A half-typed day stays on the screen whilevalueis null; another day from outside is written into the field. - Its own error. The field writes what is typed wrong under the box itself: a date not finished, once the field is left, a day the calendar has not (31 February), a day outside
minDateandmaxDate, a dayisDateEnableddoes not take. The app'serrorcomes first; its message issupportingText. - Calendar. It opens with the button, with Alt+↓ on the desktop and the web, by pressing the box of a read-only field, and with Enter or Space.
presentationsays what it opens in: by default a sheet in a narrow window and a popover under the box in a wider one; or in every window a popover, a sheet or a dialog; the same on every platform. Pressing a day in the calendar takes the day and closes the calendar; Escape and pressing outside close it with nothing changed. Opened with the keyboard the focus goes to the chosen day, or else to today; once it closes it is back on the field. - Three overloads. The one with
valueandonValueChange; the one withstate(rememberDateTextFieldState()):dateis the field's day, setting it puts the day in the field,texttells a half-typed field from an empty one; the one withfieldbinds to a form's field. - Date type. Days are
PantrixDates; to convert them tokotlinx-datetimeorjava.time, docs/guide/date-time/DATE_TIME_GUIDE.md.
Examples
A birthday, typed
A birthday, typed: a day after today is not taken. The calendar opens too, but typing a far year is quicker. Once the day is whole it is written below; left half typed, the field shows its error that says the pattern.
var birthday by remember { mutableStateOf<PantrixDate?>(null) }
val strings = PantrixTheme.strings
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
PantrixDateTextField(
value = birthday,
onValueChange = { birthday = it },
label = "Birthday",
maxDate = rememberToday(),
showClearButton = true,
tagKey = "birthday"
)
PantrixText(
text = birthday?.let { "Birthday: ${strings.date(it.day, it.month, it.year)}" } ?: "No day yet",
style = PantrixTheme.typography.body
)
}
A delivery day on a weekday
A delivery day on a weekday within the next month. In the calendar the weekends are faint. A Saturday typed is not taken and the field says "This day cannot be chosen"; a day past the month says the bounds.
var delivery by remember { mutableStateOf<PantrixDate?>(null) }
val today = rememberToday()
PantrixDateTextField(
value = delivery,
onValueChange = { delivery = it },
label = "Delivery day",
supportingText = "Delivered on weekdays only",
minDate = today.plusDays(1),
maxDate = today.plusMonths(1),
isDateEnabled = { it.dayOfWeek <= FRIDAY },
tagKey = "delivery"
)
private const val FRIDAY = 5
The calendar in a sheet or a dialog
The calendar opens in a sheet: near the thumb on a phone, as wide as the screen. presentation = Dialog opens it in a dialog in the middle of the window. A day chosen closes the sheet and is written into the field.
var appointment by remember { mutableStateOf<PantrixDate?>(null) }
var presentation by remember { mutableStateOf(DatePickerPresentation.Sheet) }
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
PantrixSegmentButton(
options = listOf(
SegmentOption(DatePickerPresentation.Popover, "Popover", tagKey = "popover"),
SegmentOption(DatePickerPresentation.Sheet, "Sheet", tagKey = "sheet"),
SegmentOption(DatePickerPresentation.Dialog, "Dialog", tagKey = "dialog")
),
selected = presentation,
onSelect = { presentation = it },
fullWidth = false,
tagKey = "presentation"
)
PantrixDateTextField(
value = appointment,
onValueChange = { appointment = it },
label = "Appointment day",
minDate = rememberToday(),
presentation = presentation,
tagKey = "appointment"
)
}
The app's error and disabled
The app's own error comes before the field's: a day already taken is said with error and supportingText. The field under it is disabled: it takes no focus and opens no calendar.
var meeting by remember { mutableStateOf<PantrixDate?>(PantrixDate(2026, 11, 2)) }
val taken = meeting == PantrixDate(2026, 11, 2)
PantrixDateTextField(
value = meeting,
onValueChange = { meeting = it },
label = "Meeting day",
supportingText = if (taken) "This day is taken, choose another" else "A day the office is open",
error = taken,
tagKey = "meeting"
)
PantrixDateTextField(
value = PantrixDate(2026, 10, 1),
onValueChange = {},
enabled = false,
label = "Contract start",
tagKey = "contract"
)
Read-only: from the calendar alone
A read-only field is not typed into: the day is chosen from the calendar alone. A press on the box, Enter or Space opens the calendar. For a place where a day must be chosen, and what is typed must not be misread.
var checkIn by remember { mutableStateOf<PantrixDate?>(null) }
PantrixDateTextField(
value = checkIn,
onValueChange = { checkIn = it },
readOnly = true,
label = "Check-in day",
placeholder = "Choose from the calendar",
minDate = rememberToday(),
tagKey = "check_in"
)
Bound to a form
Bound to a form's field: with no day, "Save" shows the error and sends the focus to the field. The error shows on leaving the field too; going into the calendar is no leaving it. Digits that make no day leave the value null, and the rule catches it.
val form = rememberFormState()
val scope = rememberCoroutineScope()
val report = LocalExampleEvents.current
val start = form.field<PantrixDate?>("start", initial = null, Rules.required("Enter the start day"))
PantrixDateTextField(
field = start,
modifier = Modifier.fillMaxWidth(),
form = form,
label = "Start day",
minDate = rememberToday()
)
PantrixButton(
text = "Save",
onClick = { scope.launch { form.submit { report("Saved") } } },
form = form,
tagKey = "save_start"
)
On a state, as ISO 8601 writes a day
On a state: state.date is the field's day, state.text the digits typed. Together they tell an empty field from a half-typed one. ISO 8601 reads the same in every language. The button changes the state's day, and the field writes it in itself.
val state = rememberDateTextFieldState()
val today = rememberToday()
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
PantrixDateTextField(
state = state,
label = "Report day",
format = DateFieldFormat.Iso,
tagKey = "report_day"
)
PantrixText(
text = when {
state.date != null -> "Day: ${state.date}"
state.text.isEmpty() -> "The field is empty"
else -> "Half typed: ${state.text}"
},
style = PantrixTheme.typography.body
)
PantrixButton(
text = "Write today",
onClick = { state.date = today },
variant = ButtonVariant.Outline,
tagKey = "write_today"
)
}
An own format
The field's format comes from the theme's language; format says another. The formats of most languages are ready in DateFieldFormat (DayMonthYearDots, MonthDayYearSlashes, Iso, …); an own format is the order of the parts (DateFieldOrder) and a mark: the first here puts the year first, with slashes, as Japanese does. Change the format: the field's day stays the same, only the way it is written changes.
var shipping by remember { mutableStateOf<PantrixDate?>(PantrixDate(2026, 10, 18)) }
var format by remember { mutableStateOf(DateFieldFormat(DateFieldOrder.YearMonthDay, '/')) }
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
PantrixSegmentButton(
options = listOf(
SegmentOption(DateFieldFormat(DateFieldOrder.YearMonthDay, '/'), "YYYY/MM/DD", tagKey = "year_first"),
SegmentOption(DateFieldFormat.MonthDayYearSlashes, "MM/DD/YYYY", tagKey = "month_first"),
SegmentOption(DateFieldFormat(DateFieldOrder.DayMonthYear, '-'), "DD-MM-YYYY", tagKey = "day_first")
),
selected = format,
onSelect = { format = it },
fullWidth = false,
tagKey = "format"
)
PantrixDateTextField(
value = shipping,
onValueChange = { shipping = it },
label = "Shipping day",
format = format,
tagKey = "shipping"
)
}
A sheet in a narrow window
The default presentation = DatePickerPresentation.Adaptive: in a narrow window the calendar opens in a sheet from the bottom, near the finger and as wide as the window; in a wider window in a popover under the box. Pick "Narrow" in the card's window selector and open the calendar. It is chosen as the calendar opens: it does not change while it is open, even if the window does.
var day by remember { mutableStateOf<PantrixDate?>(null) }
// The card's window is as tall as the example: the example is kept tall so that a sheet opened in a narrow window has room.
Column(Modifier.fillMaxWidth().height(460.dp)) {
PantrixDateTextField(
value = day,
onValueChange = { day = it },
modifier = Modifier.fillMaxWidth(),
label = "Meeting day",
minDate = rememberToday(),
presentation = DatePickerPresentation.Adaptive,
tagKey = "adaptive_day"
)
}
API
value
A text field for a day: the day is typed in digits, or chosen from a calendar that a button at the box's end opens.
| Parameter | Type | Default | What it is |
|---|---|---|---|
value | PantrixDate? | the day the field shows, or null. | |
onValueChange | (PantrixDate?) -> Unit | told the field's day each time the user changes it: a day, or null for none. | |
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 used. One that cannot takes no focus and opens no calendar. |
readOnly | Boolean | false | whether the day can only be chosen from the calendar, and not typed. |
label | String? | null | what the field is for, written above the box: the field's name for a screen reader, and its calendar's. |
placeholder | String? | null | shown in the box while it is empty, in the place of the pattern the field shows by itself. |
supportingText | String? | null | help under the box. With error it is the message that says what to put right; without it, the field's own error takes its place while there is one. |
error | Boolean | false | whether the caller finds the day wrong, as a day already taken: the field is drawn in error, and supportingText is the message. |
leadingIcon | (@Composable () -> Unit)? | null | what stands before the text. A slot does not size what is put in it. |
showClearButton | Boolean | false | shows a button that empties the field, while it can be typed into and is not empty. |
minDate | PantrixDate? | null | the first day the field takes, or null for no first. Not after maxDate. |
maxDate | PantrixDate? | null | the last day the field takes, or null for no last. |
isDateEnabled | ((PantrixDate) -> Boolean)? | null | whether the field takes a day between the two, or null for every day: a weekday, a day not yet booked. |
firstDayOfWeek | Int? | null | the day the calendar's weeks start on, 1 for Monday to 7 for Sunday as ISO 8601 numbers them, or null for the language's. |
today | PantrixDate | rememberToday() | the day the calendar marks as today: by default the one the screen's clock shows (rememberToday), which turns at midnight. |
format | DateFieldFormat | DateFieldFormat.of(PantrixTheme.locale) | the order the parts are typed in and the mark between them: the theme's language's and region's by default. of: The role of the theme this tone is drawn in. |
presentation | DatePickerPresentation | DatePickerPresentation.Adaptive | what the calendar opens in: by default a sheet in a narrow window and a popover under the box in a wider one, chosen as it opens; or always a popover, a sheet or a dialog. |
style | TextFieldStyle? | null | this call's own style of the field: over every enclosing one and under the density. |
colors | TextFieldColors? | null | this call's own colours of the field, in place of the theme's roles. |
pickerStyle | DatePickerStyle? | null | this call's own style of the calendar. |
pickerColors | DatePickerColors? | null | this call's own colours of the calendar. |
contentDescription | String? | null | what a screen reader says for the field where it has no label. |
tagKey | String? | null | the field's automation key: the field is DateTextField_<tagKey>, its button DateTextFieldCalendar_<tagKey>, its clear button DateTextFieldClear_<tagKey> and its calendar DatePicker_<tagKey>. The field reports that the calendar opened and that a day was chosen in it, and never the day. |
interactionSource | MutableInteractionSource? | null | the field's interactions, hoisted: its focus, its hover and its presses. |
state
A date field on a state its caller keeps, as PantrixDateTextField of a value is in every other way: the state's date is the field's day, setting it writes the day in, and its text tells a field that is half typed from an empty one.
| Parameter | Type | Default | What it is |
|---|---|---|---|
state | DateTextFieldState | the digits typed and the day they make. Create it with rememberDateTextFieldState(). | |
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 used. One that cannot takes no focus and opens no calendar. |
readOnly | Boolean | false | whether the day can only be chosen from the calendar, and not typed. |
label | String? | null | what the field is for, written above the box: the field's name for a screen reader, and its calendar's. |
placeholder | String? | null | shown in the box while it is empty, in the place of the pattern the field shows by itself. |
supportingText | String? | null | help under the box. With error it is the message; without it, the field's own error takes its place while there is one. |
error | Boolean | false | whether the caller finds the day wrong: the field is drawn in error, and supportingText is the message. |
leadingIcon | (@Composable () -> Unit)? | null | what stands before the text. |
showClearButton | Boolean | false | shows a button that empties the field, while it can be typed into and is not empty. |
minDate | PantrixDate? | null | the first day the field takes, or null for no first. Not after maxDate. |
maxDate | PantrixDate? | null | the last day the field takes, or null for no last. |
isDateEnabled | ((PantrixDate) -> Boolean)? | null | whether the field takes a day between the two, or null for every day. |
firstDayOfWeek | Int? | null | the day the calendar's weeks start on, 1 for Monday to 7 for Sunday, or null for the language's. |
today | PantrixDate | rememberToday() | the day the calendar marks as today: by default the one the screen's clock shows (rememberToday). |
format | DateFieldFormat | DateFieldFormat.of(PantrixTheme.locale) | the order the parts are typed in and the mark between them: the theme's locale's by default. of: The role of the theme this tone is drawn in. |
presentation | DatePickerPresentation | DatePickerPresentation.Adaptive | what the calendar opens in: by default a sheet in a narrow window and a popover under the box in a wider one, chosen as it opens; or always a popover, a sheet or a dialog. |
style | TextFieldStyle? | null | this call's own style of the field. |
colors | TextFieldColors? | null | this call's own colours of the field. |
pickerStyle | DatePickerStyle? | null | this call's own style of the calendar. |
pickerColors | DatePickerColors? | null | this call's own colours of the calendar. |
contentDescription | String? | null | what a screen reader says for the field where it has no label. |
tagKey | String? | null | the field's automation key, as PantrixDateTextField of a value takes it. |
interactionSource | MutableInteractionSource? | null | the field's interactions, hoisted. |
field
A date field bound to a form's field: its day, its error and its name come from field.
| Parameter | Type | Default | What it is |
|---|---|---|---|
field | FormField<PantrixDate?> | the form's field the date 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. |
enabled | Boolean | true | whether the field can be used. |
readOnly | Boolean | false | whether the day can only be chosen from the calendar. |
label | String? | null | what the field is for, written above the box. |
placeholder | String? | null | shown in the box while it is empty, in the place of the pattern. |
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. |
showClearButton | Boolean | false | shows a button that empties the field, while it can be typed into and is not empty. |
minDate | PantrixDate? | null | the first day the field takes, or null for no first. Not after maxDate. |
maxDate | PantrixDate? | null | the last day the field takes, or null for no last. |
isDateEnabled | ((PantrixDate) -> Boolean)? | null | whether the field takes a day between the two, or null for every day. |
firstDayOfWeek | Int? | null | the day the calendar's weeks start on, 1 for Monday to 7 for Sunday, or null for the language's. |
today | PantrixDate | rememberToday() | the day the calendar marks as today: by default the one the screen's clock shows (rememberToday). |
format | DateFieldFormat | DateFieldFormat.of(PantrixTheme.locale) | the order the parts are typed in and the mark between them: the theme's locale's by default. of: The role of the theme this tone is drawn in. |
presentation | DatePickerPresentation | DatePickerPresentation.Adaptive | what the calendar opens in: by default a sheet in a narrow window and a popover under the box in a wider one, chosen as it opens; or always a popover, a sheet or a dialog. |
style | TextFieldStyle? | null | this call's own style of the field. |
colors | TextFieldColors? | null | this call's own colours of the field. |
pickerStyle | DatePickerStyle? | null | this call's own style of the calendar. |
pickerColors | DatePickerColors? | null | this call's own colours of the calendar. |
contentDescription | String? | null | what a screen reader says for the field where it has no label. |
interactionSource | MutableInteractionSource? | null | the field's interactions, hoisted. |
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. |