flutterx_table 2.0.0
flutterx_table: ^2.0.0 copied to clipboard
Highly customizable Flutter table with selection, sorting, filtering, actions, column swap, paging and scrolling modules.
flutterx_table #
Highly customizable Flutter table built from composable modules: row selection, sorting and filtering, row actions, column reordering, plus asynchronous paged loading with paging indicators and scroll views.
Installation #
flutter pub add flutterx_table flutterx_live_data
import 'package:flutterx_table/flutterx_table.dart';
Table #
XRawTable renders a heading from the TableColumns and one row per item, with the cells you yield
per row. Pass a
LiveList as items and the table rebuilds when it changes:
final users = LiveList<User>(source: initialUsers);
AutoScrollView(
child: XRawTable<User>(
columns: const [TableColumn(id: 'name', label: 'Name'), TableColumn(id: 'email', label: 'Email')],
items: users,
rowDecorator: XRawTable.oddRowDecorator(),
placeholder: (context) => const Center(child: Text('No users')),
rowBuilder: (context, row, user) => [Text(user.name), Text(user.email)],
),
)
A column may have a heading widget instead of a label, a columnWidth, and the sortable /
filterable flags read by the sort and filter module. Column ids are unique.
Modules #
Modules extend the table through modules, applied in list order; the columns they add (selection
first, actions last) are not part of your rowBuilder.
modules: [
TableSelectionModule<User>(keyOf: (user) => user.id, multipleSelection: selection),
TableSortFilterModule<User>(sortOptions: sort, filterOptions: filter),
TableActionsModule<User>(actions: [
TableAction<User>(icon: const Icon(Icons.delete), tooltip: 'Delete', onAction: remove),
]),
ColumnSwapModule<User>(onSwap: moveColumn),
],
- Selection:
multipleSelection(LiveSet<String>of keys) adds a checkbox column;singleSelection(MutableLiveData<String?>) selects one row.onTapBehaviour(TableRowOnTapBehaviour.none,singleSelect,singleSelectToggle,multiSelect) says what a tap on a row does; it defaults to none whenonTapis set, otherwise to selecting the row. - Sort and filter:
sortis aMutableLiveData<SortOptions?>andfilteraMutableLiveData<FilterOptions?>. Tapping a sortable heading cycles ascending, descending, none (sortClearable: falsegoes back to ascending); the filter button opens a text field. The module only edits the options: sort and filter the data yourself, or load it withContentTableBuilder.SortOptionsandFilterOptionshavetoJson/fromJson. - Actions:
TableActionis an icon button per row;actionStatereturnsActionState.enabled,disabledorinvisibleper item;TableAction.rawtakes any cell widget. - Column swap: long-press a heading and drag. The table does not reorder itself; move the column
in
onSwap:
void moveColumn(TableColumn column, int oldIndex, int newIndex) => columns.apply((list) {
list.insert(newIndex > oldIndex ? newIndex - 1 : newIndex, list.removeAt(oldIndex));
return true;
});
To write your own module extend TableModule.
Paged data #
ContentTableBuilder loads the items of the current page, reloads when the page, sort or filter
change (after
updateDelay, kThemeAnimationDuration by default) and shows the paging indicators under the
content:
final pageInfo = MutableLiveData(value: const PageInfo.empty(pageSize: 10));
final sort = MutableLiveData<SortOptions?>();
final filter = MutableLiveData<FilterOptions?>();
final tableKey = GlobalKey<ContentTableBuilderState<User>>();
ContentTableBuilder<User>(
key: tableKey,
pageInfo: pageInfo,
sortOptions: sort,
filterOptions: filter,
itemsBuilder: (page, filter, sort) => api.users(page, filter, sort), // FutureOr<PagedList<User>>
builder: (context, users, loading) => AutoScrollView(child: XRawTable<User>(/* ... */ items: users)),
)
await tableKey.currentState!.refresh(); // reloads the current page even if nothing changed
itemsBuilder returns a PagedList: the items of the page and the PageInfo the server
computed, which replaces the requested one. PagedList.fromJson(json, User.fromJson) reads
{"pageInfo": {...}, "items": [...]}.
Paging #
PageInfo holds page (zero-based), pageSize, pageCount and itemsCount, and asserts they are
consistent. Build it with PageInfo.empty(pageSize:), PageInfo.exactly(itemsCount:) or
PageInfo.of(pageSize:, itemsCount:), and derive from it with copyWith.
The indicators read the PageInfo of the enclosing PagingIndicatorLayout, which
ContentTableBuilder provides:
PagingIndexIndicator (numbered buttons, the default pageIndicator), PagingArrowsIndicator
(first / previous / next / last) and RowsPerPageIndicator (rowsPerPageIndicator: null hides it).
Outside a builder, place them in a layout:
PagingIndicatorLayout(
pageInfo: pageInfo,
pageIndicator: const PagingArrowsIndicator(),
)
Scrolling #
AutoScrollView scrolls its child in both axes with scrollbars; widthBehaviour and
heightBehaviour take a
SizeBehaviour (matchContainer, intrinsic, none). IntrinsicScrollView scrolls on one axis
(scrollDirection)
and is as large as its child unless the parent gives less.
Behavior #
TableSelectionModule.rowMultipleSelectionColorandrowSingleSelectionColor, like the default ofXRawTable.oddRowDecorator, come from the theme whennull; passColors.transparentto turn the highlight off.- Icons in the cells are scaled by the ambient
IconThemesize times the cell text size over the theme body text size, so a largertextStylegives larger icons. onSwapis not called when the drag is cancelled or the heading is dropped at its own position.newIndexfollowsReorderableList.onReorder: it is the index before the moved column is removed.PageInfo.copyWithwithoutpageandPageInfo.fromJsonlimit the page to the last valid one instead of failing; an explicitpageincopyWithor the constructor must be valid.- An error of
itemsBuilderwithoutonErroris reported toFlutterError.reportError; the page, sort and filter revert to the last loaded ones. WithonError, its return value decides whether to revert. refresh()waits forupdateDelay, and completes at once if a newer change supersedes it.- All the rows are built at every rebuild: there is no virtualization, so page the data.
Claude Code plugin #
The repository ships a plugin with a skill that teaches Claude Code to integrate the library:
/plugin marketplace add https://gitlab.com/dade.sliep/flutterx_table
/plugin install flutterx-table@flutterx-table