nhance_partner/lib/presentation/themes/indicators/export_btn.dart
2026-04-03 15:04:34 +05:30

311 lines
10 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:excel/excel.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:nhance_partner/data/utils/toastNotification.dart';
import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:universal_html/html.dart' as html;
import 'dart:typed_data';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:io' show File; // Only used in mobile/desktop
import 'package:path_provider/path_provider.dart';
import 'package:open_file/open_file.dart';
/// Optional per-field Excel cell formatter. Use when raw map values differ from
/// on-screen display (dates, status labels, etc.). Key must match [keys] entries.
typedef ExportValueFormatter = String Function(
dynamic value,
Map<String, dynamic> row,
);
class ExportBtn extends HookWidget {
final String sheetName;
final String fileName;
final List<Map<String, dynamic>> data;
final List<String>? displayHeaders; // dynamic headers
// final List<String> headers; // dynamic headers
final List<String>? keys; // dynamic headers
final bool? txt;
/// When set, these keys are exported as the returned string (not raw int/double).
final Map<String, ExportValueFormatter>? valueFormatters;
const ExportBtn({
super.key,
this.txt,
required this.sheetName,
required this.fileName,
required this.data,
// required this.headers,
required this.displayHeaders,
this.keys,
this.valueFormatters,
});
@override
Widget build(BuildContext context) {
return Tooltip(
message: 'Export File',
child: InkWell(
onTap: () async {
print('TAV ExcelExporter');
final scaffold = ScaffoldMessenger.of(context);
try {
await ExcelExporter.exportToExcel(
sheetName: sheetName,
data: data,
displayHeaders: displayHeaders!,
keys: keys!,
fileName: fileName,
valueFormatters: valueFormatters,
);
ToastHelper.showSuccessToast(context, 'Export finished');
} catch (e) {
ToastHelper.showErrorToast(context, 'Export failed: $e');
}
},
// onTap: () {
// print('TAV ExcelExporter');
//
// ExcelExporter.exportToExcel(
// sheetName: sheetName,
// data: data,
// headers: headers,
// fileName: fileName,
// keys: keys!,
// );
// },
child: Container(
padding: const EdgeInsets.all(7.0),
decoration: BoxDecoration(
color: const Color(0xFF2E7D6E),
// color: const Color(0xFF425B5B),
borderRadius: BorderRadius.circular(8.0),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// ?txt!
// ? Text(
// 'Export',
// style: TextStyle(
// color: Colors.white,
// fontWeight: FontWeight.w600,
// fontSize: 14,
// ),
// )
// : null,
// ?txt! ? SizedBox(width: 15) : null,
Image.asset(
"assets/miscellaneous/export.png",
height: 13,
width: 13,
),
// Icon(Icons.input_sharp, color: Colors.white),
// Image.asset("assets/miscellaneous/export", height: 15, width: 15),
],
),
),
),
);
}
}
class ExcelExporter {
/// Export to an Excel file.
/// - [displayHeaders]: labels to show in Excel (e.g. "S.No.", "Status")
/// - [keys]: actual field names in your data map
static Future<void> exportToExcel({
required String sheetName,
required List<Map<String, dynamic>> data,
required List<String> displayHeaders,
required List<String> keys,
required String fileName,
Map<String, ExportValueFormatter>? valueFormatters,
}) async {
if (displayHeaders.length != keys.length) {
throw ArgumentError('displayHeaders and keys must have same length');
}
try {
final excel = Excel.createExcel();
// Rename default sheet if needed
if (excel.sheets.containsKey('Sheet1')) {
try {
excel.rename('Sheet1', sheetName);
} catch (_) {}
}
// Get the sheet
final sheet = excel[sheetName]!;
// ✅ Add headers as CellValues
sheet.appendRow(displayHeaders.map((h) => TextCellValue(h)).toList());
// ✅ Add data rows
// for (var i = 0; i < data.length; i++) {
// final rowMap = data[i];
// final row = <CellValue?>[];
//
// for (var key in keys) {
// dynamic value;
// if (key == 'sno' ||
// key.toLowerCase() == 's.no' ||
// key.toLowerCase() == 'sno.') {
// value = i + 1; // serial number
// } else {
// value = rowMap[key];
// }
//
// if (value == null) {
// row.add(TextCellValue('-'));
// } else if (value is int) {
// row.add(IntCellValue(value));
// } else if (value is double) {
// row.add(DoubleCellValue(value));
// } else {
// row.add(TextCellValue(value.toString()));
// }
// }
//
// sheet.appendRow(row);
// }
// ✅ Reverse the data list so latest entries come first
final reversedData = data.reversed.toList();
final formatters = valueFormatters;
for (var i = 0; i < reversedData.length; i++) {
final rowMap = reversedData[i];
final row = <CellValue?>[];
// Inside ExcelExporter class -> exportToExcel method
for (var key in keys) {
dynamic value;
if (key == 'sno' || key.toLowerCase() == 's.no' || key.toLowerCase() == 'sno.') {
value = i + 1;
} else if (formatters != null && formatters.containsKey(key)) {
final formatted = formatters[key]!(rowMap[key], rowMap);
final text = formatted.trim().isEmpty ? '-' : formatted;
row.add(TextCellValue(text));
continue;
} else if (key.toLowerCase() == 'is_active') {
final rawVal = rowMap[key];
value = (rawVal == 1 || rawVal == '1') ? 'Active' : 'Inactive';
} else {
// FIX: Ensure we extract the value and check if it's "null" as a string
value = rowMap[key];
if (value == "null") value = null;
}
if (value == null || value.toString().trim().isEmpty) {
row.add(TextCellValue('-')); // This ensures the column isn't empty
} else if (value is int) {
row.add(IntCellValue(value));
} else if (value is double) {
row.add(DoubleCellValue(value));
} else {
row.add(TextCellValue(value.toString()));
}
}
sheet.appendRow(row);
}
final excelBytes = excel.encode();
if (excelBytes == null) throw Exception('Failed to encode Excel file');
if (kIsWeb) {
// Web download
final base64Str = base64Encode(excelBytes);
final dataUri = 'data:application/octet-stream;base64,$base64Str';
final anchor = html.AnchorElement(href: dataUri)
..setAttribute('download', '$fileName.xlsx')
..click();
} else {
// Mobile/Desktop save + open
final dir = await getApplicationDocumentsDirectory();
final filePath = '${dir.path}/$fileName.xlsx';
final file = File(filePath);
await file.writeAsBytes(excelBytes, flush: true);
await OpenFile.open(file.path);
}
} catch (e, st) {
debugPrint('❌ Excel export error: $e\n$st');
rethrow;
}
}
}
// class ExcelExporter {
// static Future<void> exportToExcel({
// required String sheetName,
// required List<Map<String, dynamic>> data,
// required List<String> headers,
// required String fileName,
// required List<String> keys,
// }) async {
// try {
// // Create Excel
// var excel = Excel.createExcel();
//
// // Rename default sheet
// if (excel.sheets.containsKey('Sheet1')) {
// excel.rename('Sheet1', sheetName);
// }
//
// // Get the sheet
// Sheet sheetObject = excel[sheetName]!;
//
// // Add headers
// sheetObject.appendRow(keys.map((h) => TextCellValue(h)).toList());
//
// // Add data
// for (var rowData in data) {
// var row = <CellValue?>[];
// for (var key in keys) {
// final value = rowData[key];
// if (value == null) {
// row.add(TextCellValue('-'));
// } else if (value is int) {
// row.add(IntCellValue(value));
// } else if (value is double) {
// row.add(DoubleCellValue(value));
// } else {
// row.add(TextCellValue(value.toString()));
// }
// }
// sheetObject.appendRow(row);
// }
//
// var bytes = excel.encode()!;
//
// if (kIsWeb) {
// // ✅ Web: base64 download instead of Blob
// final base64Str = base64Encode(bytes);
// final dataUri = "data:application/octet-stream;base64,$base64Str";
// final anchor = html.AnchorElement(href: dataUri)
// ..setAttribute("download", "$fileName.xlsx")
// ..click();
// } else {
// // Mobile/Desktop: save file
// final dir = await getApplicationDocumentsDirectory();
// final file = File('${dir.path}/$fileName.xlsx');
// await file.writeAsBytes(bytes);
// await OpenFile.open(file.path);
// }
// } catch (e) {
// print("Error exporting Excel: $e");
// }
// }
// }