-
Notifications
You must be signed in to change notification settings - Fork 0
LayrzSearchInput
Text search field with optional inline or overlay presentation.
Specification Status: Derived from
layrz_theme.ThemedSearchInput. This documentation reflects the current layrz_theme API and is NOT YET CONFIRMED as the design for layrz_ui. Team review and design sign-off are required before implementation.
| Property | Value |
|---|---|
| Mirror | ThemedSearchInput |
| Phase | M3 Inputs |
| Domain | Inputs |
| SDK Primitive |
EditableText (with optional Overlay for expansion) |
Open question: LayrzSearchInput does NOT obviously compose
LayrzTextInputlike the picker-style inputs. In overlay form, the search surface appears in an overlay above the button; in field form, it is an inline field. Should both forms compose a read-onlyLayrzTextInputas a display surface? See open questions below.
-
Value type:
String— the search query text -
Interaction pattern:
- Overlay form (compact icon button): A magnifying glass icon; on tap, expands into an overlay with a text input field
- Field form (inline): A full-width text field with a search icon prefix
- Summary display: The field displays the current search query (or empty if not searching)
LayrzSearchInput differs from the picker-style inputs in that it offers two distinct presentation modes, not one. The choice is made at construction time:
// Design sketch — illustrative only
class LayrzSearchInput extends StatefulWidget {
/// The current search query text.
final String value;
/// Callback invoked when the search query changes.
/// Debounce can be applied (see [debounce] parameter).
final void Function(String)? onSearch;
/// Label or placeholder text for the search field.
final String labelText;
/// Presentation mode: field (inline) or button (overlay).
final LSearchPosition position;
/// Whether to render as an inline field (true) or compact button (false).
final bool asField;
/// Maximum width of the search field/overlay.
final double maxWidth;
/// Debounce duration for search callbacks.
/// If null, no debounce is applied.
/// Defaults to 300 milliseconds.
final Duration? debounce;
/// Whether the search is disabled.
final bool disabled;
/// Custom widget to replace the entire search UI.
final Widget? customChild;
const LayrzSearchInput({
required this.value,
required this.onSearch,
this.labelText = 'Search',
this.position = .left,
this.asField = false,
this.maxWidth = 300,
this.debounce = const Duration(milliseconds: 300),
this.disabled = false,
this.customChild,
});
}
enum LSearchPosition {
/// Overlay expands to the left of the button.
left,
/// Overlay expands to the right of the button.
right,
}Shipped: LayrzSearchInput composes LayrzInputChrome directly and exposes helpTitleText/helpContentText, controller, focusNode, and a bool dense (default false) that selects the chrome's density ramp, same as every other chrome-owning input. Default padding is pd2/10px; dense: true gives pd1/6px — identically on every viewport (isCompact no longer varies this). There is no public padding override — density is expressed only through dense (D66). This section previously described an open design question; the constructor sketch above predates the shipped API and should not be relied on for the full parameter list.
ThemedSearchInput (source: lib/src/inputs/src/general/search_input.dart):
| Parameter | Type | Notes |
|---|---|---|
value |
String |
Current search query |
onSearch |
OnSearch (void Function(String)) |
Search callback |
maxWidth |
double |
Max width of field/overlay (default: 300) |
labelText |
String |
Field label/placeholder (default: "Search") |
customChild |
Widget? |
Replace entire search UI with custom widget |
disabled |
bool |
Disable the search (default: false) |
position |
ThemedSearchPosition |
Overlay direction: left or right (default: left) |
asField |
bool |
Render as field instead of button (default: false) |
inputPadding |
EdgeInsets |
Padding for field form only (default: EdgeInsets.zero) |
debounce |
Duration? |
Debounce delay for onSearch (default: 300ms) |
ThemedSearchPosition enum:
-
left: Overlay appears to the left -
right: Overlay appears to the right
DESIGN-142 moved LayrzSearchInput off wrapping LayrzTextInput and onto composing
LayrzInputChrome directly (see decision D63).
Going through the chrome directly gave the field four parameters ThemedSearchInput never had —
before this, LayrzSearchInput could not display a validation error at all:
| Parameter | Type | Notes |
|---|---|---|
errors |
List<String> |
Error messages shown below the field. Non-empty renders a danger-colored border, a trailing error icon, and the messages themselves. Defaults to const []. |
helpTitleText |
String? |
Title text for the help affordance tooltip. Ignored unless helpContentText is also set. |
helpContentText |
String? |
Content text for the help affordance tooltip. When set (and non-empty), a help icon appears in the trailing icon cluster. |
readOnly |
bool |
Field is not editable but still fires tap callbacks, and renders a lock affordance in the trailing icon cluster. Does not affect the clear button. Defaults to false. |
All four are additive and non-breaking — they were not part of the ThemedSearchInput mirror table above. isRequired was also added here at one point but has since been removed: LayrzSearchInput never had a labelText parameter, so isRequired (which only ever rendered a required marker next to a label) had nothing to mark and was already a no-op before it was deleted.
LayrzSearchInput gained a sixth addition, preferredSide, when the tooltip's four-value side
vocabulary (LayrzPreferredSide) was extracted into its own module and taught to
LayrzAnchoredPanel, the overlay icon mode opens:
| Parameter | Type | Notes |
|---|---|---|
preferredSide |
LayrzPreferredSide |
The preferred side on which the search panel opens relative to the trigger button. Flips to the opposite side when it does not fit, and is clamped into the overlay if neither fits. Only applies in icon mode (including auto on a compact viewport) — ignored in field mode, where there is no panel. Defaults to LayrzPreferredSide.right. |
This default deliberately differs from LayrzAnchoredPanel's own default of bottom. A global
right default on the panel would put every picker-style dropdown (LayrzSelectInput, etc.) on top
of its own field; right was chosen for LayrzSearchInput specifically, because its trigger is a
compact icon button rather than an inline field. alignment is not forwarded — the panel keeps its
own default there.
Before DESIGN-142, the clear affordance in field mode only appeared when the field was seeded
with an initial value — typing into an initially-empty field never made it show up, because the
widget read the controller's text once but never listened for changes on it. The controller now
carries a listener that triggers a rebuild on the empty/non-empty transition (not on every
keystroke), so the clear icon correctly appears as soon as the user types a character and
disappears again once the field is cleared. The listener is removed in dispose before the
widget's own conditional controller disposal, so a caller-supplied controller is still never
disposed by this widget.
- LayrzTextInput — only if redesigned to always compose it (see open questions)
- Material-free TextSelectionControls — required for copy/paste in the search field
-
Overlay positioning: The overlay is smart-positioned based on available screen space. If
positionisleftbut there isn't enough space on the left, the overlay is repositioned to the right. - Escape key handling: Pressing Escape closes the overlay.
-
Debounce: If
debounceis null,onSearchis invoked immediately on every keystroke. Otherwise, it fires after the debounce duration. -
Form vs. button rendering:
asField: truerenders the search input inline with a specifiedinputPadding.asField: falserenders as a 40×40 icon button.
-
Composition pattern: Should LayrzSearchInput be redesigned to always compose
LayrzTextInput, bringing it into the input family contract? Or is the current minimal surface appropriate for layrz_ui? -
Form support: If redesigned to compose LayrzTextInput, does it inherit the full contract (label, placeholder, prefix/suffix, help affordances)?
-
Overlay vs. field naming: If both forms are kept, should they be:
- Factory constructors (e.g.,
LayrzSearchInput()for overlay,LayrzSearchInput.field()for inline)? - Or a single constructor with a boolean flag?
- Factory constructors (e.g.,
-
Position parameter purpose: In the overlay form,
positionis used to indicate which side of the button the overlay expands toward. In the field form, does this parameter have any meaning? -
Keyboard management: Are Tab, Shift+Tab, Enter, and Escape supported for keyboard navigation and submission?
-
Search trigger: Is there a "search" or "clear" button in the overlay/field, or only auto-trigger on debounce?
-
Accessibility: Does the overlay escape key behavior work with screen readers? Should there be a close button visible?
Last updated: 2026-08-13
Related documents: Input Contract, Component Catalog, Design Tokens
Made with ❤️ by Golden M, Inc.
- LayrzAnchoredPanel
- LayrzBottomSheet
- LayrzDialog
- LayrzDropdownMenu
- LayrzResponsiveModal
- LayrzPageTransition
- LayrzTextInput
- LayrzSelectableAction
- LayrzSelectionToolbar
- LayrzTextSelectionControls
- LayrzSelectionMagnifier
- LayrzSelectionHandlePainter
- LayrzTextAreaInput
- LayrzComboBoxInput
- LayrzNumberInput
- LayrzPasswordInput
- LayrzCheckboxInput
- LayrzRadioInput
- LayrzSelectInput
- LayrzMultiSelectInput
- LayrzSearchInput
- LayrzDualListInput
- LayrzDurationInput
- LayrzSlider
- LayrzStepper