readMultiChoice static method

List<String> readMultiChoice(
  1. String message,
  2. List<String> options, {
  3. List<String> selected = const [],
  4. CappColors color = CappColors.none,
  5. bool required = false,
})

Implementation

static List<String> readMultiChoice(
  String message,
  List<String> options, {
  List<String> selected = const [],
  CappColors color = CappColors.none,
  bool required = false,
}) {
  int selectedIndex = 0;
  List<bool> checked = List.filled(options.length, false);
  for (var i = 0; i < options.length; i++) {
    var option = options[i];
    checked[i] = selected.contains(option);
  }

  void render([int input = -1]) {
    // Clear the console
    clear();
    write(message, CappColors.info, true);

    for (var i = 0; i < options.length; i++) {
      final checkbox = checked[i] ? '[x]' : '[ ]';
      final prefix = (i == selectedIndex) ? '> ' : '  ';
      if ((i == selectedIndex)) {
        write('$prefix$checkbox ${options[i]}', color);
      } else {
        write('$prefix$checkbox ${options[i]}', CappColors.none);
      }
    }
    write(
      '\n\nUse Arrow Keys to navigate, Space to toggle, Enter to confirm.',
      color,
    );
  }

  render();

  // stdin.lineMode = false;
  // stdin.echoMode = false;

  try {
    while (true) {
      var input = console.readKey();
      //if (input.controlChar == ControlCharacter.escape) {
      // Arrow key sequence starts with ESC (27)

      if (input.controlChar == ControlCharacter.arrowUp) {
        // Arrow Up
        selectedIndex = (selectedIndex - 1) % options.length;
        if (selectedIndex < 0) selectedIndex += options.length;
      } else if (input.controlChar == ControlCharacter.arrowDown) {
        // Arrow Down
        selectedIndex = (selectedIndex + 1) % options.length;
      } else if (input.char == " ") {
        // Space key
        checked[selectedIndex] = !checked[selectedIndex];
      } else if (input.controlChar == ControlCharacter.escape ||
          input.controlChar == ControlCharacter.enter) {
        // Enter key
        break;
      }
      render(int.tryParse(input.char) ?? -1);
    }
  } finally {
    // stdin.lineMode = true;
    // stdin.echoMode = true;
  }

  var res = [
    for (var i = 0; i < options.length; i++)
      if (checked[i]) options[i]
  ];
  if (required && res.isEmpty) {
    return readMultiChoice(
      message + "\n\n * At least one option is required!",
      options,
      selected: [],
      color: color,
      required: required,
    );
  }

  return res;
}