-
Notifications
You must be signed in to change notification settings - Fork 0
LayrzDurationInput
Time span selection field with day, hour, minute, and second components.
Specification Status:
LayrzDurationInputhas shipped and is implemented atlib/src/inputs/src/duration/duration_input.dart(+duration_picker_panel.dart). The section below marked Shipped behavior describes the actual, current implementation.Everything below that, starting at "Metadata", is the original pre-implementation design sketch carried over from the
layrz_thememigration plan. It predates the actual implementation and does not match the shipped API in several places (for example: the shipped surface isLayrzAnchoredPanelon desktop /LayrzBottomSheeton mobile, not aRawDialog;visibleUnitsis aSet<LayrzDurationUnit>, notList<LDurationUnit>; the summary format is controlled by theLayrzDurationFormatenum (long/short), not a fixed "and"-joined humanization; there is nolabelwidget parameter,suffixIcon, orprefixIcon— the field exposes no prefix/suffix parameters at all). Reconciling the rest of this legacy sketch with the shipped widget is out of scope for this unit and is left for a dedicated documentation pass.
LayrzDurationInput renders a clock-style icon (MdiIcons.clockOutline) that identifies the
field as a duration picker. Added because the field previously had no visual affordance at
all distinguishing it from a plain read-only text field.
-
External sibling, not a slot. The icon is not placed in the chrome's
prefixSlotorsuffixSlot— both remain completely empty (LayrzInputPrefixSlot()/LayrzInputSuffixSlot(), no icon/widget/text) and free for a caller to use, the same way every other slot-bearing input in this package keeps its slots free of its own chrome. The icon instead sits beside the chrome, inside one outer bordered container — the same compositionLayrzNumberInputuses for its increment/decrement step controls: aRowof[chrome, icon], with the innerLayrzInputChromegivenshowBorder: falseandborderRadius: BorderRadius.zeroso its own box never paints a competing border, and the outerContainerdrawing the single unified border and radius.LayrzInputChromeneeded no change to support this —showBorderandborderRadiusalready existed on it for exactly this purpose. - Present on both bands. The icon renders identically whether the field opens the desktop anchored panel or the mobile bottom sheet — it is part of the shared anchor composition both bands render, not duplicated per band.
-
State-matched color, no separate tracking. The icon's color is read from the same
LayrzInputStyleSpecthe chrome itself resolves for its text color —fg1at rest,fg4whendisabled, and the danger color whenerrorsis non-empty — so it always agrees with the field's current state without the icon tracking hover/focus/disabled on its own. A left divider (the same error-aware color logic asNumberFieldControl's cap divider) separates it visually from the chrome. -
Decorative — excluded from semantics. The icon carries no accessible name and is wrapped
in
ExcludeSemantics; the field's ownSemanticsnode (label,button: true, enabled state) already describes the control, so the icon is never announced as a second, unlabeled element.
The panel matches the anchor's width (BREAKING). LayrzDurationInput's desktop panel uses
LayrzAnchoredPanelWidthPolicy.matchAnchor, the same "elevated field" illusion (DESIGN-145)
LayrzSelectInput uses — the panel covers the field itself, at the field's own width, rather than
sizing to its content within a fixed 280–480 logical-pixel band as an earlier version did. The
unit fields inside the panel no longer depend on that band either: LayrzDurationPickerPanel reads
its own measured width via a LayoutBuilder and packs as many fields as fit per row without any
field dropping below a minimum usable width, wrapping the rest to additional rows instead of
shrinking further.
Editing a field no longer closes the panel — only Reset does. Tapping a unit field's +/−
control, or typing a value directly into one, keeps the panel open, so a duration can be composed
across multiple fields (day, then hour, then minute) in one session. LayrzDurationPickerPanel
exposes onChanged (fired on every field edit) and a separate onReset (fired once, after Reset
zeroes every field) — only onReset closes the panel. Previously both were the same callback,
which the field treated as "close", so the panel closed on every keystroke or step-button tap.
Unit fields are integers, not decimals. Day/hour/minute/second values render as whole numbers
(e.g. 2, not 2.0) and reject a typed decimal separator at the formatter level.
The field now styles itself as errored. With one or more errors, the field renders the
danger border and fill, matching every other input in the package. It previously could not: the
field passed readOnly: true into its own style resolution, and the resolver's precedence ranks
readOnly above error, so the danger styling was silently suppressed regardless of how many
errors were supplied. LayrzDurationInput exposes no readOnly parameter of its own — the flag
described an internal fact about the field (it never accepts typed input, see the read-only anchor
note above), not a caller-supplied state, and fixing this did not add one.
| Property | Value |
|---|---|
| Mirror | ThemedDurationInput |
| Phase | M3 Inputs |
| Domain | Inputs |
| SDK Primitive | Composes LayrzTextInput (read-only) + RawDialog with LayrzNumberInput controls |
LayrzDurationInput conforms to the Input Contract. Like all picker-style inputs, it composes LayrzTextInput internally in read-only mode and opens a selection surface on tap. The entire contract applies: labels, prefix/suffix, help affordance, focus management, padding, and validation error display.
-
Value type:
Duration— represents a time span (days, hours, minutes, seconds) - Selection surface: Dialog with numeric input fields for each time unit
- Summary display: The field displays the duration in human-readable format (e.g., "2 days 3 hours 15 minutes"); unselected state shows placeholder or empty
- Formatting: Duration is formatted using human-readable language (e.g., "2 days, 3 hours, and 15 minutes")
// Design sketch — illustrative only
class LayrzDurationInput extends StatefulWidget {
/// The currently selected duration.
final Duration? value;
/// Callback invoked when user changes the duration.
final void Function(Duration?)? onChanged;
/// Which time units are visible in the picker.
/// Supported: day, hour, minute, second.
/// Defaults to all four.
final List<LDurationUnit> visibleUnits;
const LayrzDurationInput({
required this.value,
this.onChanged,
this.visibleUnits = const [
LDurationUnit.day,
LDurationUnit.hour,
LDurationUnit.minute,
LDurationUnit.second,
],
// ... shared contract parameters (label, placeholder, prefix, suffix, etc.)
});
}
enum LDurationUnit {
/// Day unit (24-hour increment)
day,
/// Hour unit (0–23 range; wraps when combined with days)
hour,
/// Minute unit (0–59 range; wraps when combined with hours)
minute,
/// Second unit (0–59 range; wraps when combined with minutes)
second,
}Inside the dialog:
- Each visible unit is rendered as a numeric input field with a label and suffix (e.g., "Days: [2]")
- Numeric inputs display step buttons (+/−) or spin controls
- Minimum value for all units is 0; no maximum constraints
The field displays the duration as human-readable text, e.g.:
-
"2 days, 3 hours, and 15 minutes"(with localized "and") -
"1 hour 30 minutes"(short format if only two units) -
"45 seconds"(single unit)
The format depends on which units are visible; hidden units are omitted from the summary.
All of the following are inherited from LayrzTextInput:
- labelText and label (mutually exclusive)
- placeholder
- prefixIcon, prefixWidget, onPrefixTap (mutually exclusive icon and widget)
- suffixIcon, suffixWidget, onSuffixTap (mutually exclusive icon and widget)
- helpTitleText, helpContentText (two-part help tooltip)
- readOnly (always true for duration input)
- onTap (opens the duration picker dialog)
- focusNode, controller (focus and value management)
-
dense (bool, default false) —
pd2/10px by default,pd1/6px when true, identically on every viewport (isCompactno longer varies this). Nopaddingoverride (D66) - disabled and error display
ThemedDurationInput (source: lib/src/inputs/src/general/duration_input.dart):
| Parameter | Type | Notes |
|---|---|---|
value |
Duration? |
Currently selected duration |
onChanged |
Function(Duration?)? |
Callback when duration changes |
errors |
List<String> |
Error messages (default: []) |
labelText |
String? |
Label text (or use label Widget instead) |
label |
Widget? |
Label widget (mutually exclusive with labelText) |
suffixIcon |
IconData? |
Icon in suffix slot |
prefixIcon |
IconData? |
Icon in prefix slot |
disabled |
bool |
Disable the field (default: false) |
padding |
EdgeInsets? |
Field padding |
visibleValues |
List<ThemedUnits> |
Which units to display (default: day, hour, minute, second) |
ThemedUnits enum:
-
year(not supported in duration picker; mentioned for reference) -
month(not supported) -
week(not supported) dayhourminutesecond-
millisecond(not typically used)
Supported units for ThemedDurationInput:
const kThemedDurationSupported = [
ThemedUnits.day,
ThemedUnits.hour,
ThemedUnits.minute,
ThemedUnits.second,
];- LayrzTextInput — must ship first; LayrzDurationInput composes it.
- LayrzNumberInput — required for numeric controls inside the picker dialog.
-
LayrzTooltip — required for help affordances if using
helpTitleText/helpContentText. - Material-free TextSelectionControls — if LayrzTextInput requires copy/paste in read-only mode, this blocker applies.
- i18n Support — humanized duration formatting requires localization strings for unit names (days, hours, minutes, seconds).
- Step controls: The numeric input controls inside the dialog include +/− step buttons for easy adjustment.
- Reset button: The dialog includes a "Reset" button to set all units to 0.
- Field value update: When the dialog closes, the field's display text is updated with the newly formatted duration.
- Humanized formatting: The duration is formatted using a humanization library; the format respects the visible units and the current locale.
-
Additional time units: Should year, month, and week be added as optional visible units? Currently, only day/hour/minute/second are supported.
-
Unit constraints: Should there be maximum values for each unit (e.g., hours capped at 23, minutes at 59)? Or should overflow be allowed and calculated into days?
-
Formatting options: Should there be a
valueFormattercallback to customize the human-readable summary display? Or is localized humanization the only option? -
Keyboard entry: Can users type values directly into the numeric input fields, or only use step buttons?
-
Negative durations: Should negative durations be supported, or are all values constrained to ≥ 0?
-
Dialog height/constraints: Is the dialog height fixed, or does it adapt based on the number of visible units?
-
Millisecond precision: Should millisecond duration component be supported if needed (e.g., for timeout values)?
Last updated: 2026-08-26 (v0.0.13 — documented the panel's matchAnchor width, the
onChanged/onReset split that keeps the panel open across field edits and closes it only on
Reset, integer-only unit fields, and the field's error styling fix; duration-external-affordance's
external clock affordance icon note from 2026-08-25 remains; the remaining pre-implementation
sections are unchanged and still need a full documentation pass)
Related documents: Input Contract, Component Catalog, Design Tokens
Made with ❤️ by Golden M, Inc.
- LayrzTextInput
- LayrzSelectableAction
- LayrzSelectionToolbar
- LayrzTextSelectionControls
- LayrzSelectionMagnifier
- LayrzSelectionHandlePainter
- LayrzTextAreaInput
- LayrzComboBoxInput
- LayrzNumberInput
- LayrzPasswordInput
- LayrzCheckboxInput
- LayrzRadioInput
- LayrzSelectInput
- LayrzMultiSelectInput
- LayrzSearchInput
- LayrzDualListInput
- LayrzDurationInput
- LayrzSlider
- LayrzStepper