view method

  1. @override
Object view()
override

Renders the current model state for display.

This method is called after every update to refresh the screen. It should return either a String or a View object.

Guidelines

  • Keep view functions pure - no side effects
  • View should only depend on model state
  • Use string interpolation or StringBuffer for complex views
  • Consider terminal width/height for responsive layouts

Example

@override
String view() {
  final buffer = StringBuffer();

  // Header
  buffer.writeln('╔════════════════════════════╗');
  buffer.writeln('║      My Application        ║');
  buffer.writeln('╚════════════════════════════╝');
  buffer.writeln();

  // Content
  if (loading) {
    buffer.writeln('Loading...');
  } else {
    for (final item in items) {
      final prefix = item == selectedItem ? '▸ ' : '  ';
      buffer.writeln('$prefix$item');
    }
  }

  buffer.writeln();

  // Footer
  buffer.writeln('↑/↓: Navigate  Enter: Select  q: Quit');

  return buffer.toString();
}

Implementation

@override
Object view() {
  // Placeholder text
  if (_value.isEmpty && placeholder.isNotEmpty) {
    return _placeholderView();
  }

  final styles = activeStyle();
  String styleText(String s) => styles.text.inline(true).render(s);

  final visibleValue = _value.sublist(_offset, _offsetRight);
  final pos = math.max(0, _pos - _offset);

  // Selection range in visible space
  int? selStart, selEnd;
  if (_selectionStart != null && _selectionEnd != null) {
    final start = math.min(_selectionStart!, _selectionEnd!);
    final end = math.max(_selectionStart!, _selectionEnd!);

    selStart = math.max(0, start - _offset);
    selEnd = math.min(visibleValue.length, end - _offset);

    if (selStart >= visibleValue.length || selEnd <= 0) {
      selStart = null;
      selEnd = null;
    }
  }

  var v = '';
  final selectionStyle = Style()
      .background(const AnsiColor(7))
      .foreground(const AnsiColor(0));

  for (var i = 0; i < visibleValue.length; i++) {
    final char = _echoTransform(visibleValue[i]);
    final isSelected = selStart != null && i >= selStart && i < selEnd!;

    if (i == pos) {
      cursor = cursor.setChar(char);
      var cv = cursor.view();
      if (isSelected) {
        cv = selectionStyle.render(cv);
      }
      v += cv;
    } else {
      final rendered = styleText(char);
      v += isSelected ? selectionStyle.render(rendered) : rendered;
    }
  }

  if (pos >= visibleValue.length) {
    if (_focused && _canAcceptSuggestion()) {
      final suggestion = _matchedSuggestions[_currentSuggestionIndex];
      if (_value.length < suggestion.length) {
        cursor = cursor.setChar(_echoTransform(suggestion[_value.length]));
        v += cursor.view();
        v += _completionView(1);
      } else {
        cursor = cursor.setChar(' ');
        v += cursor.view();
      }
    } else {
      cursor = cursor.setChar(' ');
      v += cursor.view();
    }
  }

  // Padding for fixed width
  final valWidth = stringWidth(visibleValue.join());
  if (width > 0 && valWidth <= width) {
    var padding = math.max(0, width - valWidth);
    if (valWidth + padding <= width && pos < visibleValue.length) {
      padding++;
    }
    v += styleText(' ' * padding);
  }

  final styledPrompt = styles.prompt.render(prompt);
  final content = '$styledPrompt$v';

  if (useVirtualCursor || !_focused) {
    return content;
  }

  return View(content: content, cursor: terminalCursor);
}