worksheet 1.3.0
worksheet: ^1.3.0 copied to clipboard
High-performance Flutter spreadsheet widget supporting large datasets, 10%-400% zoom, and GPU-optimized tile-based rendering.
Worksheet Widget #
A Flutter widget that brings Excel-like spreadsheet functionality to your app.
[Worksheet Screenshot]
Display and edit tabular data with smooth scrolling, pinch-to-zoom, and cell selection - all running at 60fps even with hundreds of thousands of rows.
Try It In 30 Seconds #
import 'package:flutter/material.dart';
import 'package:worksheet/worksheet.dart';
void main() => runApp(MaterialApp(home: MySpreadsheet()));
class MySpreadsheet extends StatelessWidget {
@override
Widget build(BuildContext context) {
final data = SparseWorksheetData(rowCount: 100, columnCount: 10, cells: {
(0, 0): 'Name'.cell,
(0, 1): 'Amount'.cell,
(1, 0): 'Apples'.cell,
(1, 1): 42.cell,
(2, 1): '=2+42'.formula,
(3, 1): Cell.text('test'),
});
return Scaffold(
body: WorksheetTheme(
data: const WorksheetThemeData(),
child: Worksheet(
data: data,
rowCount: 100,
columnCount: 10,
),
),
);
}
}
That's it! You now have a scrollable, zoomable spreadsheet with row/column headers.
Add Selection and Editing #
Want users to select and edit cells? Add a controller and callbacks:
class EditableSpreadsheet extends StatefulWidget {
@override
State<EditableSpreadsheet> createState() => _EditableSpreadsheetState();
}
class _EditableSpreadsheetState extends State<EditableSpreadsheet> {
final _data = SparseWorksheetData(rowCount: 1000, columnCount: 26);
final _controller = WorksheetController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: WorksheetTheme(
data: const WorksheetThemeData(),
child: Worksheet(
data: _data,
controller: _controller,
rowCount: 1000,
columnCount: 26,
onCellTap: (cell) {
print('Tapped ${cell.toNotation()}'); // "A1", "B5", etc.
},
onEditCell: (cell) {
// Double-tap triggers edit - implement your editor UI
print('Edit ${cell.toNotation()}');
},
),
),
);
}
@override
void dispose() {
_controller.dispose();
_data.dispose();
super.dispose();
}
}
Now you can:
- Click cells to select them
- Use arrow keys to navigate
- Track selection via
_controller.selectedRange - Zoom with pinch gestures or
_controller.setZoom(1.5)
Format Your Numbers #
Display values as currency, percentages, dates, and more using Excel-style format codes:
final data = SparseWorksheetData(rowCount: 100, columnCount: 10, cells: {
(0, 0): 'Revenue'.cell,
(0, 1): Cell.number(1234.56, format: CellFormat.currency), // "$1,234.56"
(1, 0): 'Growth'.cell,
(1, 1): Cell.number(0.085, format: CellFormat.percentage), // "9%"
(2, 0): 'Date'.cell,
(2, 1): Cell.date(DateTime(2024, 1, 15), format: CellFormat.dateIso), // "2024-01-15"
(3, 0): 'Precision'.cell,
(3, 1): Cell.number(3.14159, format: CellFormat.scientific), // "3.14E+00"
});
16 built-in presets cover common formats. For custom codes, create your own:
const custom = CellFormat(type: CellFormatType.number, formatCode: '#,##0.000');
Style Your Data #
Add colors, bold text, and conditional formatting:
// Header row styling
const headerStyle = CellStyle(
backgroundColor: Color(0xFF4472C4),
textColor: Color(0xFFFFFFFF),
fontWeight: FontWeight.bold,
textAlignment: CellTextAlignment.center,
);
// Apply to cells
_data.setStyle(const CellCoordinate(0, 0), headerStyle);
_data.setStyle(const CellCoordinate(0, 1), headerStyle);
// Highlight negative numbers in red
final value = _data.getCell(CellCoordinate(row, col));
if (value != null && value.isNumber && value.asDouble < 0) {
_data.setStyle(
CellCoordinate(row, col),
const CellStyle(textColor: Color(0xFFCC0000)),
);
}
Handle Large Datasets #
The widget uses sparse storage and tile-based rendering, so this works smoothly:
// Excel-sized grid: 1 million rows, 16K columns
final data = SparseWorksheetData(
rowCount: 1048576,
columnCount: 16384,
);
// Only populated cells use memory
for (var row = 0; row < 50000; row++) {
data[(row, 0)] = Cell.text('Row ${row + 1}');
}
// Memory usage: ~50K cells, not 17 billion empty cells
Why This Widget? #
Built for Performance #
- Tile-based rendering: Only visible cells are drawn, cached as GPU textures
- 60fps scrolling: Smooth even with 100K+ populated cells
- 10%-400% zoom: Pinch to zoom with automatic level-of-detail
- O(log n) lookups: Binary search for row/column positions
Built for Real Apps #
- Sparse storage: Memory scales with data, not grid size
- Full selection: Single cell, ranges, entire rows/columns
- Keyboard navigation: Arrow keys, Tab, Enter, Home/End
- Resize support: Drag column/row borders to resize
- Theming: Full control over colors, fonts, headers
Built with Quality #
- SOLID principles: Clean separation of concerns
- Test coverage: 87%+ with unit, widget, and performance tests
- TDD workflow: Tests written before implementation
Documentation #
| Guide | Description |
|---|---|
| Getting Started | Installation, basic setup, enabling editing |
| Cookbook | Practical recipes for common tasks |
| Performance | Tile cache tuning, large dataset strategies |
| Theming | Colors, fonts, headers, selection styles |
| Testing | Unit tests, widget tests, benchmarks |
| API Reference | Quick reference for all classes and methods |
| Architecture | Deep dive into the rendering pipeline |
Installation #
Add to your pubspec.yaml:
dependencies:
worksheet: ^1.0.0
Then run:
flutter pub get
Keyboard Shortcuts #
| Key | Action |
|---|---|
| Arrow keys | Move selection |
| Shift + Arrow | Extend selection |
| Tab / Shift+Tab | Move right/left |
| Enter / Shift+Enter | Move down/up |
| Home / End | Start/end of row |
| Ctrl+Home | Go to A1 |
| F2 | Edit current cell |
| Delete | Clear cell |
Quick API Overview #
// Data - map literal construction with record coordinates
final data = SparseWorksheetData(
rowCount: 1000,
columnCount: 26,
cells: {
(0, 0): 'Hello'.cell,
(0, 1): 42.cell,
},
);
// Bracket access with (row, col) records
data[(1, 0)] = 'World'.cell;
data[(1, 1)] = Cell.number(99, style: const CellStyle(fontWeight: FontWeight.bold));
final cell = data[(0, 0)]; // Cell(value: 'Hello', style: null)
// Extensions for quick cell creation
'Hello'.cell // Cell with text value
42.cell // Cell with numeric value
true.cell // Cell with boolean value
DateTime.now().cell // Cell with date value
'=SUM(A1:A10)'.formula // Cell with formula
// Cell constructors for full control (when you need style or format)
Cell.text('Hello', style: headerStyle)
Cell.number(42.5, format: CellFormat.currency)
Cell.boolean(true)
Cell.date(DateTime.now(), format: CellFormat.dateIso)
Cell.withStyle(headerStyle) // style only, no value
// Controller
final controller = WorksheetController();
controller.selectCell(const CellCoordinate(5, 3));
controller.selectRange(CellRange(0, 0, 10, 5));
controller.setZoom(1.5); // 150%
controller.scrollTo(x: 500, y: 1000, animate: true);
Running the Example #
cd example
flutter run
The example app demonstrates:
- 50,000 rows of sample sales data
- Cell editing with double-tap
- Column/row resizing
- Zoom slider (10%-400%)
- Keyboard navigation
Running Tests #
flutter test # Run all tests
flutter test --coverage # With coverage report
flutter test test/core/ # Run specific directory
License #
MIT License - see LICENSE for details.