-
Notifications
You must be signed in to change notification settings - Fork 0
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.
| Property | Value |
|---|---|
| Mirror | ThemedDualListInput<T> |
| Phase | M3 Inputs |
| Domain | Inputs |
| SDK Primitive | Hand-rolled dual ListView + transfer buttons |
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:
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 asLayrzSelectInput, see LayrzSelectInput)
Architectural question: LayrzDualListInput does not follow the input contract pattern of composing a read-only
LayrzTextInputfield. 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?
// 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.)
});
}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))
}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
}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) |
- LayrzTextInput — only if redesigned to compose it (see open questions)
- LayrzSelectItem — required for item representation
-
Mobile layout: On narrow screens (
width < tokens.breakpoints.xs), the height is multiplied bymobileScaleFactorto 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.
-
Architectural fit: Should LayrzDualListInput truly compose an
LayrzTextInputfield (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? -
Error display and validation: If it composes LayrzTextInput, where are error messages shown? In the summary field, or in the modal?
-
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?
-
Transfer interaction: Should items be transferable via drag-and-drop, or only via click-to-select and button clicks?
-
Keyboard navigation: Are Tab, arrow keys, and Enter supported for keyboard-only transfer control?
-
Reordering: Once items are selected (on the right panel), can the user reorder them, or is order fixed by the original items list?
-
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?
-
Mobile rendering: Should the layout change on mobile (stacked panels instead of side-by-side)? What does
mobileScaleFactordo exactly — only increase height, or also change layout?
Last updated: 2026-08-13
Related documents: Input Contract, LayrzSelectInput, 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