DataTable
A virtualized table for large collections of text rows.
See also: Table for small grids of widget cells · TreeTable for hierarchies.
Learn more: Lists & scrolling
Import: package:fleury/fleury.dart, or package:fleury/fleury_core.dart in browser code.
Choose a row
Use ↑ / ↓ to browse, then Enter to choose. A click chooses the row too. Watch Browsing and Chosen change separately.
import 'package:fleury/fleury_core.dart';
class TableRows extends StatefulWidget { const TableRows({super.key});
@override State<TableRows> createState() => _TableRowsState();}
class _TableRowsState extends State<TableRows> { final table = DataTableController(); String browsing = 'Row 1'; String chosen = 'None';
@override void dispose() { table.dispose(); super.dispose(); }
@override Widget build(BuildContext context) => Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 7, child: DataTable( controller: table, rowCount: 100, autofocus: true, onFocusedItemChanged: (row) => setState(() => browsing = 'Row ${row + 1}'), onSelect: (row) => setState(() => chosen = 'Row ${row + 1}'), columns: const [ DataTableColumn( id: 'name', title: 'Name', width: FixedColumnWidth(12), ), DataTableColumn(id: 'status', title: 'Status'), ], cellBuilder: (row, column) => column == 'name' ? 'Row ${row + 1}' : 'Ready', ), ), Text('Browsing: $browsing'), Text('Chosen: $chosen'), ], );}Select a cell range
Click a cell, then Shift-click another to select a range. Arrow keys move the underlined cursor; Space selects that cell. Shift-arrow extends a range from the cursor. Ctrl+C copies the range. Enter or a double-click opens a row.
import 'package:fleury/fleury_core.dart';
class TableCells extends StatefulWidget { const TableCells({super.key});
@override State<TableCells> createState() => _TableCellsState();}
class _TableCellsState extends State<TableCells> { String range = '1 × 1'; String status = 'No row opened';
@override Widget build(BuildContext context) => Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 7, child: DataTable( rowCount: 100, autofocus: true, selectionMode: DataTableSelectionMode.cell, onRangeChanged: (selection) => setState(() { range = '${selection.rowCount} × ${selection.columnCount}'; }), onAction: (row) => setState(() => status = 'Opened row ${row + 1}'), onCopy: (result) => setState( () => status = 'Copied ${result.export.rowCount} × ${result.export.columnCount}', ), columns: const [ DataTableColumn( id: 'name', title: 'Name', width: FixedColumnWidth(12), ), DataTableColumn(id: 'status', title: 'Status'), ], cellBuilder: (row, column) => column == 'name' ? 'Row ${row + 1}' : 'Ready', ), ), Text('Range: $range'), Text(status), ], );}onSelect confirms a row choice. In cell mode, onRangeChanged reports the selected cells and onAction runs the row command. Scrolling leaves the cursor and range unchanged.
For programmatic navigation, keep a DataTableController in state and set currentRowIndex or currentColumnIndex. Use selectCell to change the range. Controller writes do not call the interaction callbacks.
Details
Section titled “Details”Row mode uses arrows to browse and click or Enter to choose. Cell mode
keeps the navigation cursor separate from a range selected for copying;
Enter or double-click runs a row command. The wheel only scrolls.
Ctrl+C copies the current row or selected cell range as TSV or CSV.
Clicking the header of a sortable column calls onSort; the header then
shows the sort you pass back as sortColumnId and sortDirection.
Unlike Table, this widget does not mount every cell as a widget. It asks
cellBuilder only for the visible body rows, paints those directly into the
cell buffer, and contributes visible-row semantics from the render object.
Constructors
Section titled “Constructors”DataTable()
Section titled “DataTable()”| Parameter | Type | Default | Description |
|---|---|---|---|
rowCount: | int | required | Number of source rows available to the table. |
shrinkWrap: | bool | false | Whether an unbounded parent may size the table to all source rows. Keep false for virtualization; use Expanded in a Column or a SizedBox with height. Enable only for deliberately content-sized small tables. |
columns: | List<DataTableColumn> | required | Column definitions, in display order. |
cellBuilder: | DataTableCellBuilder | required | Returns the display text for a visible cell. |
rowKeyBuilder: | DataTableRowKeyBuilder? | — | Optional stable row identity used by semantics and copy callbacks. |
controller: | DataTableController? | — | External navigation and range controller. If omitted, the table owns one. |
currentRowIndex: | int? | — | Parent-owned browsing row. Supply onFocusedItemChanged to accept input requests by rebuilding with the requested row. Ignored requests leave the cursor at this value. Out-of-range values are clamped to the available rows. More |
focusNode: | FocusNode? | — | Focus node used for keyboard navigation. |
autofocus: | bool | false | Whether the table should request focus when mounted. |
onSelect: | void Function(int rowIndex)? | — | Confirms a row on a completed click, Enter, or semantic select/press. Available in row mode. Reconfirming the same row calls this again. |
onAction: | void Function(int rowIndex)? | — | Runs a row command on Enter, a completed double-click, or semantic press. Available in cell mode; selecting a cell range does not invoke it. |
onFocusedItemChanged: | void Function(int rowIndex)? | — | Reports user navigation to a different row, including pointer and semantic input. Column-only movement, scrolling, and controller writes do not fire it. |
onRangeChanged: | void Function(DataTableSelectionRange range)? | — | Reports a changed cell range after user selection, including semantic selection. Navigation alone and programmatic controller writes do not fire it. Available in cell mode. |
typeahead: | bool | true | Whether typing a printable character moves the cursor to the next row whose first-column cell starts with it (grid type-ahead). On by default. Turn it off when the surrounding app binds bare printables (a vim-style command key, a q quit): a focused table with type-ahead on consumes every printable before those bindings see it. |
selectionMode: | DataTableSelectionMode | DataTableSelectionMode.row | Choose rows with click/Enter, or select cell ranges and invoke a separate row command with double-click/Enter. |
copySelectedRow: | bool | true | Whether Ctrl+C and semantic copy export the current selection. |
copyOptions: | DataTableCopyOptions | const DataTableCopyOptions() | Export and clipboard options used when copying table data. |
onCopy: | void Function(DataTableCopyResult result)? | — | Called after a copy attempt completes. |
columnSpacing: | int | 1 | Empty cells inserted between adjacent columns. |
headerSeparator: | bool | true | Whether to draw a separator below the header row. |
separatorStyle: | CellStyle? | — | Style used for header and row separators. |
selectedStyle: | CellStyle? | — | Style merged onto the current row in row mode or selected cells in cell mode. |
sortColumnId: | String? | — | Id of the column your data is sorted by. If that column is sortable (DataTableColumn.sortable), its header shows ▲ or ▼ for sortDirection. The sort is also exposed through semantics; the table never reorders rows itself. |
sortDirection: | DataTableSortDirection? | — | Direction of the current sort: the sortColumnId header shows ▲ for ascending and ▼ for descending (^ and v where the terminal draws those symbols two cells wide). Also exposed through semantics. |
onSort: | void Function(String columnId)? | — | Called with a column’s id when the user clicks its header or activates it through semantics, only for columns whose DataTableColumn.sortable is true. The app sorts its data and chooses the direction, then rebuilds with sortColumnId and sortDirection to show the new sort. |
filterText: | String? | — | App-owned filter text exposed through semantics. |
semanticLabel: | String? | 'Data table' | Semantic label for the table. |
currentRowIndex: Update this androwCounttogether when filtering or replacing data. This live value cannot be combined withcontroller. If omitted, the supplied controller or an internal controller owns navigation. It does not invokeonSelector change the independently selected cell range.
Source
Section titled “Source”DataTable is defined in packages/fleury/lib/src/catalog/data_table.dart.
Category: Lists & data · All widgets