Time Text Field
A text field for a time of day: the time is typed in digits, or chosen from a time picker that a button at the box's end opens.
import com.pantrixui.components.date.timetextfield.PantrixTimeTextField
- Digits alone. The field takes four digits at most; once the hour is whole the colon comes by itself:
1430becomes14:30. A first digit that cannot start an hour is an hour of one digit:930becomes09:30. A phone opens its keyboard of digits. - A pasted time. A time written out is read as it is written:
14:30,14.30,1430, or with a half of the day,2:30 PM,ÖS 2:30. A second written after the minute is let go. - Twelve hours. With
is24Hour = falsethe hours are typed 01 to 12, and the half of the day stands in the box beside the digits as a button: a press, or Space while it has the focus, turns it, and on desktop and the web A and P do too. The default is 24 hours, as the picker's. - Value.
onValueChangeis called each time the field's time changes: a whole time the field takes, or null while the field is empty, half typed, or holds digits that make no time. A half-typed time stays on the screen whilevalueis null; another time from outside is written into the field, its second kept until the hour and minute are typed over. - Its own error. The field writes what is wrong under the box itself: an unfinished time on leaving the field, a time the clock has not (25:00), a time outside
minTimeandmaxTime. WhereminTimeis aftermaxTimethe times run past midnight, as a night shift's. The app'serrorcomes first; its message issupportingText. - Picker. 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;
presentationchooses what it opens in: by default a sheet in a narrow window and a popover in a wider one; or in every window a popover, a sheet or a dialog. Every press in the picker writes the time into the field at once and the picker stays open, so the hour is chosen first and then the minutes; Enter on a value, Escape and pressing outside close it. Opened with the keyboard the focus goes to the chosen hour, or else to the current one.minuteStepsets only the picker's minutes; a minute in between can be typed. - Three forms. The form that takes
valueandonValueChange; the form that takes astate(rememberTimeTextFieldState()):timeis the field's time,texttells a half-typed field from an empty one; the form that takes afieldis bound to a form's field. - The time type. Times are
PantrixTime; to put one together with a day, and to turn it into other libraries' types, see docs/guide/date-time/DATE_TIME_GUIDE.md.
Examples
A meeting's start, on the quarter hour
A meeting's start, on the quarter hour: the picker's minutes come fifteen apart. A minute between them can be typed too. Once the time is whole it is written below.
var start by remember { mutableStateOf<PantrixTime?>(null) }
val strings = PantrixTheme.strings
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
PantrixTimeTextField(
value = start,
onValueChange = { start = it },
label = "Meeting start",
minuteStep = QUARTER,
showClearButton = true,
tagKey = "meeting_start"
)
PantrixText(
text = start?.let { "Start: ${strings.time(it.hour, it.minute, is24Hour = true)}" }
?: "No time yet",
style = PantrixTheme.typography.body
)
}
private const val QUARTER = 15
Within working hours
A call within working hours: a time typed before 09:00 or after 18:00 is not taken, and the field says the two ends. In the picker the hours outside are faint and cannot be pressed.
var call by remember { mutableStateOf<PantrixTime?>(null) }
PantrixTimeTextField(
value = call,
onValueChange = { call = it },
label = "Call time",
supportingText = "Weekdays from 09:00 to 18:00",
minTime = PantrixTime(OPENS, 0),
maxTime = PantrixTime(CLOSES, 0),
tagKey = "call"
)
private const val OPENS = 9
private const val CLOSES = 18
A clock of twelve hours
A clock of twelve hours: the hours are typed 01 to 12, and the half of the day stands in the box beside the digits. A press on it turns it between AM and PM; the picker has it as a third column too.
var alarm by remember { mutableStateOf<PantrixTime?>(PantrixTime(7, 30)) }
val strings = PantrixTheme.strings
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
PantrixTimeTextField(
value = alarm,
onValueChange = { alarm = it },
label = "Alarm",
is24Hour = false,
tagKey = "alarm"
)
PantrixText(
text = alarm?.let { "Alarm: ${strings.time(it.hour, it.minute, is24Hour = false)}" } ?: "Alarm off",
style = PantrixTheme.typography.body
)
}
The picker in a sheet or a dialog
The picker 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. The sheet stays open at each press; a press outside or back closes it.
var pickup by remember { mutableStateOf<PantrixTime?>(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"
)
PantrixTimeTextField(
value = pickup,
onValueChange = { pickup = it },
label = "Pickup time",
minuteStep = FIVE_MINUTES,
presentation = presentation,
tagKey = "pickup"
)
}
private const val FIVE_MINUTES = 5
The app's error and disabled
The app's own error comes before the field's: a time already taken is said with error and supportingText. The field under it is disabled: it takes no focus and opens no picker.
var slot by remember { mutableStateOf<PantrixTime?>(PantrixTime(BOOKED, 0)) }
val taken = slot == PantrixTime(BOOKED, 0)
PantrixTimeTextField(
value = slot,
onValueChange = { slot = it },
label = "Appointment time",
supportingText = if (taken) "This time is taken, choose another" else "A half-hour appointment",
error = taken,
tagKey = "slot"
)
PantrixTimeTextField(
value = PantrixTime(OPENS, 0),
onValueChange = {},
enabled = false,
label = "Opening time",
tagKey = "opening"
)
private const val OPENS = 9
private const val BOOKED = 14
Read-only: from the picker alone
A read-only field is not typed into: the time is chosen from the picker alone. A press on the box, Enter or Space opens the picker. For a place where a time must be chosen from a list.
var departure by remember { mutableStateOf<PantrixTime?>(null) }
PantrixTimeTextField(
value = departure,
onValueChange = { departure = it },
readOnly = true,
label = "Departure time",
placeholder = "Choose from the picker",
minuteStep = QUARTER,
tagKey = "departure"
)
private const val QUARTER = 15
Bound to a form
Bound to a form's field: with no time, "Save" shows the error and sends the focus to the field. The error shows on leaving the field too; going into the picker is no leaving it. Digits that make no time leave the value null, and the rule catches it.
val form = rememberFormState()
val scope = rememberCoroutineScope()
val report = LocalExampleEvents.current
val start = form.field<PantrixTime?>("shift_start", initial = null, Rules.required("Enter the shift's start"))
PantrixTimeTextField(
field = start,
modifier = Modifier.fillMaxWidth(),
form = form,
label = "Shift start",
minuteStep = QUARTER
)
PantrixButton(
text = "Save",
onClick = { scope.launch { form.submit { report("Saved") } } },
form = form,
tagKey = "save_shift"
)
private const val QUARTER = 15
On a state
On a state: state.time is the field's time, state.text the digits typed. Together they tell an empty field from a half-typed one. The button changes the state's time, and the field writes it in itself, by a clock of twelve hours with the half of the day.
val state = rememberTimeTextFieldState()
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
PantrixTimeTextField(
state = state,
label = "Reminder",
is24Hour = false,
tagKey = "reminder"
)
PantrixText(
text = when {
state.time != null -> "Time: ${state.time}"
state.text.isEmpty() -> "The field is empty"
else -> "Half typed: ${state.text}"
},
style = PantrixTheme.typography.body
)
PantrixButton(
text = "Write now",
onClick = { state.time = PantrixTime.now() },
variant = ButtonVariant.Outline,
tagKey = "write_now"
)
}
API
value
A text field for a time of day: the time is typed in digits, or chosen from a time picker that a button at the box's end opens.
| Parameter | Type | Default | What it is |
|---|---|---|---|
value | PantrixTime? | the time the field shows, or null. | |
onValueChange | (PantrixTime?) -> Unit | told the field's time each time the user changes it: a time, 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 picker. |
readOnly | Boolean | false | whether the time can only be chosen from the picker, 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 picker'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 time wrong, as a time already booked: 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. |
minTime | PantrixTime? | null | the first time the field takes, or null for no first. Where it is after maxTime, the times run past midnight, as a night's do. |
maxTime | PantrixTime? | null | the last time the field takes, or null for no last. |
minuteStep | Int | 1 | how many minutes apart the picker's minutes are, 1 to 60. A minute typed between them is taken. |
is24Hour | Boolean | true | whether the field and its picker count the hours of a day, 00 to 23, or by a clock of twelve hours. |
presentation | DatePickerPresentation | DatePickerPresentation.Adaptive | what the picker 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 | TimePickerStyle? | null | this call's own style of the picker. |
pickerColors | TimePickerColors? | null | this call's own colours of the picker. |
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 TimeTextField_<tagKey>, its button TimeTextFieldClock_<tagKey>, its half of the day TimeTextFieldPeriod_<tagKey>, its clear button TimeTextFieldClear_<tagKey> and its picker TimePicker_<tagKey>. The field reports that the picker opened and that a value was chosen in it, and never the time. |
interactionSource | MutableInteractionSource? | null | the field's interactions, hoisted: its focus, its hover and its presses. |
state
A time field on a state its caller keeps, as PantrixTimeTextField of a value is in every other way: the state's time is the field's time, setting it writes the time in, and its text tells a field that is half typed from an empty one.
| Parameter | Type | Default | What it is |
|---|---|---|---|
state | TimeTextFieldState | the digits typed and the time they make. Create it with rememberTimeTextFieldState(). | |
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 picker. |
readOnly | Boolean | false | whether the time can only be chosen from the picker, 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 picker'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 time 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. |
minTime | PantrixTime? | null | the first time the field takes, or null for no first; after maxTime, the times run past midnight. |
maxTime | PantrixTime? | null | the last time the field takes, or null for no last. |
minuteStep | Int | 1 | how many minutes apart the picker's minutes are, 1 to 60. |
is24Hour | Boolean | true | whether the field and its picker count the hours of a day, or by a clock of twelve hours. |
presentation | DatePickerPresentation | DatePickerPresentation.Adaptive | what the picker 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 | TimePickerStyle? | null | this call's own style of the picker. |
pickerColors | TimePickerColors? | null | this call's own colours of the picker. |
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 PantrixTimeTextField of a value takes it. |
interactionSource | MutableInteractionSource? | null | the field's interactions, hoisted. |
field
A time field bound to a form's field: its time, its error and its name come from field.
| Parameter | Type | Default | What it is |
|---|---|---|---|
field | FormField<PantrixTime?> | the form's field the time 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 time can only be chosen from the picker. |
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. |
minTime | PantrixTime? | null | the first time the field takes, or null for no first; after maxTime, the times run past midnight. |
maxTime | PantrixTime? | null | the last time the field takes, or null for no last. |
minuteStep | Int | 1 | how many minutes apart the picker's minutes are, 1 to 60. |
is24Hour | Boolean | true | whether the field and its picker count the hours of a day, or by a clock of twelve hours. |
presentation | DatePickerPresentation | DatePickerPresentation.Adaptive | what the picker 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 | TimePickerStyle? | null | this call's own style of the picker. |
pickerColors | TimePickerColors? | null | this call's own colours of the picker. |
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. |