grid_sheet 3.0.1
grid_sheet: ^3.0.1 copied to clipboard
A powerful Flutter DataGrid/DataTable for large datasets, offering Excel-like features and fully customizable cells — ready with minimal setup. See README for full features
GridSheet #
It is a highly flexible, high-performance Flutter DataTable / DataGrid package designed for building rich tabular UIs similar to Excel or spreadsheet applications, with minimal configuration and powerful built-in features. It gives developers full control over both data management and UI presentation while remaining easy to set up and extend.
Demo #

Explore GridSheet's features through our live demos. Every demo page has a Code tab with its complete source.
- Live Demo: View GridSheet Features
- Features Guide: GridSheet Features Documentation
- API Reference: GridSheet API — every class, property and method, with examples
- Upgrading to 3.0.0: see the Migration Guide
Found a bug or have an idea to improve GridSheet? We'd love to hear from you.
- Report bugs or request features: here
Table of Contents #
Highly Configurable #
GridSheet is designed to adapt to different application requirements rather than enforcing a fixed set of behaviors.
Most features are configurable and can be enabled, disabled, or customized through the available configuration classes and callbacks. Whether you need a lightweight data table or a fully featured spreadsheet-like experience, you can choose only the functionality your application requires.
Every application has different requirements, so GridSheet focuses on providing sensible defaults while allowing you to override behavior whenever needed. Most visual components, interactions, and data handling mechanisms can be customized through configuration objects, builder callbacks, and event listeners.
You can integrate GridSheet with local data sources, server-side APIs, or your own state management solution. Features can be enabled independently, allowing you to keep the widget lightweight while adding advanced capabilities only where they are needed.
The package also exposes customization points for headers, rows, filters, footers, cells, pagination, loading states, empty states, context menus, and styling. This makes it easy to match your application's design system without modifying the package itself.
As new features are introduced, they are designed to remain configurable and backward compatible whenever possible, ensuring existing implementations continue to work while giving you access to additional functionality.
The goal of GridSheet is to provide a flexible foundation that can scale from simple data tables to complex enterprise applications with minimal changes to your existing code.
Features #
Core Functionality #
- ✅ Data Display & Management - Display tabular data with full CRUD operations on rows and columns
- ✅ Multi-Column Sorting - Sort by multiple columns with ascending/descending order
- ✅ Advanced Filtering - 14 comparison modes (contains, exact match, greater/less than, between, LIKE patterns, and more), combining values with AND/OR, plus typed operators (
>100,=Apple,app%) parsed automatically with no comparison chosen - ✅ Cross-Column Search - Search across every cell in every row, narrowing progressively as more terms are added, with the same typed-operator parsing (
>100,app%) as filtering - ✅ Pagination - Built-in pagination with customizable rows per page (client-side, server-side, and infinite scroll)
- ✅ Column Pinning - Pin columns to the left or right side, excluded from horizontal scrolling
- ✅ Row Pinning - Pin specific rows to a non-scrolling band above or below the scrollable section, excluded from sorting, filtering, and pagination
- ✅ Column Reordering - Drag-and-drop column reordering with visual feedback,.
onColumnReordercallback - ✅ Row Reordering - Long-press-and-drag row repositioning with visual feedback,
onRowReordercallback. Works even without a serial-number column — the whole row becomes the drag handle - ✅ Column Resizing - Resize columns by dragging column borders
- ✅ Row Resizing - Resize rows by dragging a handle on their bottom edge. Works even without a serial-number column — every column grows its own handle
- ✅ Cell Editing - Inline cell editing with type-specific input formatters
- ✅ Dropdown Cell Type - Built-in dropdown/select cells with a constrained options list and popup menu
Selection & Navigation #
- ✅ Multi-Selection - Select multiple rows, columns, or cells simultaneously
- ✅ Keyboard Navigation - Navigate between cells with arrow keys, Tab/Shift+Tab, and Enter/Shift+Enter
- ✅ Select All - Tri-state header checkbox (unchecked/indeterminate/checked) to select/deselect all visible rows
- ✅ Selection Callbacks - Events for row, column, and cell selection changes
Data Manipulation #
- ✅ Change Tracking - Track inserted, modified, and deleted rows
- ✅ Position-Based Insert - Insert rows/columns above/below or before/after the current selection
- ✅ Undo Operations - Revert changes to individual rows or all changes
- ✅ Cell Undo/Redo - Step back and forward through cell edits with
gridManager.historyor Ctrl+Z / Ctrl+Y - ✅ Bulk Operations - Clear, fill, find, and replace operations on cell ranges
- ✅ Copy/Paste - Clipboard support for tab-delimited data
- ✅ Auto-Fill - Fill cells with patterns and automatic incrementing
Visual Customization #
- ✅ Conditional Formatting - Apply background colors and text styles based on expressions
- ✅ Custom Cell Widgets - Build custom widgets for headers, filters, and data cells via
cellBuilder, optionally organized in anIGridSheetCustomCellBuilderimplementation - ✅ Row State Indicators - Visual indicators for inserted, modified, and deleted rows
- ✅ Theme Support - Comprehensive styling with light/dark theme support
- ✅ Custom Wrappers - Wrap header, filter, and row widgets with custom containers
Advanced Features #
- ✅ Row & Column Virtualization - Rows and non-pinned columns are windowed (Sliver-backed), only building what's currently visible plus a small overscan buffer — scrolls smoothly with 100K+ rows and very wide schemas
- ✅ Row Expansion - Inline master-detail panel below each row, rendered full-width across all pinned/scrollable columns, with responsive field layout, animated open/close, and full cell widget consistency
- ✅ Row Separators - Insert a custom full-width widget below any row via
rowSeparatorBuilder— section dividers, group banners, subtotal rows, and more - ✅ Row Swipe - Swipe a row sideways to reveal a custom strip on either side via
rowSwipeConfiguration. - ✅ Row Grouping - Group rows by one or more columns (by value, numeric range, date part, or custom logic) under collapsible headers, with per-group aggregates (sum, average, min, max, count, custom) and a grand total via
groupingConfiguration.rows - ✅ Column Grouping - Gather related columns under nested header bands (e.g. Contact over Name, Email and Phone) that can collapse to fewer columns, across pinned and scrollable columns, via
groupingConfiguration.columns - ✅ Form Layout - Card-based
label : valuelayout for narrow/mobile viewports, switching automatically at a configurable breakpoint while sharing the table's selection, editing, reorder, and pagination - ✅ Runtime Configuration Updates - Toggle visual and behavioral flags at runtime via
gridManager.config.updateGridwithout rebuilding the parent widget - ✅ Data Reload - Manual reload on demand via
gridManager.reload, plus interval-based and daily scheduled auto-reload withonBeforeReload/onAfterReloadlifecycle callbacks - ✅ Expression Evaluation - Evaluate conditional expressions for cell editability and formatting. Implement
GridSheetEvaluatorto use your own logic for conditional expressions - ✅ Context Menus - Right-click context menus for cells, rows, and columns
- ✅ Column Visibility - Show/hide columns dynamically
- ✅ Row Visibility - Show/hide rows dynamically, addressed by row key
- ✅ Row Height Management - Auto-fit or manually adjust row heights
- ✅ Column Width Management - Auto-fit or manually adjust column widths
- ✅ Snapshot/Restore - Save and restore grid state for undo/redo functionality
- ✅ Statistics - Calculate sum, average, min, max, median for numeric columns
- ✅ Data Export - Export to CSV with customizable options
- ✅ Loading States - Display custom loading indicators during operations
- ✅ Formula Columns - Excel-style formulas, automatic evaluation, reactive updates, and filter support
- ✅ Undo Delete - After deleting rows or columns, show an UNDO prompt instead of deleting permanently right away.
- ✅ Right-to-Left (RTL) Support - Full right-to-left support: the whole grid mirrors its layout, scrolling, icons and navigation.
- ✅ Localization - Translate the grid's own text (tooltips, pagination footer, undo banner, group and pivot labels) with
GridSheetLocalizations, per grid or through your app'slocalizationsDelegates. - ✅ Animations - Optional reveal animations for columns and rows, and a flash when a cell's value changes.
- ✅ Dynamic Table Generation - Generate a table of any size from a column and row count, for prototypes and performance testing.
Widgets #
The package provides several customizable widget options:
- ✅ Empty Rows Widget - Show your own widget when the grid has no rows..
- ✅ Empty Columns Widget - Show your own widget when every column is hidden..
- ✅ Loading Widget - Show your own loading indicator while the grid is loading or processing data.
- ✅ TopBar Widget - Optional widget displayed above the table.
- ✅ BottomBar Widget - Optional widget displayed below the table.
Installation #
Add grid_sheet to your pubspec.yaml:
dependencies:
grid_sheet: ^3.0.1
Then run:
flutter pub get
Import #
import 'package:grid_sheet/grid_sheet.dart';
Usage #
Basic Setup #
late final GridSheetManager gridManager;
GridSheet(
columns: [
GridSheetColumn(
key: const ValueKey('col_name'),
index: 0,
name: 'COL_NAME',
title: 'Name',
type: GridSheetColumnType.text(),
width: 200,
),
GridSheetColumn(
key: const ValueKey('col_age'),
index: 1,
name: 'AGE',
title: 'Age',
type: GridSheetColumnType.double(),
width: 80,
textAlign: TextAlign.right,
),
GridSheetColumn(
key: const ValueKey('col_active'),
index: 2,
name: 'ACTIVE',
title: 'Active',
type: GridSheetColumnType.boolean(),
width: 80,
),
],
rows: List.generate(
50,
(i) => GridSheetRow(
key: ValueKey('row_$i'),
index: i,
data: [
'Person $i',
20 + i,
i.isEven,
],
),
),
onLoaded: (event) {
gridManager = event.gridManager;
debugPrint('Table initialized!');
},
);
Data Ownership #
The grid changes the
GridSheetRowobjects you pass it, in place. As the grid is used it writes into each row object: cell values (also rearranged when columns are reordered), change state and original data, selection, height, position and pinning.
That has two consequences:
- Don't share row objects between grids. Two
GridSheets given the sameGridSheetRowobjects edit each other's rows. Build a separate list of rows for each grid. - Don't keep
GridSheetRowobjects in immutable state (Bloc, Riverpod, Redux, …). The grid would change the objects your earlier states still hold. Keep your own models in state, map them to rows when building, and read the edits back through the manager:
GridSheet(
columns: columns,
// New row objects from your own models on each build. Rebuilding with
// equal data changes nothing, so edits made in the grid survive.
rows: [
for (final (i, p) in people.indexed)
GridSheetRow(key: ValueKey(p.id), index: i, data: [p.name, p.age, p.active]),
],
onCellValueChanged: (event) {
// Read edits back as plain values, not as the row objects.
final edited = event.gridManager.data.toMap(event.rowKey);
// ...update your own state from `edited`.
},
);
Passing different rows or columns (by key and content) replaces the grid's data and keeps sort, filters, grouping, the page, and the selection and expansion of rows that are still there. If the user has unsaved edits at that moment, the new data wins. Unsaved edits are listed in gridManager.changes.
Platform Support #
GridSheet works in web browsers and supports all Flutter platforms:
- ✅ Android
- ✅ iOS
- ✅ Web (Chrome, Edge, Firefox, Safari)
- ✅ Windows
- ✅ macOS
- ✅ Linux
Notes #
Performance Considerations #
- Both rows and non-pinned columns are virtualized (windowed): only the rows and columns currently visible, plus a small overscan buffer, are actually built — scrolling in either direction stays smooth into the 100K-row / hundreds-of-column range without needing pagination purely for rendering performance.
- Pinned-left and pinned-right columns are never windowed (there are typically few of them), and always fully built for every visible row.
- Pagination (client-side, server-side, or infinite scroll) is still the right tool when the dataset itself — not just its on-screen rendering — is too large to hold in memory at once; use server-side pagination or infinite scroll for very large remote datasets so only one page of rows is fetched/held at a time.
- Limit the number of conditional format rules for better performance
- Avoid excessive column reordering in production
- The controller pool automatically manages TextEditingControllers, bounding retention to the visible row window (plus overscan) rather than the whole current page — most impactful with pagination disabled, where the "current page" is the entire dataset
- Column/row drag-resize and row-expansion animation frames update only the affected row or column's layout instead of rebuilding the whole grid's row/column extents
- Formula expressions are parsed once and cached by their text, so repeated recalculation of the same formula doesn't re-tokenize/re-parse it
Limitations #
- Column expressions use the
expressionspackage for evaluation - Custom evaluators can be provided for advanced expression handling
- Multi-column sorting preserves order by priority (first added = highest priority)
- While rows are grouped, rows can't be reordered by dragging (the same as while sorted), client-side pagination counts group header and summary lines toward
rowsPerPage, and the form layout shows its cards in a single list - Column group bands aren't shown in the form layout. Collapsing a group hides its columns on screen only (they stay
visible); exports of the visible columns match the screen, andGridSheetColumnScope.allincludes them.data.toGrouped()returns the data with its row and column grouping for exporting to any format
Best Practices #
- Use stable keys - Always provide unique
ValueKey<String>for rows and columns - Leverage callbacks - Use
onCellValueChangedto track changes in real-time - Manage state externally - Use
GridSheetManagerfrom callbacks to update external state - Optimize rendering - Use
hasTextController: falsefor read-only columns or when using custom widgets that do not require aTextEditingController, preventing unnecessary controller creation and improving performance.showOverflowTooltipdefaults tofalse(no per-cell overflow measurement); opt it in withshowOverflowTooltip: trueonly on columns where truncated text is likely (e.g. free-formtextcolumns) and the hover tooltip is worth the cost. - Handle loading states - Provide a
loadingIndicatorfor better UX during async operations
Contributions #
If GridSheet has been helpful, please consider giving our Pub package a 👍. You can also support the project by spreading the word and sharing it with others.
We truly appreciate your support and contributions to the ongoing development of this project. We welcome discussions, feedback, and pull requests—feel free to share your ideas or suggestions on our GitHub repository. Issues Tracker
License #
This project is licensed under the MIT License - see the LICENSE file for details.