render method

  1. @override
void render(
  1. Buffer buffer,
  2. Rect area
)
override

Renders the underlying widget to the provided buffer within the area.

Implementation

@override
void render(Buffer buffer, Rect area) {
  final table = widget as Table;
  if (table.headers.isEmpty ||
      table.columnWidths.isEmpty ||
      area.height <= 2 ||
      area.width <= 0) {
    return;
  }

  // 1. Render Headers
  final headerSb = StringBuffer();
  for (var i = 0; i < table.headers.length; i++) {
    final width = i < table.columnWidths.length ? table.columnWidths[i] : 10;
    final text = table.headers[i];
    final chars = text.characters;
    final padded = chars.length >= width
        ? chars.take(width).toString()
        : chars.toString() + (' ' * (width - chars.length));
    headerSb.write(padded);
    if (i < table.headers.length - 1) headerSb.write(' ');
  }
  final headerChars = headerSb.toString().characters;
  final headerStr = headerChars.length >= area.width
      ? headerChars.take(area.width).toString()
      : headerChars.toString() + (' ' * (area.width - headerChars.length));
  buffer.writeString(0, 0, headerStr, table.headerStyle);

  // 2. Render Header Divider Line
  final dividerChar = '─';
  final divider = dividerChar * area.width;
  buffer.writeString(0, 1, divider, table.borderStyle);

  // 3. Render Rows with Scroll Adjustment
  final usableHeight = area.height - 2;
  table.adjustScroll(usableHeight);

  // Synchronize cell elements list
  while (cellElements.length < table.rows.length) {
    cellElements.add(
      List.filled(table.headers.length, null, growable: false),
    );
  }
  if (cellElements.length > table.rows.length) {
    cellElements.removeRange(table.rows.length, cellElements.length);
  }

  for (var r = 0; r < usableHeight; r++) {
    final rowIdx = table.scrollOffset + r;
    if (rowIdx >= table.rows.length) break;

    final rowData = table.rows[rowIdx];
    final isSelected = rowIdx == table.selectedRowIndex;
    final currentStyle = isSelected ? table.selectedRowStyle : table.rowStyle;

    int startX = 0;
    for (var c = 0; c < table.headers.length; c++) {
      final colWidth = c < table.columnWidths.length
          ? table.columnWidths[c]
          : 10;
      final cellData = c < rowData.length ? rowData[c] : '';

      final cellRect = Rect(startX, 2 + r, colWidth, 1);
      final vp = Viewport(buffer, cellRect);

      if (cellData is Widget) {
        // Pre-fill cell viewport with space using currentStyle
        vp.fill(Cell(' ', currentStyle));

        // Mount / update child element
        Element? cellEl = cellElements[rowIdx][c];
        if (cellEl != null &&
            cellEl.widget.runtimeType == cellData.runtimeType) {
          cellEl.update(cellData);
        } else {
          cellEl = cellData.createElement();
          cellEl.mount(this);
          cellElements[rowIdx][c] = cellEl;
        }

        cellEl.render(vp, Rect(0, 0, colWidth, 1));
        // We do NOT apply selection style on top of the rendered cell cells
        // to prevent negative image and custom styling override.
      } else {
        // Standard text rendering
        final cellText = cellData.toString();
        final chars = cellText.characters;
        final padded = chars.length >= colWidth
            ? chars.take(colWidth).toString()
            : chars.toString() + (' ' * (colWidth - chars.length));
        vp.writeString(0, 0, padded, currentStyle);
      }

      // Render spacing between columns
      if (c < table.headers.length - 1) {
        final sepX = startX + colWidth;
        if (sepX < area.width) {
          buffer.writeString(sepX, 2 + r, ' ', currentStyle);
        }
      }

      startX += colWidth + 1;
    }

    // Pad remainder of the row to fill area.width
    if (startX < area.width) {
      final remainder = area.width - startX;
      buffer.writeString(startX, 2 + r, ' ' * remainder, currentStyle);
    }
  }
}