Skip to content

LayrzTextInput

Kenny Mochizuki Escalona edited this page Aug 13, 2026 · 12 revisions

LayrzTextInput

Single-line text entry field, forming the architectural base for the entire input family.


Specification Status

CONFIRMED by the team. This component is in active scope for Phase M3 and is the essential foundation for all other Layrz*Input components.


Metadata

Field Value
Mirrors ThemedTextInput from layrz_theme
Phase M3 (Core Inputs)
Domain Inputs
Composes Layers of: EditableText (SDK), Material-free TextSelectionControls (to be provided), LayrzTooltip for help affordances
Arch Pattern Base of composition: all other inputs are built on top of LayrzTextInput

Conformance

LayrzTextInput conforms to the shared input contract. This document records only what is specific to this component—parameters, behavior, and architectural requirements that go beyond the contract. For the baseline API (labelText, placeholder, prefix/suffix, help affordances, onChanged, onSubmit, onTap, readOnly, focusNode, controller, padding), refer to the contract.


Value Type and Interaction

  • Value type: String (mutable via TextEditingController)
  • Interaction: Direct editing via keyboard input
  • Single-line only: No multiline textarea support in this component. Multiline is deferred to a separate LayrzTextAreaInput component.

Deltas from the Input Contract

LayrzTextInput adds the following over the base contract:

Keyboard and Text Handling

  • keyboardType (TextInputType) — controls the soft keyboard presented (text, email, phone, number, URL, etc.). Default: TextInputType.text.
  • textInputAction (TextInputAction?) — controls the submit/action button on the keyboard (next, done, go, search, etc.).
  • inputFormatters (List<TextInputFormatter>) — applied during input to enforce character or format restrictions.
  • maxLength (int?) — maximum number of characters allowed. When set, character count may be displayed.
  • autofocus (bool) — whether the field requests focus when the widget is first built. Default: false.
  • textCapitalization (TextCapitalization) — automatic capitalization rules for input. Default: TextCapitalization.none.
  • autocorrect (bool) — whether autocorrection is enabled. Default: true.
  • enableSuggestions (bool) — whether text suggestions are shown. Default: true.

Prefix and Suffix Variants

The contract specifies icon vs. widget exclusivity. LayrzTextInput clarifies:

  • prefixText (String?) — static prefix text (e.g., "$" for currency, "+" for phone). Distinct from icon or widget.
  • suffixText (String?) — static suffix text (e.g., unit labels like "kg", "°C").
  • These are independent of icon/widget slots; a field can have a text prefix AND an icon suffix simultaneously.

Validation and Error Display

The contract leaves validator/error handling as an open question. LayrzTextInput's stance:

  • validator (bool Function(String)?) — optional validation function returning true if valid, false otherwise. Invoked when needed (e.g., on form submission).
  • errors (List<String>) — list of error messages to display. Rendered by a companion LayrzFieldError component below the input (or integrated directly; see blockers).
  • hideDetails (bool) — whether to suppress error display. Default: false.

Disabled State

The contract questions whether a disabled state exists distinct from readOnly.

  • disabled (bool) — disables the field: keyboard input is blocked, visual feedback is grayed out or reduced in opacity, onTap is not triggered. Distinct from readOnly, which allows focus/selection but not editing.
  • Default: false.

Other Passthroughs

  • autofillHints (List<String>) — platform autofill hints (e.g., AutofillHints.email). Used by password managers and browser autofill.

Reference: ThemedTextInput API

For porting purposes, the current layrz_theme implementation exposes (non-exhaustive list of key parameters from source):

// Design sketch — shows parameter names found in layrz_theme source
class ThemedTextInput extends StatefulWidget {
  final TextInputType keyboardType;
  final String? labelText;
  final Widget? label;              // NOT supported in LayrzTextInput (labelText only)
  final String? placeholder;
  final String? prefixText;
  final IconData? prefixIcon;
  final Widget? prefixWidget;
  final VoidCallback? onPrefixTap;
  final bool prefixIconDisabled;
  final bool suffixIconDisabled;
  final IconData? suffixIcon;
  final String? suffixText;
  final VoidCallback? onSuffixTap;
  final bool obscureText;           // Handled by LayrzPasswordInput, not exposed here
  final TextEditingController? controller;
  final void Function(String)? onChanged;
  final VoidCallback? onTap;
  final String? value;
  final bool disabled;
  final List<String> errors;
  final bool hideDetails;
  final EdgeInsets? padding;
  final bool dense;                 // NOT ported; layrz_ui uses design tokens only
  final bool isRequired;            // Markup only; not validated
  final FocusNode? focusNode;
  final bool Function(String)? validator;
  final VoidCallback? onSubmitted;
  final TextInputAction? textInputAction;
  final bool readonly;              // Mapped to readOnly in LayrzTextInput
  final List<TextInputFormatter> inputFormatters;
  final List<String> autofillHints;
  final double? borderRadius;       // Controlled by theme, not parameterized
  final int maxLines;               // For LayrzTextInput, always 1; multiline deferred
  final bool autocorrect;
  final bool enableSuggestions;
  final bool autofocus;
  final Widget? suffixWidget;
  final List<String> choices;       // Combobox support — DEFERRED to LayrzSelectInput
  final bool enableCombobox;        // Combobox support — DEFERRED
  // ... combobox-related params omitted
}

Key changes in LayrzTextInput relative to ThemedTextInput:

  • No label Widget: labelText only. Rationale: composable inputs need consistent chrome; label widgets break consistency. If needed, render a custom label outside LayrzTextInput.
  • No multiline: maxLines is always 1. Multiline textarea is a separate component.
  • No combobox: Combobox/autocomplete is deferred to LayrzSelectInput (M3 or later). ThemedTextInput conflates text input and autocomplete; LayrzTextInput is pure text input.
  • No dense parameter: Spacing is controlled entirely by design tokens from LayrzThemeData. No per-field density tuning.
  • No isRequired markup: Required state is visual only (e.g., red asterisk on label). Validation is delegated to validator callback.
  • No obscureText parameter: Obscuring (password hide/show toggle) is the job of LayrzPasswordInput, not LayrzTextInput.

Dependencies and Blockers

Blocker: Material-Free TextSelectionControls

The SDK's EditableText widget requires a concrete TextSelectionControls implementation to handle:

  • Text selection handles (mobile draggable endpoints)
  • Selection toolbar (cut/copy/paste buttons)
  • Desktop toolbar positioning and button layout

Current state: Material provides MaterialTextSelectionControls and Cupertino provides CupertinoTextSelectionControls. Neither can be imported in layrz_ui.

Solution required: Implement a custom, Material-free LayrzTextSelectionControls class.

Available SDK building blocks:

  • RawMagnifier — a magnifying glass widget that shows enlarged selection area during dragging (useful for mobile).
  • SystemContextMenu — native system context menu (cut/copy/paste) for platforms that support it (e.g., Android 11+).

Status: BLOCKING. LayrzTextInput cannot proceed to implementation until this is available.

Dependency: LayrzTooltip (M2)

The help affordance uses the tooltip component to render helpTitleText and helpContentText. LayrzTooltip must be available in M2.

Status: Blocking, but M2 precedes M3 in the roadmap.

Dependency: LayrzThemeData

Text styling (font family, size, weight, color in normal/focused/disabled states) comes from LayrzThemeData. The theme must be in place before styling can be implemented.

Status: M1 work, completed.


Design Reference Gap

CRITICAL: The visual design of LayrzTextInput diverges from ThemedTextInput per internal design review meetings and is not captured in any linked artefact. This gap is a blocker for the entire input family, since every other input composes LayrzTextInput and inherits its chrome.

Required before implementation: A design reference (Figma file, annotated mockup, or design spec) must define:

  • Label positioning, typography, and spacing
  • Placeholder text styling (color, font, opacity rules for light/dark themes)
  • Prefix/suffix icon sizing and spacing
  • Focus state appearance (border, shadow, background change)
  • Hover state appearance (if any, for desktop)
  • Error state appearance (e.g., red border, error icon, error text color)
  • Disabled state appearance (opacity, color desaturation, cursor handling)
  • Help affordance icon (location, size, visibility rules)
  • Padding and overall field dimensions
  • Light and dark theme variants

Without this, implementation will guess at design decisions, leading to rework.


Open Questions

The following decisions are not yet made and must be resolved before implementation.

1. Error Display Integration

  • Is error display integrated into LayrzTextInput itself, or does it delegate to a companion LayrzFieldError component?
  • If delegated, does LayrzFieldError sit below LayrzTextInput, or does the caller wrap both?
  • Is there a color-coded error indicator (e.g., red border when errors exist)?

2. Help Affordance Placement and Behavior

  • Does the help icon (showing helpTitleText and helpContentText) appear always, only when help text is supplied, or only on focus?
  • Does it occupy the suffix slot or sit beside a caller-supplied suffix? If both are present, what is the precedence?
  • Is the help affordance a separate clickable/hoverable element, or is it integrated with the suffix?

3. Additional EditableText Passthroughs

  • Are there other EditableText constructor parameters worth exposing (e.g., minLines, strutStyle, showCursor, cursorColor, cursorWidth, cursorRadius)?
  • Should the widget expose style and textAlign parameters, or are these locked to theme-derived values?

4. Disabled vs. ReadOnly Semantics

  • Is disabled visually distinct from readOnly in all states (focus, hover, text selection)?
  • Does disabled prevent focus entirely, or does it allow focus but block input?
  • Are both states required, or can one subsume the other?

5. Validation Callback Return Value

  • The current contract suggests validator: bool Function(String)?, returning true/false. Is this the right contract, or should it return an optional error message string instead (more flexible)?

Implementation Roadmap

  1. M1 (Foundation): Design reference and LayrzThemeData must be finalized.
  2. M2 (Core primitives): Implement Material-free TextSelectionControls and ship LayrzTooltip.
  3. M3 (Inputs): Implement LayrzTextInput, resolve all open questions, and ship.
  4. M3 continuation: Implement LayrzNumberInput, LayrzPasswordInput, LayrzCheckboxInput, LayrzRadioInput on top of LayrzTextInput.
  5. M4 (Pickers): Implement picker inputs (LayrzDateInput, LayrzColorInput, LayrzEmojiInput, etc.) as read-only LayrzTextInput with selection dialogs.

Last updated: 2026-08-13
Related documents: Input Contract, Design Tokens, Flutter 347 Audit

Clone this wiki locally