Skip to content

Latest commit

 

History

History
163 lines (134 loc) · 4.42 KB

File metadata and controls

163 lines (134 loc) · 4.42 KB
name themed-table-2
description Use ThemedTable2<T> in a layrz Flutter widget. Apply when displaying a list of domain objects in a table with sort, search, multiselect, and row actions.

Dart syntax: This library requires Dart ≥ 3.10. Use dot shorthand for all enum values — never write the fully-qualified form.

Full constructor and property reference: read references/api.md in this skill's directory.


When to use

  • Displaying lists of domain objects in tabular form (assets, users, reports, etc.)
  • Handles small and large datasets — tested up to 55,000+ rows via virtualized rendering
  • Use ThemedColumn2<T> to define columns alongside ThemedTable2<T>

Minimal usage

ThemedTable2<Asset>(
  items: store.assets,
  actionsCount: 0,
  hasMultiselect: false,
  columns: [
    ThemedColumn2<Asset>(
      headerText: 'Name',
      valueBuilder: (item) => item.name,
    ),
    ThemedColumn2<Asset>(
      headerText: 'Plate',
      valueBuilder: (item) => item.plate ?? 'N/A',
    ),
  ],
)

Always wrap in Expanded or give a fixed height — ThemedTable2 requires bounded height.


Key behaviors

  • Virtualized via ListView.builder + fixed itemExtent — only visible rows are rendered.
  • Sort runs in a background isolate via compute()valueBuilder and customSort must be isolate-safe (no BuildContext, no i18n, no Flutter objects).
  • Search is debounced 600ms and searches all column values.
  • actionsCount must equal the number of buttons actionsBuilder actually returns — assert enforced.
  • hasMultiselect: true requires at least one entry in multiselectActions — assert enforced.
  • onTapDefaultBehavior defaults to .copyToClipboard; set to .none to disable.
  • onFilteredCountChanged fires after every _filterAndSort cycle (initial load, search, sort, items update) with the visible row count. Fires with 0 for an empty dataset. Optional — null by default, no overhead when omitted.

Isolate safety — CRITICAL

valueBuilder and customSort run inside a background isolate. They cannot capture Flutter objects (BuildContext, i18n, State, Streams, widgets). Doing so causes a runtime crash:

Invalid argument(s): Illegal argument in isolate message: object is unsendable

Wrong:

// ❌ CRASH — i18n captures BuildContext
valueBuilder: (item) => i18n.t('status.${item.status}'),

Right:

// ✅ SAFE — precompute a plain Map before the ThemedTable2 call
final Map<String, String> statusLabels = {
  'active': i18n.t('status.active'),
  'inactive': i18n.t('status.inactive'),
};

valueBuilder: (item) => statusLabels[item.status] ?? item.status ?? 'N/A',

Common patterns

// With search + actions
ThemedTable2<Asset>(
  items: _items,
  canSearch: true,
  actionsCount: 2,
  hasMultiselect: false,
  columns: [
    ThemedColumn2<Asset>(
      headerText: i18n.t('asset.name'),
      valueBuilder: (item) => item.name,
    ),
    ThemedColumn2<Asset>(
      headerText: i18n.t('asset.plate'),
      width: 150,
      valueBuilder: (item) => item.plate ?? 'N/A',
    ),
  ],
  actionsBuilder: (item) => [
    ThemedActionButton.edit(
      labelText: i18n.t('actions.edit'),
      onTap: () => _onEdit(item),
    ),
    ThemedActionButton.delete(
      labelText: i18n.t('actions.delete'),
      onTap: () => _onDelete(item),
    ),
  ],
)

// With multiselect
final ValueNotifier<List<Asset>> _selected = ValueNotifier([]);

ThemedTable2<Asset>(
  items: _items,
  hasMultiselect: true,
  actionsCount: 0,
  multiselectValue: _selected,
  multiselectActions: [
    ThemedActionButton(
      icon: LayrzIcons.solarOutlineTrashBin,
      labelText: i18n.t('actions.deleteSelected'),
      color: Colors.red,
      onTap: () => _onDeleteSelected(_selected.value),
    ),
  ],
  columns: [ /* ... */ ],
)

// With filtered count callback
ThemedTable2<Asset>(
  items: _items,
  canSearch: true,
  actionsCount: 0,
  hasMultiselect: false,
  onFilteredCountChanged: (count) {
    setState(() => _visibleCount = count);
  },
  columns: [ /* ... */ ],
)

// With programmatic controller
final _controller = ThemedTable2Controller<Asset>();

// In dispose():
_controller.dispose();

ThemedTable2<Asset>(
  items: _items,
  controller: _controller,
  columns: [ /* ... */ ],
  actionsCount: 0,
  hasMultiselect: false,
)

// Trigger sort programmatically
_controller.sort(columnIndex: 0, ascending: true);
_controller.refresh();