multiSelect<T> method
Lets the user pick multiple values from options. Space toggles each
row; Enter submits.
Implementation
Future<List<T>> multiSelect<T>(
String message, {
required List<T> options,
List<T> defaults = const [],
String Function(T item)? display,
int? minSelections,
int? maxSelections,
int visibleCount = 5,
bool filterable = false,
}) {
if (options.isEmpty) {
throw ArgumentError.value(options, 'options', 'must not be empty');
}
final state = tui_select.SelectState<T>();
bool submitted = false;
List<T>? pending;
return runOneShot<List<T>>(
(ctx, submit) {
if (submitted) {
final labels = (pending ?? const [])
.map((e) => display?.call(e) ?? e.toString())
.join(', ');
_drawCompactAnswer(ctx, message, labels.isEmpty ? '(none)' : labels);
submit(pending ?? const []);
return;
}
final widget = tui_select.Select<T>(
id: Key.symbol(#__inline_multi),
items: options,
state: state,
defaultValues: defaults,
mode: tui_select.SelectionMode.multi,
minSelections: minSelections,
maxSelections: maxSelections,
visibleCount: visibleCount,
filterable: filterable,
builder: (item, itemState) {
final label = display?.call(item) ?? item.toString();
final mark = itemState.isSelected ? '☒' : '☐';
return Text(
' $mark $label',
style: itemState.isActive ? const Style(bold: true) : null,
);
},
onSubmit: (list) {
pending = list;
submitted = true;
},
);
_drawWithHeader(ctx, message, widget,
bodyHeight:
_selectBodyHeight(options.length, visibleCount, filterable));
},
theme: _theme,
terminal: _terminal(),
allowNonInteractive: _allowNonInteractive,
);
}