Skip to main content

Cells & columns

Renderers, display options, validation, per-cell styling, and merges.

Cell renderers

Custom widget per cell (hyperlink, rich text, or your own):

// Hyperlink column
BCellColumn(
title: 'Site', field: 'site', type: BCellColumnType.text(),
renderer: BCellHyperlink.column(
onTap: (url) => launchUrl(Uri.parse(url)), // wire url_launcher yourself
label: (raw) => raw.split('|').first,
url: (raw) => raw.split('|').last,
).toRenderer(),
)
BCellValue(value: 'Flutter|https://flutter.dev')

// Rich text runs in one cell
BCellColumn(
title: 'Note', field: 'note', type: BCellColumnType.text(),
renderer: BCellRichText.column().toRenderer(),
)
BCellValue(value: const BCellRichText([
BCellTextRun('Priority: ', bold: true),
BCellTextRun('high', bold: true, color: Color(0xFFD32F2F)),
]))

For value-based color without a full widget, use cellStyle — see theming.

Column display options

BCellColumn(
title: 'Price', field: 'price', type: BCellColumnType.number(),
formatter: (v) => '\$$v', // display-only; editor edits raw
textAlign: BCellColumnTextAlign.right, // auto / left / center / right
wrapText: true, // soft-wrap in the fixed row height
);
BCellValue(value: 20, formatter: (v) => '<$v>'); // per-cell formatter override
// Wrap cascade: cell -> row -> column (inherit falls through each level).
BCellRow(wrapText: BCellWrapText.wrap, height: 80, cells: {...});
BCellValue(value: long, wrapText: BCellWrapText.noWrap); // this one cell only

// Number formats: multi-section, scientific, [Red] tokens (colour stripped):
BCellColumnType.number(format: '#,##0.00;(#,##0.00);"-"');
BCellColumnType.number(format: '0.00E+00');

// Dropdown editor (Excel data-validation list):
BCellColumn(title: 'Status', field: 's',
type: BCellColumnType.select(const ['Open', 'Closed']));

Data validation

Prebuilt Excel-style validators return a BCellColumn.validator; a rejected edit keeps the old value (also guards paste).

BCellColumn(validator: BCellValidation.number(min: 0, max: 120, integer: true));
BCellColumn(validator: BCellValidation.date(first: DateTime(2024)));
BCellColumn(validator: BCellValidation.time(min: '09:00', max: '17:30'));
BCellColumn(validator: BCellValidation.list(const ['red', 'green', 'blue']));
BCellColumn(validator: BCellValidation.formula('AND(V>=1, V<=10)')); // V = value

Per-cell style & borders, comments, sparklines

// Per-cell visual override (wins over the column cellStyle resolver):
BCellValue(value: 42, style: BCellStyle(
backgroundColor: Colors.yellow.shade100,
textStyle: const TextStyle(fontWeight: FontWeight.bold),
border: Border.all(color: Colors.red, width: 2), // per-side borders
));

// Cell comment: corner marker + hover tooltip.
sm.setNote(cell, 'reviewed'); sm.noteFor(cell);

// Sparkline (in-cell mini chart); cell value is a List<num>.
BCellColumn(title: 'Trend', field: 't', type: BCellColumnType.text(),
renderer: BCellSparkline.line().toRenderer()); // also .bar()
BCellValue(value: const [3, 1, 4, 1, 5, 9, 2, 6]);

// Per-cell lock (Excel unlock-then-protect): locked (default true) only bites
// once the grid is protected; an unlocked cell still edits when protected.
BCellValue(value: 'editable', locked: false);
sm.setGridMode(BCellGridMode.readOnly); // protect the sheet

Conditional-formatting rules

// Highlight by rule (renderers, like the color-scale/data-bar helpers):
BCellColumn(renderer: BCellConditionalFormat.duplicates(sm).toRenderer());
BCellColumn(renderer: BCellConditionalFormat.topN(sm, 3).toRenderer()); // bottom: true
BCellColumn(renderer: BCellConditionalFormat
.formulaRule(sm, '=A1>B1', style: const BCellStyle(backgroundColor: Color(0xFFC6EFCE)))
.toRenderer());

Merge cells

rows.first.cells['name']!.colspan = 2; // merge across columns
rows.first.cells['name']!.rowspan = 3; // merge down rows