table_view_ex 0.1.6
table_view_ex: ^0.1.6 copied to clipboard
A more feature rich data table control built on TableView with sortable, resizable and rearrangeable headers and customized styling
TableViewEx #
A powerful, Flutter table widget with a rich set of features for enhanced user interaction and customization. It's an extended TableView control to present multi-column data. It is built on top of Flutter's two_dimensional_scrollables library.
The main control to use is ViewOnlyTableViewEx.
Table of Contents #
- Why do we need it?
- Screenshot
- Requirements
- General
- Features
- Details
- Configurable Properties
- Usage
- Issues/limitations/caveats
- License
Why do we need it? #
I'm glad you asked! TableView is great to present data (read full set of features), but it skipped on some features that are desired in many places. Some of the features that were missing and have been implmented here:
- Column width change: Flutter is no longer limited to mobile and tablet devices. On a traditional PC desktop, you would be using mouse or similar pointing devices. In those situations, you may want to expand or shrink width of certain columns. This is implemented in
TableViewEx. - Column re-arrangement: If you need to rearrange columns - moving them left or right - this is implemented in
TableViewEx. Now you can drag and drop over other column. - Column sorting:
TableViewExallows the user to sort the column according to configured comparison algorithm with visual indicator.
TableViewEx makes it slightly easier to configure and customize TableView.
Screenshot #
Requirements #
- Flutter SDK
two_dimensional_scrollablespackage- Dart SDK version compatible with null safety
General #
ViewOnlyTableViewExis composed of its own features and an instance ofTableViewfromtwo_dimensional_scrollablespackage.- Column width calculations are performed once and cached for optimal performance
- The widget maintains state for column resizing, reordering, and sorting operations
- Proper hit-testing ensures resize handles don't interfere with drag-and-drop operations
Features #
- Column Resizing: Users can dynamically resize columns by dragging the column dividers.
- Column Reordering: Easily reorder columns using intuitive drag-and-drop functionality.
- Column Sorting: Supports data sorting with visual indicators (ascending/descending arrows) in the header.
- Customizable Scrollbars: Fine-tune the visibility, color, and thickness of both vertical and horizontal scrollbars.
- Custom Row Colors: Apply custom colors to rows for improved readability and aesthetics.
- Configurable Borders: Define custom styles for horizontal and vertical borders between cells.
- Auto-fit Column Width: Double-click a column divider to automatically resize the column to fit its content.
- Pinned Header: An optional header row that remains visible while scrolling vertically through the data (carried over from
TableView).
Details #
Column Resizing #
Users can resize columns by:
- Hovering over the right edge of a column header (cursor changes to resize indicator)
- Dragging to adjust width
- Double-clicking to auto-fit content (requires
contentMaxWidthProvider)
To have auto-fit content, you need to provide contentMaxWidthProvider like:
contentMaxWidthProvider:
(colIndex) => _myContentMaxWidthCalculator(columnDefs[colIndex], rows),
Since it is up to you - what you want to put in the table cells - the control cannot assume it to be directly measurable. Hence, this implementation shall be provided by you. Suppose your content is made up of string/num or basic data type content, you can use this kind of implementation:
/// Find widest cell content in pixels
double _myContentMaxWidthCalculator(
TableViewExColumnConfig columnDef,
List<Map<String, String?>> rows) {
double maxWidth = 0.0;
for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) {
final Map<String, String?> row = rows[rowIndex];
String contentAsText;
if (row[columnDef.key] == null) {
contentAsText = "";
} else if (row[columnDef.key] is String) {
contentAsText = row[columnDef.key] as String;
} else {
// Force the text conversion - can be controlled by you
contentAsText = row[columnDef.key].toString();
}
final measuredWidth = _measureTextSize(contentAsText, null).width;
maxWidth = max(maxWidth, measuredWidth);
}
return maxWidth;
}
/// Rough estimation of the text size in pixels
Size _measureTextSize(String? text, TextStyle? style) {
if (text == null || text.isEmpty) {
return Size.zero;
}
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: style),
maxLines: 1,
textDirection: TextDirection.ltr,
)..layout(minWidth: 0, maxWidth: double.infinity);
return textPainter.size;
}
Content Sorting #
Implement sorting by providing an onSortRequested callback. The widget will automatically display sort indicators (up/down arrows) for the currently sorted column.
onSortRequested: (columnIndex) {
yourSortingMethod(columnIndex);
setState(() {
// Update your data source
});
},
Column Reordering #
Enable column reordering by setting allowColumnReordering: true. Users can then drag column headers to reorder them.
Note: In order to deal with reordering of columns along with sorted column tracking, you need to write your Action<TableViewExSortedColumnMovedIntent> handler. Essentially, if you have sorted the data on a column, and then proceeded with reordering, the sorted column index may be required to be tracked outside the control, as the sorting of content is done by consumer of the ViewOnlyTableViewEx control. The ViewOnlyTableViewEx control raises action for the intent TableViewExSortedColumnMovedIntent which the control's parent need to track.
Sample action handler:
class TableViewExSortedColumnMovedIntentHandler
extends Action<TableViewExSortedColumnMovedIntent> {
final void Function(int newSortedColumnIndex) onNotified;
TableViewExSortedColumnMovedIntentHandler(this.onNotified);
@override
void invoke(covariant TableViewExSortedColumnMovedIntent intent)
=> onNotified(intent.newSortedColumnIndex);
}
Full code is available in the example app.
Handling pointer events #
These are the supported pointer events. You need to write your handlers to make use of them:
Action<TableViewExCellSelectionChangeIntent>: Notifies parent widget that selection has changed. Provides data of the cell's row and column indices.Action<TableViewExCellDoubleTapIntent>: Notifies parent widget that double tap/click has been performed. Provides data of the cell's row and column indices. When a cell is double-tapped, the selection intentTableViewExCellSelectionChangeIntentis also raised.
Handler template code:
/// Selection change Intent action handler
class TableViewExCellSelectionChangeIntentHandler extends Action<TableViewExCellSelectionChangeIntent> {
final void Function(int? rowIndex, int? colIndex) onNotified;
TableViewExCellSelectionChangeIntentHandler(this.onNotified);
@override
void invoke(covariant TableViewExCellSelectionChangeIntent intent) => onNotified(intent.rowIndex, intent.colIndex);
}
/// Double click/tap Intent action handler
class TableViewExCellDoubleTapIntentHandler extends Action<TableViewExCellDoubleTapIntent> {
final void Function(int? rowIndex, int? colIndex) onNotified;
TableViewExCellDoubleTapIntentHandler(this.onNotified);
@override
void invoke(covariant TableViewExCellDoubleTapIntent intent) => onNotified(intent.rowIndex, intent.colIndex);
}
Check code of example app.
Custom Row Colors #
Here in this sample, we create zebra-striped tables with custom row colors:
rowBackgroundColorProvider:
(rowIndex) => rowIndex.isEven ? Colors.grey[50]! : Colors.white,
Initial Column Width - Custom Calculations: #
In case you need to compute your own column widths, implement your TableViewExWidthCalculator, like this:
class MyTableViewExWidthCalculator extends TableViewExWidthCalculator {
/// Calculate the actual pixel widths based on the current constraints
@override
List<double> calculateColumnWidths(
double viewportWidth,
List<TableViewExColumnConfig> columnDefinitions) {
// your logic - divide viewportWidth among the number of columns.
// Just ensure that your output array length is same as the length of `columnDefinitions`.
return [ ... ];
}
}
Then consume it when creating instance of ViewOnlyTableViewEx via:
columnWidthCalculator : MyTableViewExWidthCalculator()
Configurable Properties #
For detailed information about all the configurable properties of TableViewEx, please see CONFIGURATION.md.
Usage #
Please check the example folder which demonstrates various features of the widget.
Example app has been deployed to https://cnayan.github.io/table_view_ex/ as well.
Issues/limitations/caveats #
- Any limitation of
TableViewwill impact this control. Kindly check the issues of that package to see if your concern is addressed there.
License #
Copyright 2025 Nayan Choudhary. All rights reserved. Use of this source code is governed by a MIT license.
