view method

  1. @override
String 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
String view() {
  if (_items.isEmpty) {
    final buffer = StringBuffer();
    if (showTitle) {
      buffer.writeln(styles.title.render(title));
    }
    buffer.writeln(styles.dimmed.render('  No items'));
    return buffer.toString();
  }

  final buffer = StringBuffer();

  // Title
  if (showTitle) {
    buffer.writeln(styles.title.render(title));
  }

  // Hint
  if (showHint && hint.isNotEmpty) {
    buffer.writeln(styles.dimmed.render('  $hint'));
  }

  // Calculate visible range
  final pageSize = _visibleHeight;
  final startIndex = (_cursor ~/ pageSize) * pageSize;
  final endIndex = (startIndex + pageSize).clamp(0, _items.length);

  // Render items
  for (var i = startIndex; i < endIndex; i++) {
    final item = _items[i];
    final isHighlighted = i == _cursor;
    final isSelected = _selected.contains(i);
    final displayText = _displayItem(item);

    final icon = isSelected
        ? styles.selectedIcon.render(styles.selectedIconChar)
        : styles.unselectedIcon.render(styles.unselectedIconChar);

    final prefix = isHighlighted ? styles.cursorPrefix : ' ';
    final itemStyle = isHighlighted ? styles.highlightedItem : styles.item;

    buffer.writeln('  $prefix $icon ${itemStyle.render(displayText)}');
  }

  // Pagination
  if (showPagination && _paginator.totalPages > 1) {
    buffer.writeln(styles.dimmed.render(_paginator.view()));
  }

  // Help
  if (showHelp) {
    final helpItems = keyMap.shortHelp();
    final helpText = helpItems
        .where((b) => b.help.hasContent)
        .map((b) => '${b.help.key} ${b.help.desc}')
        .join('  ');
    buffer.writeln(styles.dimmed.render(helpText));
  }

  return buffer.toString();
}