super_table_field 3.1.1
super_table_field: ^3.1.1 copied to clipboard
Super Table Field — a GeniusLink Flutter design-system package for ERP and accounting data grids. Includes read/edit modes, typed columns, change tracking, filtering, aggregations, selection stats, co [...]
super_table_field #
A keyboard-first, generic Flutter data grid for ERP, accounting, inventory, and other data-heavy applications.
super_table_field provides a single SuperTable<R> widget for readable and editable workflows, backed by a SuperTableController<R>. It includes typed columns, validation, filtering, grouping, totals, pagination, change tracking, export, clipboard operations, undo/redo, expandable rows, runtime column configuration, and English/Arabic localization.
super_table_field exports only its own table APIs and localizations. It does not re-export super_core, super_auto_suggestion_box, or super_form_field. Import companion packages directly when application code references their APIs.
import 'package:super_table_field/super_table_field.dart';
Features #
- Generic rows through
SuperRow<R>with support for map-backed and typed domain models. - Readable and editable modes that can be changed at runtime.
- Thirteen column types, including text, number, currency, enum, combo, date, checkbox, computed, and read-only columns.
- Inline editors based on
super_form_field; combo cells useSuperAutoSuggestionsBox. - Enumeration editors use the
super_form_field 1.12.0source-driven select API internally. - Single-cell, multi-cell, single-row, and multi-row selection modes.
- Search, per-column filters, and advanced cross-column filters.
- Multi-level grouping, group aggregates, group footers, and grand totals.
- Page, infinite-scroll, and load-more pagination modes.
- Column sorting, resizing, pinning, visibility, and runtime reordering.
- Table-wide validation, unique constraints, and jump-to-cell issue panels.
- Optional change tracking for added, modified, and deleted rows.
- CSV, TSV, JSON, clipboard, fill-down, fill-right, undo, and redo operations.
- Conditional row and cell styling.
- Optional
SuperTableStylepresets for calm ERP/accounting table styling. - Expandable detail rows.
- Interaction callbacks for cells, rows, selections, and sorting.
- English and Arabic localization with LTR and RTL support.
- Light and dark themes through
SuperMaterialThemeData.
Requirements #
| Requirement | Minimum version |
|---|---|---
| Dart SDK | 3.8.0 |
| Flutter SDK | 3.32.0 |
| super_core | 3.6.0 |
| super_auto_suggestion_box | 1.3.2 |
| super_form_field | 1.12.0 |
Installation #
Add the package to pubspec.yaml:
dependencies:
super_table_field: ^3.1.1
Then install the dependency:
flutter pub get
For local package development:
dependencies:
super_table_field:
path: ../super_table_field
Companion package imports #
The table package keeps its public barrel focused on table-owned APIs. When your application directly uses a companion API, declare that package directly too. The package currently depends on:
dependencies:
super_core: ">=3.6.0 <4.0.0"
super_auto_suggestion_box: ">=1.3.2 <2.0.0"
super_form_field: ">=1.12.0 <2.0.0"
Typical cases are SuperTextTheme/SuperMaterialThemeData from super_core, SuperAutoSuggestionsItem or custom suggestion sources from super_auto_suggestion_box, and SuperSelectSource/SuperOption from super_form_field.
Application setup #
Register the package localization delegates and use the super_core Material themes:
import 'package:flutter/material.dart';
import 'package:super_core/super_core.dart';
import 'package:super_table_field/super_table_field.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
final typography = SuperTextTheme();
return MaterialApp(
debugShowCheckedModeBanner: false,
localizationsDelegates:
SuperTableLocalization.localizationsDelegates,
supportedLocales: SuperTableLocalization.supportedLocales,
theme: SuperMaterialThemeData.light(
textTheme: typography,
primaryTextTheme: typography,
),
darkTheme: SuperMaterialThemeData.dark(
textTheme: typography,
primaryTextTheme: typography,
),
themeMode: ThemeMode.system,
home: const InventoryTablePage(),
);
}
}
super_core 3.6.0 requires explicit SuperTextTheme values for both
textTheme and primaryTextTheme. SuperThemeData no longer exposes
textTheme; read the branded typography through context.superTextTheme or
SuperMaterialThemeData.of(context).textTheme. The table follows the ambient
SuperTextTheme body/display/mono font families.
SuperTableLocalization.localizationsDelegates includes the package translation delegate together with Flutter's Material, Cupertino, and Widgets delegates. Registering it enables Arabic. Inside table widgets, use context.superTableLocalization; the extension returns the active package localization and falls back to built-in English strings when the delegate is not registered. The package currently supports:
const Locale('en');
const Locale('ar');
Quick start #
Create the controller once, dispose it with the widget lifecycle, and place SuperTable inside a bounded layout such as Expanded, Flexible, SizedBox, or a widget with maxHeight.
import 'package:flutter/material.dart';
import 'package:super_table_field/super_table_field.dart';
class InventoryTablePage extends StatefulWidget {
const InventoryTablePage({super.key});
@override
State<InventoryTablePage> createState() => _InventoryTablePageState();
}
class _InventoryTablePageState extends State<InventoryTablePage> {
late final SuperTableController<Map<String, dynamic>> controller;
@override
void initState() {
super.initState();
controller = SuperTableController<Map<String, dynamic>>(
mode: SuperTableMode.editable,
selectionMode: SuperSelectionMode.multiCells,
addRowEnabled: true,
trackChanges: true,
emptyRowValue: () => <String, dynamic>{},
columns: [
SuperTextColumn(
key: 'sku',
label: 'SKU',
width: 140,
required: true,
unique: true,
mono: true,
),
SuperTextColumn(
key: 'name',
label: 'Product',
width: 220,
required: true,
),
SuperNumberColumn<int>(
key: 'quantity',
label: 'Quantity',
width: 110,
min: 0,
agg: SuperAgg.sum,
),
SuperComboColumn<String>(
key: 'unit',
label: 'Unit',
width: 120,
values: const ['Piece', 'Box', 'Carton'],
allowFreeText: false,
),
SuperCurrencyColumn(
key: 'price',
label: 'Unit price',
width: 130,
symbol: 'ر.ي',
code: 'YER',
min: 0,
agg: SuperAgg.sum,
),
SuperComputedColumn<num>(
key: 'total',
label: 'Total',
width: 140,
align: SuperAlign.end,
agg: SuperAgg.sum,
compute: (row) {
final quantity = row['quantity'] as num? ?? 0;
final price = row['price'] as num? ?? 0;
return quantity * price;
},
format: (value, row) =>
'${(value as num? ?? 0).toStringAsFixed(2)} YER',
),
SuperCheckboxColumn(
key: 'active',
label: 'Active',
width: 90,
),
],
rows: [
SuperRow.map({
'sku': 'PRD-001',
'name': 'Notebook',
'quantity': 12,
'unit': 'Piece',
'price': 850.0,
'active': true,
}),
SuperRow.map({
'sku': 'PRD-002',
'name': 'Printer paper',
'quantity': 4,
'unit': 'Box',
'price': 6200.0,
'active': true,
}),
],
onChange: (rows) {
// Persist or synchronize the changed rows in the host application.
},
onNotify: (kind, message) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
},
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Inventory'),
actions: [
IconButton(
tooltip: 'Toggle table mode',
onPressed: controller.toggleMode,
icon: const Icon(Icons.edit_outlined),
),
],
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Expanded(
child: SuperTable<Map<String, dynamic>>(
controller: controller,
columnFilters: true,
advancedFilter: true,
showTotals: true,
showFooter: true,
groupFooters: true,
),
),
],
),
),
);
}
}
showFooter controls pagination and load-more footer controls. It no longer adds
a persistent table status strip.
Core concepts #
SuperTableController<R> #
The controller owns the table state and data pipeline:
rows
→ search
→ column or advanced filters
→ sorting
→ grouping
→ pagination
→ rendered table
It also manages selection, editing, validation, row operations, clipboard operations, history, change tracking, column configuration, and load-more state.
Create the controller in initState, keep it stable across rebuilds, and call dispose() from the owning State object.
SuperRow<R> #
Each row contains:
value: the host-owned backing model.cells: editable table values keyed by column key.id: stable row identity.fingerPrint: a rebuild token for per-row editor resources.
Use a map-backed row for simple data:
final row = SuperRow.map({
'code': '1001',
'name': 'Cash',
'balance': 25000,
});
final balance = row['balance'];
row['balance'] = 30000;
Use SuperRow.of for a typed domain model:
class Product {
Product({required this.id, required this.name, required this.quantity});
final int id;
String name;
int quantity;
}
final product = Product(id: 1, name: 'Notebook', quantity: 12);
final row = SuperRow<Product>.of(product, {
'name': product.name,
'quantity': product.quantity,
});
Use column read and write callbacks when values must be projected between cells and a typed backing model:
SuperTextColumn(
key: 'name',
label: 'Name',
read: (value) => (value as Product).name,
write: (value, next) => (value as Product).name = next,
);
SuperTable<R> #
SuperTable is the view. It observes the controller and renders headers, filters, rows, editors, totals, pagination, overlays, and status information.
The table must receive bounded vertical space:
Expanded(
child: SuperTable<Map<String, dynamic>>(
controller: controller,
),
);
Or pass an explicit height constraint:
SuperTable<Map<String, dynamic>>(
controller: controller,
maxHeight: 520,
);
Column types #
Use the typed column classes whenever possible. Instantiate SuperColumn<T> directly only for custom behavior that is not covered by the standard types.
| Column | Value type | Main use |
|---|---|---|---
| SuperTextColumn | String | General text and bilingual text through arKey |
| SuperNumberColumn<T> | int, double, or num | Quantities, rates, percentages, and numeric aggregates |
| SuperCurrencyColumn | num | Monetary values with symbol and optional code |
| SuperEnumerationColumn<T> | Any typed value | Strict pick-only selection |
| SuperComboColumn<T> | Any typed value | Suggestions with optional free text |
| SuperProgressColumn<T> | Numeric | Progress bar on a configurable range |
| SuperColorColumn<T> | Hex, integer, or Color | Color values and swatches |
| SuperDateColumn | String | YYYY-MM-DD date values |
| SuperTimeColumn | String | HH:mm time values |
| SuperLinkColumn | String | Clickable links |
| SuperCheckboxColumn | bool | Boolean values |
| SuperComputedColumn<T> | Derived | Read-only value computed from a row |
| SuperReadonlyColumn | String | Locked display value |
Shared column options #
Common options include:
SuperTextColumn(
key: 'accountCode',
label: 'Account code',
width: 160,
align: SuperAlign.start,
pin: SuperPin.start,
editable: true,
sortable: true,
groupable: true,
filterable: true,
required: true,
unique: true,
hidden: false,
mono: true,
);
Important behaviors:
hidden: truekeeps the column available for filtering, grouping, and aggregation, but never renders or exports it.editable: nullinherits the table mode;trueorfalseoverrides it.unique: truevalidates non-empty values across all rows case-insensitively.formatterchanges displayed text only; sorting, filtering, grouping, and editing still use the raw value.readandwritemap table values to typed backing objects.
Enumeration columns with SuperSelectFormField #
SuperEnumerationColumn<T> is strict pick-only editing backed by SuperSelectFormField<T>. The table still owns cell commit, validation, keyboard navigation, and row lifecycle; the select field owns option presentation and selection interaction.
For a local value set, no companion import is needed in application code:
SuperEnumerationColumn<String>(
key: 'status',
label: 'Status',
values: const ['Draft', 'Posted', 'Cancelled'],
searchable: true,
);
For source-driven values or custom option metadata, import super_form_field directly:
import 'package:super_form_field/super_form_field.dart';
import 'package:super_table_field/super_table_field.dart';
SuperEnumerationColumn<String>(
key: 'status',
label: 'Status',
sources: const [
SuperSelectListSource<String>(
items: ['Draft', 'Posted', 'Cancelled'],
),
],
searchable: true,
optionBuilder: (items, index, status) => SuperOption<String>(
value: status,
label: status,
description: 'Option ${index + 1} of ${items.length}',
),
);
When choices depend on other cells, use sourcesController. The table caches row-scoped select resources by row.fingerPrint; call row.randomFingerPrint() after changing the dependency:
SuperEnumerationColumn<String>(
key: 'bin',
label: 'Bin',
sourcesController: (context, controller, row, cell) {
final warehouse = row['warehouse'] as String?;
return [
SuperSelectListSource<String>(
items: binsByWarehouse[warehouse] ?? const [],
),
];
},
optionBuilder: (items, index, bin) => SuperOption(
value: bin,
label: bin,
),
);
Source resolution follows sourcesController → sources → values. Use cellController only when the host needs explicit row-scoped SuperSelectFieldController<T> control.
Editing and validation #
Switch modes at runtime:
controller.setMode(SuperTableMode.editable);
controller.setMode(SuperTableMode.readable);
controller.toggleMode();
Column validation #
Use validator for column-specific rules:
SuperNumberColumn<num>(
key: 'quantity',
label: 'Quantity',
min: 0,
validator: (context, controller, row, cell, value) {
if (value < 0) return 'Quantity cannot be negative';
return null;
},
);
Use onChange as a pre-commit hook. Return true to accept the new value and false to reject it:
SuperNumberColumn<num>(
key: 'debit',
label: 'Debit',
onChange: (context, controller, row, cell, previous, next) {
if (next > 0) row['credit'] = 0;
return next >= 0;
},
);
Table-wide validation #
final issues = controller.validateAll();
if (issues.isEmpty) {
// Submit or post the rows.
}
Use the side-effect-free validity getter when only a boolean is needed:
final canSubmit = controller.isValid;
Show the built-in validation panel:
await showSuperValidationPanel(context, controller);
The panel lists every issue and allows the user to jump to the affected cell.
Per-cell edit locking #
Use cellEditable to lock individual cells based on row state:
final controller = SuperTableController<Map<String, dynamic>>(
columns: columns,
rows: rows,
mode: SuperTableMode.editable,
cellEditable: (column, row) {
final posted = row['posted'] as bool? ?? false;
return !posted;
},
);
Change tracking #
Enable change tracking when the host needs an add/modify/delete delta:
final controller = SuperTableController<Map<String, dynamic>>(
columns: columns,
rows: rows,
trackChanges: true,
);
Read the current delta:
final SuperChangeSet<Map<String, dynamic>> changes = controller.changes;
final addedRows = changes.added;
final modifiedRows = changes.modified;
final deletedRows = changes.deleted;
Manage the baseline:
controller.acceptChanges(); // The current rows become the new baseline.
controller.rejectChanges(); // Restore the captured baseline.
Revert a single value or row:
controller.revertCell(row, 'quantity');
controller.revertRow(row);
Filtering and search #
Search #
controller.setSearch('notebook');
Column filters #
controller.setColumnFilter('active', true);
controller.setColumnFilter('status', 'Open');
controller.clearColumnFilters();
Advanced filter #
controller.setAdvancedFilter([
const AdvancedFilterClause(
columnKey: 'quantity',
op: FilterOp.greaterOrEqual,
value: 10,
),
const AdvancedFilterClause(
columnKey: 'active',
op: FilterOp.equals,
value: true,
),
]);
Column filters and the advanced filter are mutually exclusive. Activating one deactivates the other.
Persist or restore filter state:
final json = controller.filterStateJson();
controller.applyFilterJson(json);
Filter option sources #
Enum, currency, and color columns can receive static, asynchronous, or streaming filter options:
SuperEnumerationColumn<String>(
key: 'status',
label: 'Status',
values: const ['Draft', 'Posted', 'Cancelled'],
filterSource: FilterValueSource.async(() async {
return const [
FilterItem('Draft', 'Draft'),
FilterItem('Posted', 'Posted'),
FilterItem('Cancelled', 'Cancelled'),
];
}),
);
Grouping and aggregates #
Declare an aggregate on a numeric column:
SuperNumberColumn<num>(
key: 'amount',
label: 'Amount',
agg: SuperAgg.sum,
);
Available reducers:
SuperAgg.none
SuperAgg.sum
SuperAgg.avg
SuperAgg.count
SuperAgg.min
SuperAgg.max
SuperAgg.custom
For a custom aggregate:
SuperNumberColumn<num>(
key: 'weightedRate',
label: 'Weighted rate',
agg: SuperAgg.custom,
aggregator: (rows) {
// Return the custom aggregate for the supplied rows.
return rows.length.toDouble();
},
);
Control grouping programmatically:
controller.setGroupKeys(['category', 'status']);
controller.toggleGroup('warehouse');
controller.clearGroups();
Read aggregate values:
final quantityTotal = controller.aggregateColumn('quantity');
final totals = controller.grandTotals();
final totalsByCategory = controller.aggregateBy(
'category',
'amount',
agg: SuperAgg.sum,
);
final groupTree = controller.groupAggregates();
Enable visual group subtotal rows:
SuperTable<Map<String, dynamic>>(
controller: controller,
groupFooters: true,
);
Pagination and loading #
Select a pagination mode when creating the controller:
final controller = SuperTableController<Map<String, dynamic>>(
columns: columns,
rows: rows,
pagination: SuperPagination.pages,
pageSize: 25,
);
Available modes:
SuperPagination.none
SuperPagination.pages
SuperPagination.infinite
SuperPagination.loadMore
Change the mode or page programmatically:
controller.setPagination(SuperPagination.pages);
controller.setPage(2);
For remote loading, provide onLoadMore and update loading state after the request:
final controller = SuperTableController<Map<String, dynamic>>(
columns: columns,
rows: rows,
pagination: SuperPagination.loadMore,
hasMore: true,
onLoadMore: (filterState) async {
controller.setLoadMoreState(loadingMore: true);
try {
final nextRows = await repository.loadMore(filterState);
controller.appendRows(nextRows, hasMore: nextRows.isNotEmpty);
} finally {
controller.setLoadMoreState(loadingMore: false);
}
},
);
The active SuperFilterState is passed to onLoadMore, allowing the remote request to honor search and filter criteria.
Selection and interactions #
Choose a selection mode:
controller.setSelectionMode(SuperSelectionMode.multiRows);
Available modes:
SuperSelectionMode.singleCell
SuperSelectionMode.multiCells
SuperSelectionMode.singleRow
SuperSelectionMode.multiRows
Programmatic selection:
controller.selectCellAt(0, 1);
controller.selectCells(const [CellPos(0, 0), CellPos(0, 1)]);
controller.selectRowAt(2);
controller.selectRowsAt([1, 2, 3]);
controller.selectAll();
controller.clearSelection();
Read spreadsheet-style statistics for numeric selected cells and render them where your application needs them:
final SuperSelectionStats? stats = controller.selectionStats;
SuperTable does not render a persistent row-count / shortcut / selection-statistics
status strip. selectionStats remains available as a controller API for custom UI.
Observe user and programmatic interactions:
SuperTable<Map<String, dynamic>>(
controller: controller,
interactions: SuperInteractions<Map<String, dynamic>>(
onCellTap: (details) {
debugPrint('Cell: ${details.column.key}');
},
onRowActivate: (details) {
openDetails(details.row.value);
},
onSelectionChanged: (selection) {
debugPrint('Selected cells: ${selection.cells.length}');
},
onSortChanged: (sort) {
debugPrint('Sort key: ${sort.columnKey}');
},
),
);
Interaction callbacks are observers. They do not replace the table's built-in selection, editing, sorting, or menu behavior.
Column management #
The column manager is enabled by default in header menus:
SuperTable<Map<String, dynamic>>(
controller: controller,
columnManager: true,
);
Open it directly:
await showSuperColumnManager(context, controller);
Programmatic operations:
controller.setColumnPin('sku', SuperPin.start);
controller.cycleColumnPin('price');
controller.hideColumn('internalId');
controller.showColumn('internalId');
controller.toggleColumnVisible('status');
controller.moveColumn('price', 1);
controller.setManagedOrder(['sku', 'name', 'price', 'quantity']);
Saved views #
A saved view can include column order, widths, visibility, runtime pins, sorting, grouping, collapsed groups, and optionally filters.
final Map<String, dynamic> json = controller.viewStateJson();
// Persist json per user and screen.
controller.applyViewJson(json);
Reset personalization:
controller.resetViewState();
Keep active filters while resetting columns and grouping:
controller.resetViewState(clearFilters: false);
Expandable rows #
Add a detail panel below readable rows:
SuperTable<Map<String, dynamic>>(
controller: controller,
expansion: SuperRowExpansion<Map<String, dynamic>>(
mode: SuperRowExpansionMode.single,
defaultHeight: 140,
builder: (context, controller, row) {
return Padding(
padding: const EdgeInsets.all(16),
child: Text('Details for ${row['name']}'),
);
},
),
);
Use SuperRowExpansionMode.multi to allow multiple open rows. A heightBuilder can return a different expanded height per row.
Conditional styling #
Cell styles #
The first matching condition wins:
SuperNumberColumn<num>(
key: 'balance',
label: 'Balance',
colorSign: true,
styles: {
(context, controller, row, cell) => (cell.value as num? ?? 0) < 0:
const CellStyle(
foreground: Color(0xFFD32F2F),
fontWeight: FontWeight.w700,
),
},
);
Row styles #
Pass row conditions to SuperTable:
SuperTable<Map<String, dynamic>>(
controller: controller,
styles: {
(context, controller, row) => row['cancelled'] == true:
const SuperRowStyle(
foreground: Color(0xFF8A8A8A),
),
},
);
Row styles take priority over cell styles.
Combo columns and suggestions #
super_auto_suggestion_box is not re-exported. Import it directly when application code constructs suggestion sources, controllers, or SuperAutoSuggestionsItem metadata.
A static combo column:
SuperComboColumn<String>(
key: 'unit',
label: 'Unit',
values: const ['Piece', 'Box', 'Carton'],
allowFreeText: false,
clearButton: true,
);
SuperComboColumn follows super_auto_suggestion_box 1.3.2: suggestion data
and row-scoped sources use raw T values. Metadata is derived from optional
suggestionBuilder, then the column's display callback. Custom rows can
read the built SuperAutoSuggestionsItem<T> from itemBuilder.
The 1.3.2 suggestion box exposes selection through onSelectionChanged and
observes query text through SuperAutoSuggestionsController.text. The table
adapts these APIs internally while preserving SuperComboColumn's table-level
selection/free-text behavior. Sources stay bound to
SuperAutoSuggestionsBox, while controller instances own field state.
import 'package:super_auto_suggestion_box/super_auto_suggestion_box.dart';
import 'package:super_table_field/super_table_field.dart';
SuperComboColumn<String>(
key: 'account',
label: 'Account',
values: const ['1010 · Cash', '4000 · Revenue'],
suggestionBuilder: (items, index, account) => SuperAutoSuggestionsItem<String>(
value: account,
titleText: account.split(' · ').last,
descriptionText: account.split(' · ').first,
keywords: [account],
),
);
For suggestions that depend on the current row, provide sourceController or cellController. Update row.fingerPrint when dependent row data changes so the per-cell resources are rebuilt the next time the cell enters edit mode:
row.randomFingerPrint();
Rows and editing operations #
controller.addRow();
controller.insertRowAfterFocus();
controller.insertRowBeforeFocus();
controller.duplicateRow();
controller.deleteRow();
controller.moveRowUp();
controller.moveRowDown();
controller.moveRow(0, 3);
Replace or append data:
controller.updateRows(nextRows);
controller.appendRows(nextRows);
controller.clearTable();
Replace the declared columns:
controller.updateColumns(nextColumns);
Clipboard, export, and history #
Export the current filtered and sorted view:
final csv = controller.toCsv();
final tsv = controller.toTsv();
final jsonRows = controller.toJsonRows();
Copy data:
await controller.copyCsvToClipboard();
await controller.copyJson();
Spreadsheet operations:
controller.fillDown();
controller.fillRight();
controller.cutRange();
await controller.paste();
History operations:
if (controller.canUndo) controller.undo();
if (controller.canRedo) controller.redo();
Custom row menus #
Extend the default row menu instead of replacing its standard actions:
SuperTable<Map<String, dynamic>>(
controller: controller,
rowMenuBuilder: (context, defaults) {
return [
...defaults,
SuperMenuEntry(
label: 'Open details',
separatorBefore: true,
icon: Icons.open_in_new,
onTap: () => openDetails(context.row.value),
),
];
},
);
Table appearance #
Common view options:
SuperTable<Map<String, dynamic>>(
controller: controller,
density: SuperDensity.compact,
numbered: true,
showTotals: true,
showFooter: true,
formulaBar: true,
showCopyJsonButton: true,
showRedoUndoButtons: true,
columnFilters: true,
advancedFilter: true,
columnManager: true,
loading: false,
skeletonRows: 8,
);
Available density modes:
SuperDensity.comfortable
SuperDensity.compact
The visual system derives its colors, typography, spacing, and component behavior from the active SuperMaterialThemeData supplied by super_core.
Table styles #
SuperTableStyle is optional. When style is null, no predefined table style is applied and the table keeps its existing/default appearance.
SuperTable<Map<String, dynamic>>(
controller: controller,
style: null,
);
Apply a predefined style explicitly:
SuperTable<Map<String, dynamic>>(
controller: controller,
style: SuperTableStyle.medium,
groupFooters: true,
showTotals: true,
);
Available presets:
SuperTableStyle.plainMinimal
SuperTableStyle.light
SuperTableStyle.medium
SuperTableStyle.dark
SuperTableStyle.accent
SuperTableStyle.bandedRows
SuperTableStyle.bandedColumns
SuperTableStyle.headerEmphasis
SuperTableStyle.gridBordered
SuperTableStyle.subtleBorders
Use SuperTableStyle.presets when building style pickers or comparison screens.
Style options mirror common office-table switches without changing table structure:
final style = SuperTableStyle.accent.copyWith(
options: const SuperTableStyleOptions(
showHeaderRow: true,
showFooterRow: true,
showTotalRow: true,
showGroupRows: true,
bandedRows: true,
bandedColumns: false,
emphasizeFirstColumn: true,
emphasizeLastColumn: true,
),
);
showFooterRow styles the existing group footer/subtotal rows rendered by SuperTable(groupFooters: true). showTotalRow styles the existing totals row rendered by showTotals: true. These options do not create rows, group data, or enable totals by themselves.
Custom styles can start from a preset and override selected areas:
final customLedgerStyle = SuperTableStyle.subtleBorders.copyWith(
name: 'Ledger review',
headerStyle: const SuperTableAreaStyle(
background: Color(0xFFEFF4F8),
foreground: Color(0xFF1F2937),
fontWeight: FontWeight.w800,
),
totalRowStyle: const SuperTableAreaStyle(
background: Color(0xFFE7EEF6),
fontWeight: FontWeight.w800,
),
borderStyle: const SuperTableBorderStyle(
dividerColor: Color(0xFFD7DEE8),
strongDividerColor: Color(0xFFB9C4D2),
),
);
Interaction styles for selected, hovered, focused, and disabled cells are resolved by the table renderer. Conditional SuperRowStyle and CellStyle overrides still take priority over preset body styling.
Localization and RTL #
Set the application locale normally:
final typography = SuperTextTheme(isArabic: true);
MaterialApp(
locale: const Locale('ar'),
localizationsDelegates:
SuperTableLocalization.localizationsDelegates,
supportedLocales: SuperTableLocalization.supportedLocales,
theme: SuperMaterialThemeData.light(
textTheme: typography,
primaryTextTheme: typography,
),
darkTheme: SuperMaterialThemeData.dark(
textTheme: typography,
primaryTextTheme: typography,
),
home: const InventoryTablePage(),
);
Flutter automatically applies RTL layout for Arabic. Table menus, filters, validation messages, pagination labels, column management, shortcut help, and status text use the package localization delegate. Without that delegate, package-owned strings fall back to deterministic English.
Read package translations directly from a widget context when needed:
final translations = context.superTableLocalization;
Keyboard behavior #
The table is designed for desktop and keyboard-heavy workflows. It supports cursor navigation, range selection, editing, clipboard operations, undo/redo, row operations, fill actions, and a shortcuts dialog.
Press F1 while the table is focused to open the built-in shortcut reference.
Use the controller's onKey callback to intercept application-specific shortcuts before the table applies its default behavior:
final controller = SuperTableController<Map<String, dynamic>>(
columns: columns,
rows: rows,
onKey: (context, controller, node, event) {
// Return true when the application handled the event.
return false;
},
);
Public API overview #
Main types #
| API | Purpose |
|---|---|---
| SuperTable<R> | Table view |
| SuperTableController<R> | State, data pipeline, editing, and commands |
| SuperRow<R> | Backing value and editable cells |
| SuperCell | Individual cell value and error state |
| SuperColumn<T> | Flexible base column |
| SuperInteractions<R> | Host interaction callbacks |
| SuperRowExpansion<R> | Expandable row configuration |
| SuperTableStyle | Optional table-wide style preset or custom style |
| SuperTableStyleOptions | Office-style switches for header, totals, banding, and column emphasis |
| SuperTableAreaStyle | Background, foreground, and weight for one table area |
| SuperTableBorderStyle | Outer border and divider styling |
| SuperFilterState | Search and filter state |
| SuperViewState | Persistable user view configuration |
| SuperChangeSet<R> | Added, modified, and deleted row delta |
| SuperValidationIssue<R> | Table-wide validation result |
| SuperSelectionStats | Numeric selection statistics |
| SuperGroupAggregate<R> | Programmatic grouping result |
Enums #
| Enum | Values |
|---|---|---
| SuperTableMode | readable, editable |
| SuperSelectionMode | singleCell, multiCells, singleRow, multiRows |
| SuperPagination | none, pages, infinite, loadMore |
| SuperDensity | comfortable, compact |
| SuperAlign | start, center, end |
| SuperPin | none, left, right |
| SuperAgg | none, sum, avg, count, min, max, custom |
| FilterOp | Text, equality, comparison, range, and empty operators |
| SuperColorValue | hex, number, color |
| SuperRowExpansionMode | single, multi |
| SuperTableStylePreset | plainMinimal, light, medium, dark, accent, bandedRows, bandedColumns, headerEmphasis, gridBordered, subtleBorders |
Built-in overlays #
| Function | Purpose |
|---|---|---
| showSuperMenu | Display a table-style menu |
| showSuperConfirm | Display a confirmation dialog |
| showSuperAdvancedFilter | Display the advanced filter editor |
| showSuperShortcuts | Display the keyboard shortcut reference |
| showSuperValidationPanel | Display validation issues with jump-to-cell |
| showSuperColumnManager | Reorder, show/hide, and pin columns |
Best practices #
- Create and dispose
SuperTableControllerwith the widget lifecycle. - Keep the controller instance stable; do not recreate it from
build. - Give
SuperTablebounded height. - Use typed column classes instead of the generic base class when possible.
- Keep domain persistence and remote synchronization outside the widget; use controller callbacks and change sets to connect them.
- Use
SuperRow.ofwithreadandwritecallbacks for typed domain models. - Validate all rows before posting or saving.
- Persist
viewStateJson()per user and screen when column personalization is enabled. - Use
onLoadMorewith the suppliedSuperFilterStatefor remote pagination. - Dispose external streams, repositories, and suggestion sources in their owning application layer.
Additional documentation #
License #
This package is available under the MIT License.
Column width fitting (2.8.0) #
Every column accepts widthFit:
SuperTextColumn(
key: 'sku',
label: 'SKU',
width: 130,
widthFit: SuperColumnWidthFit.none,
);
SuperTextColumn(
key: 'description',
label: 'Description',
widthFit: SuperColumnWidthFit.maxCell,
);
SuperNumberColumn<int>(
key: 'qty',
label: 'Qty',
widthFit: SuperColumnWidthFit.auto,
);
SuperTextColumn(
key: 'notes',
label: 'Notes',
width: 180,
widthFit: SuperColumnWidthFit.fit,
);
SuperColumnWidthFit.none keeps the declared (or typed default) width.
auto columns share the viewport width left after fixed/intrinsic/base widths
are reserved. maxCell measures the widest rendered row value, includes the
normal cell padding, and adds 30 px of measurement allowance to
avoid edge clipping/ellipsis. fit keeps its base width and shares any genuinely empty
viewport space with other fit columns.
A runtime controller.setWidth(key, px) override always wins and behaves as a
fixed width. Call controller.resetWidth(key) to restore the column's declared
widthFit behavior.
When auto and fit are mixed, auto resolves its equal viewport share first;
fit receives only surplus that is still empty afterward. If the resolved
columns are wider than the viewport, the existing horizontal scrolling behavior
is preserved.