Skip to main content

Examples

NeoTable(
  columns: [
    NeoTableColumn(id: "name", label: "Name", flex: 2),
    NeoTableColumn(id: "email", label: "Email", flex: 3),
    NeoTableColumn(id: "role", label: "Role", flex: 1),
  ],
  rows: [
    NeoTableRow(
      id: "user1",
      cells: {
        "name": Text("John Doe"),
        "email": Text("john@example.com"),
        "role": Text("Admin"),
      },
    ),
    NeoTableRow(
      id: "user2",
      cells: {
        "name": Text("Jane Smith"),
        "email": Text("jane@example.com"),
        "role": Text("Editor"),
      },
    ),
  ],
),

Properties

Required

columns
List<NeoTableColumn>
required
The column definitions that determine the table’s structure and headers.
rows
List<NeoTableRow>
required
The data rows to display. Each row’s cells map must include a widget for every visible column ID.

Content

onRowTap
ValueChanged<String>
Called when a row is tapped. Receives the row ID.
onRowHover
ValueChanged<({String rowId, bool isHovered})>
Called when a row’s hover state changes. Receives the row ID and hover status.
activeRowId
String
The ID of the currently active row. The active row receives distinct highlight styling.
emptyStateBuilder
Widget
Widget to display when rows is empty. Falls back to an empty table body if not provided.

Layout

fixedRowHeight
double
default:"48"
Fixed height for all rows in logical pixels.
headerHeight
double
default:"40"
Height of the sticky column-header row in logical pixels.
showBorder
bool
default:"false"
Whether to show a border around the entire table.
showRowDividers
bool
default:"false"
Whether to show horizontal divider lines between rows.
showColumnDividers
bool
default:"false"
Whether to show vertical divider lines between columns.
scrollController
ScrollController
Scroll controller for the table body. Required when using NeoTable.scrollToRow. An internal controller is used when not provided.
hiddenColumnIds
Set<String>
Set of column IDs to hide. The columns remain in columns so their cells are still defined in rows — they are simply not rendered.

Sorting

sortState
NeoTableSortState
The current sort state (column ID + direction). null means unsorted.
defaultSortState
NeoTableSortState
The sort state to fall back to when no explicit sortState is set. The default-sort column cannot be fully unsorted — it cycles between ascending and descending.
onSortChanged
ValueChanged<NeoTableSortState?>
Called when the user clicks a sortable column header. Receives the new NeoTableSortState, or null when returning to the unsorted state.

Selection

isSelectable
bool
default:"false"
Whether rows can be selected with checkboxes. When true, onSelectionChanged must be provided.
selectedRowIds
Set<String>
The currently selected row IDs.
onSelectionChanged
ValueChanged<Set<String>>
Called when the selection changes. Required when isSelectable is true.

Reordering

isRowReorderable
bool
default:"false"
Whether rows can be reordered by dragging. When true, onRowReorder must be provided.
onRowReorder
Function(String rowId, int oldIndex, int newIndex)
Called when the user drops a row into a new position. Required when isRowReorderable is true.
isColumnReorderable
bool
default:"false"
Whether columns can be reordered by dragging their headers. When true, onColumnReorder must be provided.
onColumnReorder
Function(String columnId, int oldIndex, int newIndex)
Called when the user drops a column header into a new position. Required when isColumnReorderable is true.

State

isLoading
bool
default:"false"
Displays a loading indicator over the table body and disables interaction.

Static Helpers

NeoTable.scrollToRow()

Animates the table body scroll so the given row is aligned to the top of the viewport (below the fixed header).
NeoTable.scrollToRow(
  controller: scrollController,
  rows: rows,
  rowId: "user5",
  fixedRowHeight: 48,
);
controller
ScrollController
required
The same ScrollController passed to the table’s scrollController prop.
rows
List<NeoTableRow>
required
The current row list — must match the table’s rows prop.
rowId
String
required
The ID of the row to scroll to.
fixedRowHeight
double
required
Must match the table’s fixedRowHeight prop.
showRowDividers
bool
default:"false"
Must match the table’s showRowDividers prop so the scroll offset accounts for divider height.

Column Properties

Required

id
String
required
A unique identifier for the column.
label
String
required
The display text for the column header.

Layout

flex
int
default:"1"
Relative width of the column. Higher values take proportionally more space.
minWidth
double
Minimum width in logical pixels. The column will not shrink below this value regardless of flex.

Sorting

isSortable
bool
default:"false"
Whether clicking this column’s header triggers NeoTable.onSortChanged.

Reordering / Visibility

isReorderable
bool
default:"true"
Whether this column can be dragged when NeoTable.isColumnReorderable is true.
isHideable
bool
default:"true"
Whether this column can be included in NeoTable.hiddenColumnIds.

Row Properties

Required

id
String
required
A unique identifier for the row.
cells
Map<String, Widget>
required
A map from column ID to the cell widget. Every visible column must have a corresponding entry.

Enums

NeoTableSortDirection

Sort direction for a column. The absence of a sort state (i.e. null on the table) represents the unsorted state — there is no none value.
  • ascending: Sort from lowest to highest.
  • descending: Sort from highest to lowest.

Best Practices

  • Cell map keys: Use the exact column id strings as cells map keys. The table asserts in debug mode if any visible column’s ID is missing from a row’s cells.
  • Width constraints: Always provide a bounded width constraint (via Expanded, SizedBox, etc.) so the table’s flex layout has a finite extent to divide.
  • Scroll to row: Pass a scrollController whenever you plan to call NeoTable.scrollToRow.
  • Reordering and sorting: Enabling both isRowReorderable and a live sortState at the same time can produce confusing UX — the reordered position conflicts with the sorted order. Consider disabling sorting while reordering is active.

Integration Notes

  • The table validates that all row IDs are unique and that every visible column has a matching cell in each row, both via debug-mode assertions.
  • Sort cycling for a sortable column: descending → ascending → null (unsorted). When a defaultSortState is set for the column, the cycle is descending → ascending only.
Last modified on May 10, 2026