grid_sheet 1.1.9
grid_sheet: ^1.1.9 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:math' hide log;
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:grid_sheet/grid_sheet.dart';
import 'package:grid_sheet_example/toolbar_testing.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;
final _random = Random();
@override
void initState() {
super.initState();
_initData();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
// Build style configuration based on current theme
final styleConfiguration = _buildStyleConfiguration(context);
return GridSheet(
columns: _getColumns(),
rows: _getRows(),
configuration: GridSheetLayoutConfiguration(
enableMultiSelection: true,
enableCellSelection: true,
enableColumnSelection: true,
enableRowSelectionOnFirstColumnTap: true,
enableReorder: true,
rowsPerPage: 25,
),
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',
);
},
);
}
_initData() {
int colsCount = 20;
int rowsCount = 60;
columns = List.generate(colsCount, (i) {
if (i % 5 == 0) {
return 'Date ${i + 1}';
} else if (i % 3 == 0) {
return 'Bool ${i + 1}';
} else if (i % 2 == 0) {
return 'Numeric ${i + 1}';
} else {
return 'String ${i + 1}';
}
});
columnActualNames = {
for (var col in columns) col: col.replaceAll(' ', '').toUpperCase(),
};
columnVisibility = {
for (int i = 0; i < colsCount; i++) columns[i]: i % 4 != 0,
};
columnTypes = {
for (var col in columns)
col: col.startsWith('Date')
? GridSheetColumnType.datetime
: col.startsWith('Bool')
? GridSheetColumnType.boolean
: col.startsWith('Numeric')
? GridSheetColumnType.double
: GridSheetColumnType.text,
};
columnWidths = {
for (var col in columns)
col: () {
final type = columnTypes[col];
switch (type) {
case GridSheetColumnType.boolean:
return 80.0;
case GridSheetColumnType.double:
return 100.0;
default:
return 200.0;
}
}(),
};
rows = List.generate(rowsCount, (rowIndex) {
return List.generate(colsCount, (colIndex) {
final columnName = columns[colIndex];
final type = columnTypes[columnName];
switch (type) {
case GridSheetColumnType.text:
return 'Data_${rowIndex}_$colIndex';
case GridSheetColumnType.double:
// Generate a random double between 0 and 100
return double.tryParse(
(_random.nextDouble() * 100).toStringAsFixed(2),
);
case GridSheetColumnType.boolean:
return _random.nextBool();
case GridSheetColumnType.datetime:
// Generate a random date in the last 10 years
final start = DateTime.now().subtract(Duration(days: 3650));
final end = DateTime.now();
final randomDate = start.add(
Duration(
days: _random.nextInt(end.difference(start).inDays),
hours: _random.nextInt(24),
minutes: _random.nextInt(60),
seconds: _random.nextInt(60),
),
);
return randomDate;
default:
return 'N/A';
}
});
});
}
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 == 'BOOL7') {
editableExpression = "NUMERIC3 < 90.5";
}
TextAlign alignment = TextAlign.left;
if (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: 'STRING2',
expression: "NUMERIC3 > 90",
scope: GridSheetFormatScope.row,
backgroundColor: Colors.red,
),
GridSheetConditionalFormatRule(
name: 'BOOL4',
expression: "BOOL7 == false",
scope: GridSheetFormatScope.column,
backgroundColor: Colors.green,
),
GridSheetConditionalFormatRule(
name: 'NUMERIC3',
expression: "BOOL4 == true",
scope: GridSheetFormatScope.cell,
backgroundColor: Colors.orange,
),
];
}
Widget get loadingWidget {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text(
'Processing...',
style: TextStyle(color: Colors.white, fontSize: 16),
),
],
),
);
}
}