wrapText function

String wrapText(
  1. String text, {
  2. int? length,
  3. int? hangingIndent,
})

Wraps a block of text into lines no longer than length.

Tries to split at whitespace, but if that's not good enough to keep it under the limit, then it splits in the middle of a word.

Preserves indentation (leading whitespace) for each line (delimited by '\n') in the input, and indents wrapped lines the same amount.

If hangingIndent is supplied, then that many spaces are added to each line, except for the first line. This is useful for flowing text with a heading prefix (e.g. "Usage: "):

var prefix = "Usage: ";
print(prefix + wrapText(invocation, hangingIndent: prefix.length, length: 40));

yields:

Usage: app main_command <subcommand>
       [arguments]

If length is not specified, then no wrapping occurs, and the original text is returned unchanged.

Implementation

String wrapText(String text, {int? length, int? hangingIndent}) {
  if (length == null) return text;
  hangingIndent ??= 0;
  var splitText = text.split('\n');
  var result = <String>[];
  for (var line in splitText) {
    var trimmedText = line.trimLeft();
    final leadingWhitespace = line.substring(0, line.length - trimmedText.length);
    List<String> notIndented;
    if (hangingIndent != 0) {
      // When we have a hanging indent, we want to wrap the first line at one
      // width, and the rest at another (offset by hangingIndent), so we wrap
      // them twice and recombine.
      var firstLineWrap = wrapTextAsLines(trimmedText, length: length - leadingWhitespace.length);
      notIndented = [firstLineWrap.removeAt(0)];
      trimmedText = trimmedText.substring(notIndented[0].length).trimLeft();
      if (firstLineWrap.isNotEmpty) {
        notIndented.addAll(wrapTextAsLines(trimmedText, length: length - leadingWhitespace.length - hangingIndent));
      }
    } else {
      notIndented = wrapTextAsLines(trimmedText, length: length - leadingWhitespace.length);
    }
    String? hangingIndentString;
    result.addAll(notIndented.map<String>((String line) {
      // Don't return any lines with just whitespace on them.
      if (line.isEmpty) return '';
      var result = '${hangingIndentString ?? ''}$leadingWhitespace$line';
      hangingIndentString ??= ' ' * hangingIndent!;
      return result;
    }));
  }
  return result.join('\n');
}