grid_sheet 1.2.1
grid_sheet: ^1.2.1 copied to clipboard
A Flutter DataGrid/DataTable with minimal configuration and powerful features like filtering, formulas, pagination, editing, frozen columns, CRUD, and full customization.
example/lib/main.dart
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:grid_sheet/grid_sheet.dart';
void main() {
runApp(GridSheetApp());
}
class GridSheetApp extends StatelessWidget {
const GridSheetApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Grid Sheet Package',
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.light,
),
),
darkTheme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.dark,
),
),
themeMode: ThemeMode.system,
home: Scaffold(
body: GridSheetExample(),
),
);
}
}
class GridSheetExample extends StatefulWidget {
const GridSheetExample({super.key});
@override
State<GridSheetExample> createState() => _GridSheetExampleState();
}
class _GridSheetExampleState extends State<GridSheetExample> {
late final GridSheetManager gridManager;
late List<String> _columns;
late List<List<dynamic>> _rows;
late Map<String, double> _columnWidths;
late Map<String, bool> _columnVisibility;
late Map<String, String> _columnActualNames;
late Map<String, GridSheetColumnType> _columnTypes;
@override
void initState() {
super.initState();
_mockData();
}
@override
void dispose() {
super.dispose();
}
List<GridSheetColumn> get columns => _getColumns();
List<GridSheetRow> get rows => _getRows();
@override
Widget build(BuildContext context) {
// Build style configuration based on current theme
final styleConfiguration = _buildStyleConfiguration(context);
return GridSheet(
columns: columns,
rows: rows,
configuration: GridSheetLayoutConfiguration(
enableMultiSelection: true,
enableCellSelection: true,
enableColumnSelection: true,
enableRowSelectionOnFirstColumnTap: true,
enableReorder: true,
rowsPerPage: 15,
),
styleConfiguration: styleConfiguration,
conditionalFormatRules: getFormattingRules(),
onLoaded: (event) {
gridManager = event.gridManager;
log('Table initialized!', name: 'GridSheetTableDemo');
},
onColumnsSelected: (event) {
event.selectedColumnsData.forEach((columnName, values) {
log(
'$columnName: ${values.length} values',
name: 'GridSheetTableDemo',
);
});
},
onRowsSelected: (event) {
for (final rowMap in event.selectedRowsData) {
log('Row data: $rowMap', name: 'GridSheetTableDemo');
}
},
onCellsSelected: (event) {
event.selectedCellsData.forEach((columnName, values) {
log('$columnName: $values', name: 'GridSheetTableDemo');
});
},
onCellValueChange: (event) {
log(
'${event.columnName}: ${event.oldValue} → ${event.newValue}',
name: 'GridSheetTableDemo',
);
},
);
}
_mockData() {
_columns = [
'COUNTRY_NAME',
'REGION',
'CATEGORY_TYPE',
'AVAILABLE',
'YEAR',
'SALES',
'FORMULA_COL',
'EMPTY_COL'
];
_columnActualNames = {
for (var col in _columns) col: col.replaceAll(' ', '').toUpperCase(),
};
_columnVisibility = {
for (int i = 0; i < _columns.length; i++) _columns[i]: true,
};
_columnTypes = {
'COUNTRY_NAME': GridSheetColumnType.text,
'REGION': GridSheetColumnType.text,
'CATEGORY_TYPE': GridSheetColumnType.text,
'AVAILABLE': GridSheetColumnType.boolean,
'YEAR': GridSheetColumnType.double,
'SALES': GridSheetColumnType.double,
'FORMULA_COL': GridSheetColumnType.formula,
'EMPTY_COL': GridSheetColumnType.integer,
};
_columnWidths = {
'COUNTRY_NAME': 150,
'REGION': 150,
'CATEGORY_TYPE': 150,
'AVAILABLE': 100,
'YEAR': 100,
'SALES': 100,
'FORMULA_COL': 200,
'EMPTY_COL': 100,
};
_rows = [
['Canada', 'North', 'Furniture', true, 2024, 700.0, '=F1 * 2', null],
['Canada', 'North', 'Office', true, 2023, 500.0, '=SUM(E1:F3)', null],
['India', 'East', 'Furniture', false, 2025, 1200.0, '=F3 + E4', null],
['India', 'East', 'Office', true, 2021, 800.0, null, null],
[
'US',
'South',
'Office',
true,
2022,
400.0,
'=F * 2',
null
], //'=F * 2' (current row f-cell * 2)
['US', 'South', 'Furniture', true, 2025, 200.0, '=SALES4 + YEAR4', null],
];
}
GridSheetStyleConfiguration _buildStyleConfiguration(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final isDark = Theme.of(context).brightness == Brightness.dark;
return GridSheetStyleConfiguration(
gridBackgroundColor: colorScheme.surface,
headerColor: colorScheme.surfaceContainerHighest,
filterColor: colorScheme.surfaceContainerHighest,
rowColor: colorScheme.surface,
evenRowColor: isDark
? colorScheme.surfaceContainerLow
: colorScheme.surfaceContainerLowest,
oddRowColor: colorScheme.surface,
selectionColor: colorScheme.primary,
gridBorderColor: colorScheme.outlineVariant,
rowBorderColor: colorScheme.outlineVariant,
columnBorderColor: colorScheme.outlineVariant,
);
}
List<GridSheetColumn> _getColumns({int frozenColumnCount = 0}) {
return _columns.asMap().entries.map((e) {
final column = e.value;
final index = e.key;
final key = '${GridSheetConstants.columnCopyKeyStartsWith}$index';
// Read from configurations
bool isEditable = true;
bool noEditMode = false;
final actualName = _columnActualNames[column] ?? column;
final display = _columnVisibility[column] ?? false;
final maxWidth = _columnWidths[column] ?? 120.0;
final type = _columnTypes[column] ?? GridSheetColumnType.text;
String? editableExpression = '';
if (actualName == 'CATEGORY_TYPE') {
editableExpression = "COUNTRY_NAME == 'Canada'";
}
TextAlign alignment = TextAlign.left;
if (type == GridSheetColumnType.integer ||
type == GridSheetColumnType.double) {
alignment = TextAlign.right;
} else if (type == GridSheetColumnType.datetime) {
noEditMode = true;
}
return GridSheetColumn(
key: ValueKey<String>(key),
title: column,
name: actualName,
type: type,
width: maxWidth,
visible: display,
textAlign: alignment,
frozen: index < frozenColumnCount,
editable: isEditable,
conditionalEditExpression: editableExpression,
index: index,
resize: true,
sortable: true,
noTextControllerWidget: noEditMode,
);
}).toList();
}
List<GridSheetRow> _getRows({double rowHeight = 30.0}) {
return _rows.asMap().entries.map((e) {
final index = e.key;
final rowData = e.value;
final key = '${GridSheetConstants.rowKeyStartsWith}$index';
final row = GridSheetRow(
key: ValueKey<String>(key),
index: index,
data: rowData,
height: rowHeight,
);
return row;
}).toList();
}
List<GridSheetConditionalFormatRule> getFormattingRules() {
return [
GridSheetConditionalFormatRule(
name: 'COUNTRY_NAME',
expression: "YEAR > 2024",
scope: GridSheetFormatScope.row,
backgroundColor: Colors.red,
),
GridSheetConditionalFormatRule(
name: 'REGION',
expression: "CATEGORY_TYPE == 'Furniture'",
scope: GridSheetFormatScope.column,
backgroundColor: Colors.green,
),
GridSheetConditionalFormatRule(
name: 'CATEGORY_TYPE',
expression: "SALES > 700",
scope: GridSheetFormatScope.cell,
backgroundColor: Colors.orange,
),
];
}
}