-
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,
}Open question: LayrzSearchInput does not currently follow the input contract pattern. It does not use LayrzTextInput internally, nor does it expose the contract's standard API (label, prefix, suffix, help affordances, focus management).
Should layrz_ui redesign it to:
- Always compose a text field (whether overlay or inline)?
- Inherit standard input contract API (help affordance, padding, etc.)?
- Or keep the current minimal surface and treat it as a specialized component?
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 five 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 []. |
isRequired |
bool |
Renders a required marker (*) next to labelText in field mode. No visible effect in icon mode, where the panel field renders no label. Defaults to false. |
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 five are additive and non-breaking — they were not part of the ThemedSearchInput mirror table above.
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.
- LayrzTextInput
- LayrzSelectableAction
- LayrzSelectionToolbar
- LayrzTextSelectionControls
- LayrzSelectionMagnifier
- LayrzSelectionHandlePainter
- LayrzTextAreaInput
- LayrzComboBoxInput
- LayrzNumberInput
- LayrzPasswordInput
- LayrzCheckboxInput
- LayrzRadioInput
- LayrzSelectInput
- LayrzMultiSelectInput
- LayrzSearchInput
- LayrzDualListInput
- LayrzDurationInput