chooseOne<T extends Object?> method
T
chooseOne<T extends Object?>(})
Prompts user with message
to choose one value from the provided
choices
.
An optional defaultValue
can be specified.
The defaultValue
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
T chooseOne<T extends Object?>(
String? message, {
required List<T> choices,
T? defaultValue,
String Function(T choice)? display,
}) {
final resolvedDisplay = display ?? (value) => '$value';
final hasDefault =
defaultValue != null && resolvedDisplay(defaultValue).isNotEmpty;
var index = hasDefault ? choices.indexOf(defaultValue) : 0;
void writeChoices() {
_stdout
// save cursor
..write('\x1b7')
// hide cursor
..write('\x1b[?25l')
..writeln('$message');
for (final choice in choices) {
final isCurrent = choices.indexOf(choice) == index;
final checkBox = isCurrent ? 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();
T? result;
while (result == null) {
final key = _readKey();
final isArrowUpOrKKey =
key.controlChar == ControlCharacter.arrowUp || key.char == 'k';
final isArrowDownOrJKey =
key.controlChar == ControlCharacter.arrowDown || key.char == 'j';
final isReturnOrEnterOrSpaceKey =
key.controlChar == ControlCharacter.ctrlJ ||
key.controlChar == ControlCharacter.ctrlM ||
key.char == ' ';
if (isArrowUpOrKKey) {
index = (index - 1) % (choices.length);
} else if (isArrowDownOrJKey) {
index = (index + 1) % (choices.length);
} else if (isReturnOrEnterOrSpaceKey) {
_stdin
..lineMode = true
..echoMode = true;
_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(resolvedDisplay(choices[index]))),
);
result = choices[index];
break;
}
// restore cursor
_stdout.write('\x1b8');
writeChoices();
}
return result!;
}