flow_ui_datatable 0.2.3
flow_ui_datatable: ^0.2.3 copied to clipboard
A beautiful, universal Flutter data table with spreadsheet-style hover UX, sorting, pagination, and selection.

Flow UI DataTable #
A beautiful, universal Flutter data table with spreadsheet-style hover UX. Works with any row type and any column layout while keeping a polished, production-ready look.

Features #
- Universal API — generic
FlowDataTable<T>with custom column definitions - Same UI/UX — hover highlight, subtle scale animation, bordered spreadsheet layout, dark/light theme support
- Built-in cell types — text, avatar+subtitle, status badges, icon+text, dot indicator, dropdown
- Sorting — per-column sort with client-side or server-side control
- Pagination — optional footer with page navigation and page-size selector
- Row selection — optional checkbox column with select-all
- Row index — optional
#column - Actions column — optional trailing actions per row
- Loading & empty states
- Horizontal scroll — for wide tables
- Fully themeable —
FlowTableThemefor colors, sizes, fonts
Getting started #
Add to pubspec.yaml:
dependencies:
flow_ui_datatable: ^0.1.0
Usage #
import 'package:flow_ui_datatable/flow_ui_datatable.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
FlowDataTable<Product>(
rows: products,
showRowIndex: true,
selectable: true,
selectedRowIds: selectedIds,
onSelectionChanged: (ids) => setState(() => selectedIds = ids),
rowIdGetter: (product, _) => product.id,
showActionsColumn: true,
actionsBuilder: (context, product, index) => FlowCells.actionsButton(
onPressed: () => _onAction(product),
),
columns: [
FlowColumn(
id: 'name',
label: 'Product',
icon: LucideIcons.package,
width: const FlowFlexColumnWidth(2),
sortable: true,
sortValue: (p) => p.name,
cellBuilder: (context, product, _) => FlowCells.text(context, product.name),
),
FlowColumn(
id: 'status',
label: 'Status',
icon: LucideIcons.activity,
cellBuilder: (context, product, _) => FlowCells.badge(context, product.status),
),
],
)
Built-in cells #
The library provides a FlowCells utility class containing pre-styled cells matching the design system:
- Text cell:
FlowCells.text(context, 'Text value') - Avatar with Subtitle:
FlowCells.avatarWithSubtitle(context, title: 'John Doe', subtitle: 'john@example.com') - Badge cell:
FlowCells.badge(context, 'Active') - Icon with Text:
FlowCells.iconWithText(context, icon: LucideIcons.checkCircle, text: '5 done') - Dot Indicator with Text:
FlowCells.dotWithText(context, text: 'Online', isActive: true) - Dropdown cell:
An inline editable dropdown cell. Pass
isPlain: trueto display it as a plain text cell with a chevron down icon instead of a styled button pill.FlowCells.dropdown( context, value: role, options: const ['Admin', 'Member', 'Owner'], isPlain: true, // true for plain cell, false for styled button pill onChanged: (newRole) => setState(() => role = newRole), )
Custom cells #
Use any widget in cellBuilder:
FlowColumn(
id: 'custom',
label: 'Custom',
cellBuilder: (context, row, index) => MyCustomWidget(data: row),
)
Pagination #
pagination: FlowPagination(
currentPage: page,
pageSize: 25,
totalItems: totalCount,
onPageChanged: (p) => setState(() => page = p),
onPageSizeChanged: (size) => setState(() => pageSize = size),
),
Theming #
FlowDataTable<T>(
theme: const FlowTableTheme(
fontFamily: 'Inter',
borderRadius: 8,
rowHeight: 56,
),
// ...
)
Example #
Live demo: flowui.lol
See the example/ folder for a full demo reproducing the original user-management table.
cd example
flutter run
AI Code Assistant Setup (skills.md) #
You can install skills.md rules into your workspace to help AI coding assistants (like Claude, Cursor, GitHub Copilot, Roo Code, or Antigravity) understand the flow_ui_datatable library specifications and design rules.
Command-line Installation (Recommended) #
Run the interactive CLI installer to automatically install the rules for your favorite AI tools:
dart run flow_ui_datatable:install_skills
The installer will ask:
- Which AI tools to install rules for (Cursor
.cursorrules, Antigravity.gemini/skills/, Copilot.github/copilot-instructions.md, Cline/Roo Code.clinerules, or All). - Whether to install them globally (User profile folder) or locally for the current project.
Manual Setup #
If you prefer to install it manually, copy the text in the box below and create the rules file for your tool:
- Cursor: Create
.cursorrulesin your project root. - Cline / Roo Code: Create
.clinerulesin your project root. - GitHub Copilot: Create
.github/copilot-instructions.mdin your project root. - Antigravity: Create
.gemini/skills/flow_ui_datatable/SKILL.mdin your project root.
Click to show manual rules.md content
---
name: flow_ui_datatable
description: Guidelines and instructions for creating beautiful, theme-compliant, and Material-free data tables using the flow_ui_datatable package.
---
# AI Assistant Rules & Skills for `flow_ui_datatable`
This file provides context, API specifications, and design guidelines for AI coding assistants (Claude, Cursor, Codex, Antigravity) to write correct, high-quality, and theme-compliant code using the `flow_ui_datatable` package.
---
## Core Guidelines & Architectural Rules
1. **Strictly Material-Free**:
- The package library code (`lib/src/`) must **never** import `package:flutter/material.dart` or depend on a `Material` or `ThemeData` ancestor.
- Use `package:flutter/widgets.dart` for layouts and structural components.
- Use custom shapes and drawings (`CustomPaint`, `BoxDecoration`, `AnimatedContainer`, `GestureDetector`) rather than material buttons, check-boxes, or icons.
2. **Greyscale Clean Design System**:
- Do **not** use the Material `Colors` utility class (e.g. `Colors.grey`, `Colors.white`).
- Use hex color literals (`Color(0xFF...)`) for precise greyscale colors:
- Borders: `Color(0xFFE5E7EB)` (Light mode Gray 200), `Color(0xFF1F1F1F)` (Dark mode neutral Gray)
- Backgrounds: `Color(0xFFFFFFFF)` (Light mode pure white), `Color(0xFF000000)` (Dark mode pure black)
- Cards/Headers: `Color(0xFFFAFAFA)` (Light mode Gray 50), `Color(0xFF0D0D0D)` (Dark mode Gray 900)
3. **Dynamic Theme Mode Detection**:
- To check if the app is in dark mode without Material `Theme.of`, use `FlowTableTheme.isDark(context)`. It computes the luminance of the surrounding `DefaultTextStyle` color (light text = dark mode, dark text = light mode), falling back to `platformBrightness` when text styles are unavailable.
---
## API Reference
### `FlowDataTable<T>`
Universal, highly-performant, spreadsheet-style data table.
| Parameter | Type | Description |
| :--- | :--- | :--- |
| `columns` | `List<FlowColumn<T>>` | **Required**. List of columns to display. |
| `rows` | `List<T>` | **Required**. Data models list. |
| `theme` | `FlowTableTheme` | Theme override properties. Defaults to `FlowTableTheme.defaults`. |
| `selectable` | `bool` | Enables leading checkboxes for row selection. |
| `selectedRowIds` | `Set<String>` | The set of currently selected row IDs. |
| `onSelectionChanged` | `ValueChanged<Set<String>>?` | Triggers when rows are selected/deselected. |
| `rowIdGetter` | `String Function(T row, int index)?` | Extends unique selection keys per row. |
| `pagination` | `FlowPagination?` | Pagination configurations. |
| `clientSidePagination`| `bool` | Performs pagination slicing in memory. Defaults to `true`. |
| `sort` | `FlowTableSort?` | Active sorting state. |
| `onSortChanged` | `ValueChanged<FlowTableSort>?` | Callback when a sortable column header is tapped. |
| `clientSideSort` | `bool` | Performs sorting in-memory. Defaults to `true`. |
| `showRowIndex` | `bool` | Shows a leading numbering column (e.g., `#`). |
| `showActionsColumn` | `bool` | Shows a trailing actions column. |
| `actionsBuilder` | `Widget Function(BuildContext, T, int)?` | Builds custom action widgets. |
### `FlowColumn<T>`
Column definitions.
* **Constructor**:
```dart
FlowColumn({
required String id,
required String label,
required FlowCellBuilder<T> cellBuilder,
IconData? icon,
FlowColumnWidth width = const FlowFlexColumnWidth(1),
bool center = false,
bool sortable = false,
Comparable<dynamic> Function(T row)? sortValue,
String? tooltip,
});
FlowCells (Predefined Cell Builders) #
Helper static methods returning standard cell layouts:
- Plain Text:
FlowCells.text(context, value, {fontWeight, textAlign, maxLines, overflow}) - Avatar with Subtitle:
FlowCells.avatarWithSubtitle(context, {title, subtitle, imageUrl, avatarRadius}) - Pill Badge:
FlowCells.badge(context, label, {style, styleMap}) - Icon with Text:
FlowCells.iconWithText(context, {icon, text, iconColor}) - Dot Status with Text:
FlowCells.dotWithText(context, {text, isActive, activeColor}) - Plain/Styled Dropdown Menu:
FlowCells.dropdown(context, {value, options, onChanged, isPlain}) - Ellipsis Actions Button:
FlowCells.actionsButton({onPressed, icon})
Code Recipes #
Recipe 1: Basic In-Memory Table #
import 'package:flutter/widgets.dart';
import 'package:flow_ui_datatable/flow_ui_datatable.dart';
class SimpleUserTable extends StatelessWidget {
const SimpleUserTable({super.key});
@override
Widget build(BuildContext context) {
final users = [
{'name': 'Alice Smith', 'role': 'Admin'},
{'name': 'Bob Jones', 'role': 'Member'},
];
return FlowDataTable<Map<String, String>>(
rows: users,
columns: [
FlowColumn(
id: 'name',
label: 'Name',
cellBuilder: (context, user, _) =>
FlowCells.text(context, user['name']!),
),
FlowColumn(
id: 'role',
label: 'Role',
cellBuilder: (context, user, _) =>
FlowCells.badge(context, user['role']!),
),
],
);
}
}
Recipe 2: Interactive Table with Selection, Sorting, and Pagination #
import 'package:flutter/widgets.dart';
import 'package:flow_ui_datatable/flow_ui_datatable.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
class InteractiveUserTable extends StatefulWidget {
const InteractiveUserTable({super.key});
@override
State<InteractiveUserTable> createState() => _InteractiveUserTableState();
}
class _InteractiveUserTableState extends State<InteractiveUserTable> {
final List<User> _data = _getMockUsers();
final Set<String> _selectedIds = {};
int _page = 1;
int _pageSize = 5;
FlowTableSort? _sort;
@override
Widget build(BuildContext context) {
return FlowDataTable<User>(
rows: _data,
selectable: true,
selectedRowIds: _selectedIds,
onSelectionChanged: (ids) => setState(() {
_selectedIds.clear();
_selectedIds.addAll(ids);
}),
rowIdGetter: (user, _) => user.id,
sort: _sort,
onSortChanged: (s) => setState(() => _sort = s),
pagination: FlowPagination(
currentPage: _page,
pageSize: _pageSize,
totalItems: _data.length,
onPageChanged: (p) => setState(() => _page = p),
onPageSizeChanged: (s) => setState(() {
_pageSize = s;
_page = 1;
}),
),
columns: [
FlowColumn(
id: 'name',
label: 'Name',
icon: LucideIcons.user,
sortable: true,
sortValue: (u) => u.name,
cellBuilder: (context, user, _) =>
FlowCells.text(context, user.name, fontWeight: FontWeight.bold),
),
FlowColumn(
id: 'status',
label: 'Status',
cellBuilder: (context, user, _) =>
FlowCells.dotWithText(context, text: user.isActive ? 'Active' : 'Offline', isActive: user.isActive),
),
],
);
}
}
class User {
const User(this.id, this.name, this.isActive);
final String id;
final String name;
final bool isActive;
}
List<User> _getMockUsers() => [
User('1', 'Alice', true),
User('2', 'Bob', false),
User('3', 'Charlie', true),
];
</details>
## License
See [LICENSE](LICENSE).