super_table_field 3.1.1 copy "super_table_field: ^3.1.1" to clipboard
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 #

pub package Flutter Dart style: flutter_lints license: MIT

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 use SuperAutoSuggestionsBox.
  • Enumeration editors use the super_form_field 1.12.0 source-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 SuperTableStyle presets 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: true keeps the column available for filtering, grouping, and aggregation, but never renders or exports it.
  • editable: null inherits the table mode; true or false overrides it.
  • unique: true validates non-empty values across all rows case-insensitively.
  • formatter changes displayed text only; sorting, filtering, grouping, and editing still use the raw value.
  • read and write map 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 sourcesControllersourcesvalues. 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);
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 SuperTableController with the widget lifecycle.
  • Keep the controller instance stable; do not recreate it from build.
  • Give SuperTable bounded 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.of with read and write callbacks for typed domain models.
  • Validate all rows before posting or saving.
  • Persist viewStateJson() per user and screen when column personalization is enabled.
  • Use onLoadMore with the supplied SuperFilterState for 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.

0
likes
150
points
743
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

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, conditional styling, grouping, totals, pagination, CSV/TSV/JSON export, row reordering, cell locking, interaction events, runtime column config (reorder/pin/show-hide), clipboard, undo/redo, and SuperAutoSuggestionsBox integration. Supports light/dark themes and LTR/RTL via SuperMaterialThemeData.

Homepage
Repository (GitHub)
View/report issues

Topics

#flutter #widget #data-grid #tables #form

License

MIT (license)

Dependencies

flutter, flutter_localizations, intl, super_auto_suggestion_box, super_core, super_form_field

More

Packages that depend on super_table_field