Skip to content

LayrzDualListInput

Kenny Mochizuki Escalona edited this page Aug 17, 2026 · 3 revisions

LayrzDualListInput

Two-panel list selection field for moving items between available and selected sets.

Specification Status: Derived from layrz_theme.ThemedDualListInput. 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.

Metadata

Property Value
Mirror ThemedDualListInput<T>
Phase M3 Inputs
Domain Inputs
SDK Primitive Hand-rolled dual ListView + transfer buttons

Conformance Status

Open question: LayrzDualListInput is architecturally distinct from the picker-style inputs. It is a two-panel widget for transferring items, not an input field that opens a dialog. Does it truly belong in the Input Contract family, or should it be a separate data-entry component? See open questions below.

Value Type and Selection Surface

  • Value type: List<T> — a list of selected values
  • Interaction pattern: Two side-by-side panels (Available / Selected) with transfer buttons between them
  • Summary display: No inline summary; the dual-panel interface is the complete UI
  • Item support type: LayrzSelectItem<T> (same as LayrzSelectInput, see LayrzSelectInput)

Deltas from the Input Contract

Architectural question: LayrzDualListInput does not follow the input contract pattern of composing a read-only LayrzTextInput field. Instead, it is a large, prominent two-panel UI that typically appears full-height on a form. Should it:

  • Be redesigned to compose an LayrzTextInput in read-only mode with summary display, and a modal dual-list for detail entry (like pickers)?
  • Or remain a standalone component that doesn't follow the input contract pattern?

Multi-Selection Behavior

// Design sketch — illustrative only
class LayrzDualListInput<T> extends StatefulWidget {
  /// List of all items available to move.
  final List<LayrzSelectItem<T>> items;

  /// Callback invoked when user transfers items between panels.
  final void Function(List<LayrzSelectItem<T>>)? onChanged;

  /// Currently selected values.
  final List<T>? value;

  /// Height of the dual-panel interface.
  final double height;

  /// Label for the left (available) panel.
  final String availableListName;

  /// Label for the right (selected) panel.
  final String selectedListName;

  /// Height of each item in the lists.
  final double itemExtent;

  /// Optional custom comparison function for value matching.
  /// If null, equality operator is used.
  final bool Function(T?, T?)? compareFunction;

  const LayrzDualListInput({
    required this.items,
    this.onChanged,
    this.value,
    this.height = 400,
    this.availableListName = 'Available',
    this.selectedListName = 'Selected',
    this.itemExtent = 50,
    this.compareFunction,
    // ... shared contract parameters? (label, errors, help, etc.)
  });
}

Search and Filtering

Each panel includes a search field for filtering:

// Design sketch — illustrative only within each panel:
class DualListPanel {
  /// Search query for filtering this panel's list.
  final String searchQuery;

  /// Callback when search query changes.
  final void Function(String)? onSearchChanged;

  // Items displayed = items.where((item) => item.label.contains(searchQuery))
}

Transfer Controls

Between the two panels:

// Design sketch — illustrative only
class TransferButtons {
  /// Move all available → selected
  final IconButton moveAllToSelected;

  /// Move all selected → available
  final IconButton moveAllToAvailable;

  /// Individual items are transferred by tapping them in the list
}

Reference: Current layrz_theme API

ThemedDualListInput<T> (source: lib/src/inputs/src/general/duallist_input.dart):

Parameter Type Notes
labelText String? Label text (or use label Widget instead)
label Widget? Label widget (mutually exclusive with labelText)
items List<ThemedSelectItem<T>> All available items
value List<T>? Currently selected values
onChanged void Function(List<ThemedSelectItem<T>>)? Callback when selection changes
disabled bool Disable both panels (default: false)
errors List<String> Error messages (default: [])
height double Height of the dual-panel area (default: 400)
availableListName String Label for left panel (default: "Available")
selectedListName String Label for right panel (default: "Selected")
mobileScaleFactor double Multiply height by this on mobile (default: 2)
itemExtent double Height of each item (default: 50)
compareFunction bool Function(T?, T?)? Custom equality for value matching
translations Map<String, String> i18n strings (cancel, save, search, toggle buttons)
overridesLayrzTranslations bool Use custom translations (default: false)

Dependencies and Blockers

  • LayrzTextInput — only if redesigned to compose it (see open questions)
  • LayrzSelectItem — required for item representation

Implementation Notes from layrz_theme

  • Mobile layout: On narrow screens (width < tokens.breakpoints.xs), the height is multiplied by mobileScaleFactor to provide more space.
  • Search across both panels: Each panel has its own search field, filtering only that panel's items.
  • All-or-nothing buttons: "Move all to selected" and "Move all to available" transfer all currently visible (filtered) items in one action.
  • Icon visibility: Each item can optionally display an icon; if any item has an icon, all items show an icon slot for alignment.

Open Questions

  1. Architectural fit: Should LayrzDualListInput truly compose an LayrzTextInput field (with summary display) plus a modal dialog with the dual-list interface? Or is it a standalone component that doesn't follow the input contract?

  2. Error display and validation: If it composes LayrzTextInput, where are error messages shown? In the summary field, or in the modal?

  3. Dialog vs. always-visible: Should the dual-panel interface appear inline on the form, or only when a modal is opened from a summary field?

  4. Transfer interaction: Should items be transferable via drag-and-drop, or only via click-to-select and button clicks?

  5. Keyboard navigation: Are Tab, arrow keys, and Enter supported for keyboard-only transfer control?

  6. Reordering: Once items are selected (on the right panel), can the user reorder them, or is order fixed by the original items list?

  7. Disabled state: If an item is marked as disabled, is it:

    • Grayed out and non-transferable?
    • Moved to a separate "Disabled" section?
    • Removed from the available panel entirely?
  8. Mobile rendering: Should the layout change on mobile (stacked panels instead of side-by-side)? What does mobileScaleFactor do exactly — only increase height, or also change layout?


Last updated: 2026-08-13
Related documents: Input Contract, LayrzSelectInput, Component Catalog, Design Tokens

Clone this wiki locally