enrollment-app/lib/presentation/cdTransactionDetails.dart

1078 lines
32 KiB
Dart
Executable File

import 'dart:convert';
import 'package:csv/csv.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'package:nhancepolicy/customAppBar/toastHelper.dart';
import 'package:nhancepolicy/service/api_service.dart';
import 'package:nhancepolicy/service/token_storage_service.dart';
import 'package:universal_html/html.dart' as html;
import 'package:collection/collection.dart';
import 'package:url_launcher/url_launcher.dart';
import '../customAppBar/base_layout.dart';
import '../customAppBar/customAppBar.dart';
import '../customAppBar/customFooter.dart';
class cdTransactionDetails extends StatefulWidget {
final String insurerName;
final String cdMasterAccountNo;
final String insurerId;
final String cd_ac_pk;
final String empClientId;
// final String postToken;
const cdTransactionDetails(
{Key? key,
required this.insurerName,
required this.cdMasterAccountNo,
required this.insurerId,
required this.cd_ac_pk,
required this.empClientId,
// required this.postToken
});
@override
State<cdTransactionDetails> createState() => _cdTransactionDetailsState();
}
class _cdTransactionDetailsState extends State<cdTransactionDetails> {
final tokenService = TokenStorageService();
Uint8List? fileBytes;
List<Map<String, dynamic>> getCDTransData = [];
List<Map<String, dynamic>> getCDEndorsementData = [];
bool isLoading = false;
bool _isLoading = false;
// dynamic clintID;
late TabController _tabController;
// List<dynamic> dataPolicy = [];
List<dynamic> reversedDataPolicy = [];
List<Map<String, dynamic>> originalData = []; // Original data source
List<Map<String, dynamic>> originalEndorsementData = []; // Original data source
List<Map<String, dynamic>> filteredData = []; // Filtered data source
List<Map<String, dynamic>> filteredEndorsementData = []; // Filtered data source
List<Map<String, dynamic>> getCDTransDataAmount = []; // Filtered data source
dynamic argumentsData;
dynamic policyType;
dynamic policyName;
dynamic clientPolicyId;
dynamic clientId;
dynamic empRefId;
dynamic total_deposit;
dynamic total_consumed;
dynamic total_refund;
dynamic currect_balance;
dynamic insurer_short_name;
int inceptionType = 0;
TextEditingController searchController = TextEditingController();
late ApiService apiService;
int _currentPage = 1;
int _rowsPerPage = 5;
List<dynamic> get _paginatedData {
final startIndex = (_currentPage - 1) * _rowsPerPage;
final endIndex =
(_currentPage * _rowsPerPage).clamp(0, filteredData.length);
return filteredData.sublist(startIndex, endIndex);
}
@override
void initState() {
super.initState();
apiService = ApiService(context); // Initialize ApiService here
getCdTransactionDetails();
}
@override
void dispose() {
super.dispose();
}
// downloadPolicyFiles?file_id=13
// getPolicyAndEndorsementFiles?cd_ac_pk=12
Future<void> getCdTransactionDetails() async {
print('9');
setState(() {
isLoading = true;
});
try {
print('10');
final _postPreToken = await tokenService.getCurrentToken();
final response = await apiService.getCdTransactionData(widget.empClientId,
widget.insurerId, widget.cd_ac_pk, _postPreToken!);
if (response['status'] == 'success') {
setState(() {
isLoading = false;
});
setState(() {
getCDTransData =
List<Map<String, dynamic>>.from(response['data']['deposit_data']);
originalData = getCDTransData;
filteredData = List.from(originalData);
total_deposit = formatAmount(response['data']['total_deposit']);
total_consumed = formatAmount(response['data']['total_consumed']);
total_refund = formatAmount(response['data']['total_refund']);
currect_balance = formatAmount(response['data']['currect_balance']);
insurer_short_name = formatAmount(response['data']['insurer_short_name']);
print('filteredData');
print(filteredData);
});
} else {
setState(() {
isLoading = false;
});
// ToastHelper.showWarningToast(
// context, 'Request failed with status: ${response.statusCode}');
print('Request failed with status: ${response['code']}');
}
} catch (e) {
setState(() {
isLoading = false;
});
print('Exception occurred: $e');
} finally {
setState(() {
_isLoading = false;
});
}
}
Future<void> _openEndorsementFile(id) async {
print('9');
try {
print('10');
final _postPreToken = await tokenService.getCurrentToken();
final response = await apiService.getOpenEndorsementFileData(id, _postPreToken!);
if (response['status'] == false) {
ToastHelper.showErrorToast(context, response['message']);
} else {
// ToastHelper.showWarningToast(
// context, 'Request failed with status: ${response.statusCode}');
print('Request failed with status: ${response['code']}');
}
} catch (e) {
print('Exception occurred: $e');
}
}
Future<void> getCdEndorsementDetails(id) async {
print('9');
setState(() {
isLoading = true;
});
try {
print('10');
final _postPreToken = await tokenService.getCurrentToken();
final response = await apiService.getCdEndorsementData(id, _postPreToken!);
if (response['status'] == true) {
setState(() {
isLoading = false;
});
setState(() {
getCDEndorsementData = List<Map<String, dynamic>>.from(response['data']);
originalEndorsementData = getCDEndorsementData;
filteredEndorsementData = List.from(originalEndorsementData);
_showFileListPopup(filteredEndorsementData);
print('filteredData');
print(filteredData);
});
} else {
setState(() {
isLoading = false;
});
getCDEndorsementData = List<Map<String, dynamic>>.from(response['data']);
if (getCDEndorsementData == null || getCDEndorsementData.isEmpty || getCDEndorsementData == null || (getCDEndorsementData as List).isEmpty) {
_showEmptyPopup(response['message']);
return;
}
// ToastHelper.showWarningToast(
// context, 'Request failed with status: ${response.statusCode}');
print('Request failed with status: ${response['code']}');
}
} catch (e) {
setState(() {
isLoading = false;
});
print('Exception occurred: $e');
} finally {
setState(() {
_isLoading = false;
});
}
}
void _showFileListPopup(List<Map<String, dynamic>> files) {
showDialog(
context: context,
barrierDismissible: true,
builder: (_) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
title: const Text(
'Files',
style: TextStyle(fontWeight: FontWeight.w600),
),
content: SizedBox(
width: 400,
child: ListView.separated(
shrinkWrap: true,
itemCount: files.length,
separatorBuilder: (_, __) => const Divider(),
itemBuilder: (context, index) {
final file = files[index];
return ListTile(
leading: const Icon(
Icons.picture_as_pdf_outlined,
color: Color(0xFF00999E),
),
title: Text(
file['file_name'] ?? 'Document',
style: const TextStyle(fontSize: 14),
),
onTap: () {
// Navigator.pop(context); // close popup
_openEndorsementFile(file['id']);
},
);
},
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Close'),
),
],
);
},
);
}
void _showEmptyPopup(String message) {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
title: const Text(
'Message',
style: TextStyle(fontWeight: FontWeight.w600),
),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'),
),
],
);
},
);
}
void search(String query) {
print(query);
// Check if the query is empty
if (query.isEmpty) {
// If search query is empty, show all data
setState(() {
filteredData = List.from(originalData);
});
} else {
// Filter the original data based on the search query
setState(() {
filteredData = originalData.where((row) {
// Implement your filter logic here
// For example, check if any field in the row contains the query
// Adjust this logic based on your data structure
return row['created_at']
.toString()
.toLowerCase()
.contains(query.toLowerCase()) ||
row['record_date']
.toString()
.toLowerCase()
.contains(query.toLowerCase()) ||
row['unit']
.toString()
.toLowerCase()
.contains(query.toLowerCase()) ||
row['policy_type']
.toString()
.toLowerCase()
.contains(query.toLowerCase()) ||
row['policy_no']
.toString()
.toLowerCase()
.contains(query.toLowerCase()) ||
row['endorsement_no']
.toString()
.toLowerCase()
.contains(query.toLowerCase()) ||
row['sub_type']
.toString()
.toLowerCase()
.contains(query.toLowerCase()) ||
row['transaction_type']
.toString()
.toLowerCase()
.contains(query.toLowerCase()) ||
row['amount']
.toString()
.toLowerCase()
.contains(query.toLowerCase()) ||
row['balance']
.toString()
.toLowerCase()
.contains(query.toLowerCase()) ||
row['description']
.toString()
.toLowerCase()
.contains(query.toLowerCase()) ||
row['username']
.toString()
.toLowerCase()
.contains(query.toLowerCase());
}).toList();
});
}
print(filteredData.length);
}
void exportToCsv(List<Map<String, dynamic>> data) {
List<List<String>> rows = [];
// Header
rows.add([
'Date',
'Record Date',
'Unit',
'Policy',
'Endorsement No',
'Sub Type',
'Credit',
'Debit',
'Balance',
'Description',
'User'
]);
// Data rows
for (var item in data) {
rows.add([
item['created_at'] ?? '',
item['record_date'] ?? '',
item['unit'] ?? '',
item['policy_type'] != null &&
item['policy_type'].toString().trim().isNotEmpty &&
item['policy_no'] != null &&
item['policy_no'].toString().trim().isNotEmpty
? '${item['policy_type']} - ${item['policy_no']}'
: '-',
item['endorsement_no'] ?? '',
item['sub_type_text'] ?? '',
item['transaction_type'] == 'Credit' ? '${item['amount']}' : '-',
item['transaction_type'] == 'Debit' ? '${item['amount']}' : '-',
'${item['balance'] ?? '0'}',
item['description'] ?? '',
item['username'] ?? '',
]);
}
// Convert to CSV string
String csvData = const ListToCsvConverter().convert(rows);
// For Web: Create download
final bytes = utf8.encode(csvData);
final blob = html.Blob([bytes]);
final url = html.Url.createObjectUrlFromBlob(blob);
final anchor = html.AnchorElement(href: url)
..setAttribute("download", "CD_Transaction_Policies.csv")
..click();
html.Url.revokeObjectUrl(url);
handleExportAction();
}
Future<void> handleExportAction() async {
print('handleExportAction');
final postId = await tokenService.readValue('empHrId');
final preId = await tokenService.readValue('enrollmentEmpPrimaryId');
var activity = "export_cdsummary";
print('postId - $postId');
print('preId - $preId');
print('activity - $activity');
try {
print('10');
final _postPreToken = await tokenService.getCurrentToken();
final response = await apiService.getPostLogHrActivity(
postId!, preId!, _postPreToken!, activity);
if (response['status'] == 'success') {
print('Request success');
} else {
// ToastHelper.showWarningToast(
// context, 'Request failed with status: ${response.statusCode}');
print('Request failed with status: ${response['code']}');
}
} catch (e) {
print('Exception occurred: $e');
}
}
String formatDate(String? dateString) {
if (dateString == null || dateString.isEmpty) return '-';
try {
DateTime parsedDate = DateTime.parse(dateString);
return DateFormat('dd-MM-yyyy').format(parsedDate);
} catch (e) {
return '-';
}
}
String formatDateNextLine(String? dateString) {
if (dateString == null || dateString.isEmpty) return '-';
try {
DateTime parsedDate = DateTime.parse(dateString);
String date = DateFormat('dd-MM-yyyy').format(parsedDate);
String time = DateFormat('hh:mm a').format(parsedDate);
return '$date\n$time';
} catch (e) {
return '-';
}
}
Future<void> _launchURL(String url) async {
final Uri uri = Uri.parse(url); // Parse the URL properly
print('_launchURL $uri');
if (uri != null) {
print('If $uri');
await launchUrl(uri, mode: LaunchMode.externalApplication);
} else {
ToastHelper.showWarningToast(context, 'File not generated');
print('else $uri');
throw 'Could not launch $url';
}
}
Future<bool> _showLogoutDialog() async {
return await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text("Confirm Logout"),
content: Text("Do you want to logout?"),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text("Cancel"),
),
TextButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text("Logout"),
),
],
),
) ??
false;
}
String formatAmount(dynamic value) {
if (value == null) return '-';
final num amount = num.tryParse(value.toString()) ?? 0;
return amount.round().toString();
}
@override
Widget build(BuildContext context) {
return BaseLayout(
child: PopScope(
canPop: false, // 🚫 block default back
onPopInvoked: (didPop) async {
if (didPop) return;
Navigator.pop(context); // 👈 go to previous page
},
child: _buildContent(context),
),
);
}
Widget _buildContent(BuildContext context) {
return isLoading ? Container(
color: Colors.transparent, // Semi-transparent background
child: Center(
child: // Your GIF loader widget
Image.asset(
height: 60,
width: 60,
'assets/nhance-loader.gif'), // Adjust path to your GIF loader
),
) : Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
/// 🔙 Back + Title (LEFT)
Row(
children: [
IconButton(
tooltip: 'Previous Page',
onPressed: () => Navigator.pop(context),
icon: const Icon(
Icons.arrow_back_ios,
size: 18,
color: Colors.black,
),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
const SizedBox(width: 6),
Text(
'Transaction Details - ${insurer_short_name} (${widget.cdMasterAccountNo})',
style: GoogleFonts.poppins(
fontSize: 18,
fontWeight: FontWeight.w500,
color: Colors.black,
),
),
],
),
/// Push right content to end
const Spacer(),
/// 🔍 Search Box
Container(
width: 380,
height: 37,
decoration: BoxDecoration(
color: const Color(0xFFF0F0F0),
borderRadius: BorderRadius.circular(8),
),
child: TextField(
controller: searchController,
onChanged: search,
style: GoogleFonts.poppins(fontSize: 14),
decoration: const InputDecoration(
hintText: 'Search',
prefixIcon: Icon(Icons.search, size: 18),
border: InputBorder.none,
contentPadding:
EdgeInsets.symmetric(horizontal: 12, vertical: 8),
),
),
),
const SizedBox(width: 12),
/// ⬇️ Export Button
SizedBox(
width: 116,
height: 37,
child: ElevatedButton(
onPressed: () => exportToCsv(filteredData),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFE26728),
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: Text(
'Export',
style: GoogleFonts.poppins(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Colors.white,
letterSpacing: 1,
),
),
),
),
],
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildInfoCard('Deposits', '$total_deposit',
Color(0xFF39D45B), Icons.arrow_upward),
_buildInfoCard('Consumed', '$total_consumed',
Color(0xFFED1D24), Icons.arrow_downward),
_buildInfoCard('Refund', '$total_refund',
Color(0xFF39D45B), Icons.arrow_upward),
_buildInfoCard('Current Balance', '$currect_balance',
Colors.black, null),
],
),
SizedBox(height: 16),
Expanded(
child: _buildCDDataTable(context),
),
],
)
);
}
Widget _buildInfoCard(
String label, String value, Color iconColor, IconData? icon) {
return Expanded(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 4),
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 2,
offset: Offset(0, 1),
),
],
),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween, // label left, value right
children: [
Text(
label,
style: GoogleFonts.poppins(
color: Color(0xFF737373),
fontSize: 14,
fontWeight: FontWeight.w400),
textAlign: TextAlign.left,
),
Row(
children: [
if (icon != null) Icon(icon, size: 20, color: iconColor),
if (icon != null) SizedBox(width: 4),
Text(
value,
style: GoogleFonts.poppins(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Color(0xFF000000),
),
),
],
),
],
),
],
),
),
);
}
Widget _buildCDDataTable(BuildContext context) {
if (filteredData.isEmpty) {
return const Center(child: Text('No available data'));
}
return CustomScrollView(
slivers: [
/// 🔒 FIXED HEADER
SliverPersistentHeader(
pinned: true,
delegate: _CDTableHeaderDelegate(),
),
/// 📄 TABLE ROWS
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final item = _paginatedData[index];
return _buildCDRow(item);
},
childCount: _paginatedData.length,
),
),
/// 📌 PAGINATION
SliverToBoxAdapter(
child: _buildPagination(context),
),
],
);
}
Widget _buildCDRow(Map<String, dynamic> item) {
final bool isAllowedSubType =
item['sub_type'] == '3' || item['sub_type'] == '4';
final bool hasSplitUpFile =
item['split_up_url'] != null &&
item['split_up_url'].toString().trim().isNotEmpty;
return Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 16),
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(color: Color(0xFFA9D9DE), width: 1),
),
),
child: Row(
children: [
_cell(formatDateNextLine(item['created_at']), 2),
SizedBox(width: 10),
_cell(formatDate(item['record_date']), 2),
SizedBox(width: 10),
_cell(item['unit'], 2),
SizedBox(width: 10),
_cell(
item['policy_type'] != null && item['policy_no'] != null
? '${item['policy_type']} - ${item['policy_no']}'
: '-',
3,
),
SizedBox(width: 10),
_cell(item['endorsement_no'], 3),
SizedBox(width: 10),
_cell(item['sub_type_text'], 2),
SizedBox(width: 10),
_cell(
item['transaction_type'] == 'Credit'
? formatAmount(item['amount'])
: '-',
2,
alignRight: true,
),
SizedBox(width: 10),
_cell(
item['transaction_type'] == 'Debit'
? formatAmount(item['amount'])
: '-',
2,
alignRight: true,
),
SizedBox(width: 10),
_cell(formatAmount(item['balance']), 2, alignRight: true),
SizedBox(width: 10),
_cell(item['description'], 3),
SizedBox(width: 10),
_cell(item['username'], 2),
SizedBox(width: 10),
Expanded(
flex: 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (isAllowedSubType)
_ActionIconButton(
icon: Icons.picture_as_pdf_outlined,
onTap: () => getCdEndorsementDetails(item['id']),
),
const SizedBox(width: 8),
if (isAllowedSubType && hasSplitUpFile)
_ActionIconButton(
icon: Icons.folder_open_outlined,
onTap: () => _launchURL(item['split_up_url']),
),
],
),
),
],
),
);
}
Widget _cell(String? text, int flex, {bool alignRight = false}) {
return Expanded(
flex: flex,
child: Text(
text ?? '-',
textAlign: alignRight ? TextAlign.right : TextAlign.left,
style: GoogleFonts.poppins(fontSize: 12),
// overflow: TextOverflow.ellipsis,
),
);
}
Widget _buildPagination(BuildContext context) {
final totalPages = (filteredData.length / _rowsPerPage).ceil();
const visiblePageCount = 5;
List<int> getVisiblePages() {
if (totalPages <= visiblePageCount) {
return List.generate(totalPages, (i) => i + 1);
}
if (_currentPage <= 3) {
return [1, 2, 3, 4, 5];
} else if (_currentPage >= totalPages - 2) {
return [
totalPages - 4,
totalPages - 3,
totalPages - 2,
totalPages - 1,
totalPages
];
} else {
return [
_currentPage - 2,
_currentPage - 1,
_currentPage,
_currentPage + 1,
_currentPage + 2,
];
}
}
List<int> visiblePages = getVisiblePages();
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Row(
children: [
// Dropdown for rows per page
DropdownButton<int>(
value: _rowsPerPage,
items: [5, 10, 15, 20, 50].map((int value) {
return DropdownMenuItem<int>(
value: value,
child: Text(' $value ',
style: GoogleFonts.poppins(fontSize: 15)),
);
}).toList(),
onChanged: (newValue) {
setState(() {
_rowsPerPage = newValue!;
_currentPage = 1;
});
},
),
// Previous button
IconButton(
onPressed: _currentPage > 1
? () => setState(() => _currentPage--)
: null,
icon: const Icon(Icons.chevron_left),
),
// First page + left ellipsis
if (!visiblePages.contains(1))
Row(children: [
_buildPageButton(1),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 4),
child: Text("..."),
),
]),
// Visible page buttons
for (int page in visiblePages) _buildPageButton(page),
// Right ellipsis + last page
if (!visiblePages.contains(totalPages))
Row(children: [
const Padding(
padding: EdgeInsets.symmetric(horizontal: 4),
child: Text("..."),
),
_buildPageButton(totalPages),
]),
// Next button
IconButton(
onPressed: _currentPage < totalPages
? () => setState(() => _currentPage++)
: null,
icon: const Icon(Icons.chevron_right),
),
],
),
),
],
);
}
Widget _buildPageButton(int page) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
_currentPage == page ? const Color(0xFF00A6A6) : Colors.grey[300],
foregroundColor: _currentPage == page ? Colors.white : Colors.black,
minimumSize: const Size(36, 36),
padding: EdgeInsets.zero,
),
onPressed: () {
setState(() {
_currentPage = page;
});
},
child: Text(page.toString()),
),
);
}
}
class _CDTableHeaderDelegate extends SliverPersistentHeaderDelegate {
@override
double get minExtent => 52;
@override
double get maxExtent => 52;
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
decoration: BoxDecoration(
color: Color(0xFFD7E9EB),
borderRadius: BorderRadius.circular(6),
),
// color: Color(0xFFD7E9EB),
padding: EdgeInsets.symmetric(horizontal: 16),
alignment: Alignment.centerLeft,
child: const Row(
children: [
_HeaderCell('Date', 2),
SizedBox(width: 10),
_HeaderCell('Record Date', 2),
SizedBox(width: 10),
_HeaderCell('Unit', 2),
SizedBox(width: 10),
_HeaderCell('Policy', 3),
SizedBox(width: 10),
_HeaderCell('Endorsement No', 3),
SizedBox(width: 10),
_HeaderCell('Sub Type', 2),
SizedBox(width: 10),
_HeaderCell('₹ Credit', 2, alignRight: true),
SizedBox(width: 10),
_HeaderCell('₹ Debit', 2, alignRight: true),
SizedBox(width: 10),
_HeaderCell('₹ Balance', 2, alignRight: true),
SizedBox(width: 10),
_HeaderCell('Description', 3),
SizedBox(width: 10),
_HeaderCell('User', 2),
SizedBox(width: 10),
_HeaderCell('Action', 2, center: true),
SizedBox(width: 10),
],
),
);
}
@override
bool shouldRebuild(_) => false;
}
class _HeaderCell extends StatelessWidget {
final String text;
final int flex;
final bool alignRight;
final bool center;
const _HeaderCell(
this.text,
this.flex, {
this.alignRight = false,
this.center = false,
});
@override
Widget build(BuildContext context) {
return Expanded(
flex: flex,
child: Text(
text,
textAlign:
center ? TextAlign.center : (alignRight ? TextAlign.right : TextAlign.left),
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: const Color(0xFF000000),
),
),
);
}
}
class _ActionIconButton extends StatelessWidget {
final IconData icon;
final VoidCallback onTap;
final bool enabled;
const _ActionIconButton({
required this.icon,
required this.onTap,
this.enabled = true,
});
@override
Widget build(BuildContext context) {
return SizedBox(
width: 36,
height: 36,
child: Material(
color: enabled
? const Color(0xFFDFF4F5)
: Colors.transparent,
borderRadius: BorderRadius.circular(10),
child: InkWell(
borderRadius: BorderRadius.circular(10),
onTap: enabled ? onTap : null,
child: Icon(
icon,
size: 22,
color: enabled ? Colors.black : Colors.transparent,
),
),
),
);
}
}
// Sample Data class representing each element in the array
class Data {
final dynamic value;
final int row;
final int column;
final String sheet;
Data(this.value, this.row, this.column, this.sheet);
}