promptSelect<T> static method
T
promptSelect<T>({})
Prompts the user to select one option using arrow keys.
Implementation
static T promptSelect<T>({
required String message,
required List<T> options,
required String Function(T) labelProvider,
int defaultIndex = 0,
}) {
// If not running in a terminal (e.g. piped input, CI, tests), fallback to text prompt
if (!stdin.hasTerminal) {
print('\n${Logger.green}?${Logger.reset} ${Logger.bold}$message${Logger.reset}');
for (int i = 0; i < options.length; i++) {
final isDefault = i == defaultIndex;
final label = labelProvider(options[i]);
final indexStr = '${Logger.gray}[${i + 1}]${Logger.reset}';
if (isDefault) {
print(' $indexStr ${Logger.cyan}$label (default)${Logger.reset}');
} else {
print(' $indexStr $label');
}
}
while (true) {
stdout.write(' ${Logger.bold}Enter option (1-${options.length}) [${defaultIndex + 1}]:${Logger.reset} ');
final input = stdin.readLineSync(encoding: utf8)?.trim() ?? '';
if (input.isEmpty) {
return options[defaultIndex];
}
final choice = int.tryParse(input);
if (choice != null && choice >= 1 && choice <= options.length) {
return options[choice - 1];
}
Logger.warning('Invalid option. Please enter a number between 1 and ${options.length}.');
}
}
// Hide cursor
stdout.write('\x1B[?25l');
print('${Logger.green}?${Logger.reset} ${Logger.bold}$message${Logger.reset} ${Logger.gray}(Use arrow keys, press Enter to select)${Logger.reset}');
int selectedIndex = defaultIndex;
void drawOptions() {
for (int i = 0; i < options.length; i++) {
final isSelected = i == selectedIndex;
final marker = isSelected ? '${Logger.cyan}❯${Logger.reset}' : ' ';
final label = labelProvider(options[i]);
if (isSelected) {
print(' $marker ${Logger.cyan}$label${Logger.reset}');
} else {
print(' $label');
}
}
}
drawOptions();
while (true) {
final key = _readKey();
bool changed = false;
if (key == 1) { // UP
if (selectedIndex > 0) {
selectedIndex--;
} else {
selectedIndex = options.length - 1;
}
changed = true;
} else if (key == 2) { // DOWN
if (selectedIndex < options.length - 1) {
selectedIndex++;
} else {
selectedIndex = 0;
}
changed = true;
} else if (key == 10) { // ENTER
// Move cursor up to overwrite previous prompt block
stdout.write('\x1B[${options.length + 1}A');
// Clear from cursor down
stdout.write('\x1B[J');
// Print clean confirmation line
print('${Logger.green}✔${Logger.reset} ${Logger.bold}$message${Logger.reset} ${Logger.cyan}${labelProvider(options[selectedIndex])}${Logger.reset}');
// Restore cursor
stdout.write('\x1B[?25h');
return options[selectedIndex];
}
if (changed) {
// Move cursor up to redraw options
stdout.write('\x1B[${options.length}A');
stdout.write('\x1B[J');
drawOptions();
}
}
}