table_view_ex 0.1.6 copy "table_view_ex: ^0.1.6" to clipboard
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

Pub Version GitHub Tests Codecov

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? #

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: TableViewEx allows 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 #

image

Requirements #

  • Flutter SDK
  • two_dimensional_scrollables package
  • Dart SDK version compatible with null safety

General #

  • ViewOnlyTableViewEx is composed of its own features and an instance of TableView from two_dimensional_scrollables package.
  • 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:

  1. Hovering over the right edge of a column header (cursor changes to resize indicator)
  2. Dragging to adjust width
  3. 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 intent TableViewExCellSelectionChangeIntent is 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 TableView will 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.

2
likes
160
points
3
downloads
screenshot

Publisher

verified publishertech-disasters.blogspot.com

Weekly Downloads

A more feature rich data table control built on TableView with sortable, resizable and rearrangeable headers and customized styling

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, two_dimensional_scrollables

More

Packages that depend on table_view_ex