multiSelect<T> static method

List<T> multiSelect<T>({
  1. required String title,
  2. required List<T> options,
  3. Set<int>? defaultIndices,
  4. int? fallbackIndex,
  5. FallbackLabelBuilder<T>? labelBuilder,
  6. bool returnDefaultOnEndOfInput = true,
})

Reads one-based multi-selection input.

defaultIndices and fallbackIndex are zero-based. Input may be comma or whitespace separated, for example 1, 3 or 1 3. Empty input returns the default items when any valid defaults are present, otherwise the fallback item when fallbackIndex is in range. End-of-input follows the same policy unless returnDefaultOnEndOfInput is false, in which case it returns an empty list.

Implementation

static List<T> multiSelect<T>({
  required String title,
  required List<T> options,
  Set<int>? defaultIndices,
  int? fallbackIndex,
  FallbackLabelBuilder<T>? labelBuilder,
  bool returnDefaultOnEndOfInput = true,
}) {
  if (options.isEmpty) return <T>[];

  final defaults = _normalizeDefaultIndices(defaultIndices, options.length);
  final fallbackDefault = _normalizeDefaultIndex(
    fallbackIndex,
    options.length,
  );
  final emptyInputSelection = _defaultOrFallbackIndices(
    defaults,
    fallbackDefault,
  );

  while (true) {
    _writeOptions(title, options, labelBuilder);

    final suffix = emptyInputSelection.isEmpty
        ? ''
        : ' [${emptyInputSelection.map((i) => i + 1).join(', ')}]';
    TerminalContext.output.write('Select one or more$suffix: ');

    final line = TerminalContext.input.readLineSync();
    if (line == null) {
      return returnDefaultOnEndOfInput
          ? _itemsAt(options, emptyInputSelection)
          : <T>[];
    }
    if (line.trim().isEmpty) {
      return _itemsAt(options, emptyInputSelection);
    }

    final indices = _parseSelectionIndices(line, options.length);
    if (indices != null) {
      return _itemsAt(options, indices);
    }

    _writeError('Enter numbers from 1 to ${options.length}.');
  }
}