chooseAny<T extends Object?> method
Prompts user with message
to choose zero or more values
from the provided choices
.
An optional list of defaultValues
can be specified.
The defaultValues
must be one of the provided choices
.
This method requires a terminal to be attached to stdout. See https://api.dart.dev/stable/dart-io/Stdout/hasTerminal.html.
Implementation
List<T> chooseAny<T extends Object?>(
String? message, {
required List<T> choices,
List<T>? defaultValues,
String Function(T choice)? display,
}) {
final resolvedDisplay = display ?? (value) => '$value';
final hasDefaults = defaultValues != null && defaultValues.isNotEmpty;
final selections = hasDefaults
? defaultValues.map((value) => choices.indexOf(value)).toSet()
: <int>{};
var index = 0;
void writeChoices() {
_stdout
// save cursor
..write('\x1b7')
// hide cursor
..write('\x1b[?25l')
..writeln('$message');
for (final choice in choices) {
final choiceIndex = choices.indexOf(choice);
final isCurrent = choiceIndex == index;
final isSelected = selections.contains(choiceIndex);
final checkBox = isSelected ? lightCyan.wrap('◉') : '◯';
if (isCurrent) {
_stdout
..write(green.wrap('❯'))
..write(' $checkBox ${lightCyan.wrap(resolvedDisplay(choice))}');
} else {
_stdout
..write(' ')
..write(' $checkBox ${resolvedDisplay(choice)}');
}
if (choices.last != choice) {
_stdout.write('\n');
}
}
}
_stdin
..echoMode = false
..lineMode = false;
writeChoices();
List<T>? results;
while (results == null) {
final key = _readKey();
final keyIsUpOrKKey =
key.controlChar == ControlCharacter.arrowUp || key.char == 'k';
final keyIsDownOrJKey =
key.controlChar == ControlCharacter.arrowDown || key.char == 'j';
final keyIsSpaceKey = key.char == ' ';
final keyIsEnterOrReturnKey = key.controlChar == ControlCharacter.ctrlJ ||
key.controlChar == ControlCharacter.ctrlM;
if (keyIsUpOrKKey) {
index = (index - 1) % (choices.length);
} else if (keyIsDownOrJKey) {
index = (index + 1) % (choices.length);
} else if (keyIsSpaceKey) {
selections.contains(index)
? selections.remove(index)
: selections.add(index);
} else if (keyIsEnterOrReturnKey) {
_stdin
..lineMode = true
..echoMode = true;
results = selections.map((index) => choices[index]).toList();
_stdout
// restore cursor
..write('\x1b8')
// clear to end of screen
..write('\x1b[J')
// show cursor
..write('\x1b[?25h')
..write('$message ')
..writeln(
styleDim.wrap(
lightCyan.wrap('${results.map(resolvedDisplay).toList()}'),
),
);
break;
}
// restore cursor
_stdout.write('\x1b8');
writeChoices();
}
return results;
}