PDF Edited

This commit is contained in:
2025-12-03 18:05:34 +05:30
parent 5d307607fd
commit c7111d518a
2 changed files with 583 additions and 209 deletions

View File

@@ -1,7 +1,10 @@
import 'dart:io'; import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:pdf/pdf.dart';
import 'package:shimmer/shimmer.dart'; import 'package:shimmer/shimmer.dart';
import 'package:kmobile/data/models/transaction.dart'; import 'package:kmobile/data/models/transaction.dart';
import 'package:kmobile/data/repositories/transaction_repository.dart'; import 'package:kmobile/data/repositories/transaction_repository.dart';
@@ -9,13 +12,9 @@ import 'package:kmobile/di/injection.dart';
import '../../../l10n/app_localizations.dart'; import '../../../l10n/app_localizations.dart';
import 'transaction_details_screen.dart'; import 'transaction_details_screen.dart';
import 'package:pdf/widgets.dart' as pw; import 'package:pdf/widgets.dart' as pw;
import 'package:permission_handler/permission_handler.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:open_filex/open_filex.dart'; import 'package:open_filex/open_filex.dart';
import 'package:kmobile/data/models/user.dart'; import 'package:kmobile/data/models/user.dart';
class AccountStatementScreen extends StatefulWidget { class AccountStatementScreen extends StatefulWidget {
@@ -451,220 +450,583 @@ class _AccountStatementScreen extends State<AccountStatementScreen> {
); );
} }
Future<void> _exportToPdf() async { Future<void> _exportToPdf() async {
if (_transactions.isEmpty) { if (_transactions.isEmpty) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(content: Text('No transactions to export.')),
content: Text('No transactions to export.'), );
}
return;
}
await flutterLocalNotificationsPlugin.show(
0,
'Downloading PDF',
'Your account statement is being downloaded...',
const NotificationDetails(
android: AndroidNotificationDetails(
'download_channel',
'Download Notifications',
channelDescription: 'Notifications for PDF downloads',
importance: Importance.low,
priority: Priority.low,
showProgress: true,
maxProgress: 0,
ongoing: true,
icon: 'notification_icon',
),
),
);
// --- 1. LOAD ASSETS ---
final logoImage =
pw.MemoryImage((await rootBundle.load('assets/images/logo.png')).buffer.asUint8List());
final timesFont = await rootBundle.load("assets/fonts/Rubik-Regular.ttf");
final timesBoldFont = await rootBundle.load("assets/fonts/Rubik-Bold.ttf");
final ttf = pw.Font.ttf(timesFont);
final ttfBold = pw.Font.ttf(timesBoldFont);
// --- 2. DEFINE COLORS ---
final primaryColor = PdfColor.fromHex("#1a5f3a");
final secondaryColor = PdfColor.fromHex("#2e7d32");
final debitColor = PdfColor.fromHex("#d32f2f");
final lightGreyColor = PdfColor.fromHex("#666");
final tableBorderColor = PdfColor.fromHex("#d0d0d0");
final lightBgColor = PdfColor.fromHex("#f9f9f9");
final warningBgColor = PdfColor.fromHex("#f8d7da");
final warningBorderColor = PdfColor.fromHex("#f5c6cb");
final warningTextColor = PdfColor.fromHex("#721c24");
// --- 3. CREATE PDF ---
final pdf = pw.Document(
theme: pw.ThemeData.withFont(base: ttf, bold: ttfBold),
);
// --- 4. BUILD PAGES ---
pdf.addPage(
pw.MultiPage(
pageFormat: PdfPageFormat.a4.copyWith(
marginTop: 15 * PdfPageFormat.mm,
marginLeft: 10 * PdfPageFormat.mm,
marginRight: 10 * PdfPageFormat.mm,
marginBottom: 20 * PdfPageFormat.mm,
),
header: (context) =>
_buildHeader(logoImage, primaryColor, lightGreyColor),
footer: (context) {
return pw.Center(
child: pw.Text(
'** This is only for information purpose and not for legal use **',
style: pw.TextStyle(
fontSize: 9,
color: lightGreyColor,
fontStyle: pw.FontStyle.italic)));
},
build: (context) => [
_buildAccountDetails(
customerName: selectedUser.name ?? '',
branchCode: selectedUser.branchId ?? '',
accountNo: selectedUser.accountNo ?? '',
cifNumber: selectedUser.cifNumber ?? '',
address: selectedUser.address ?? '',
lightGreyColor: lightGreyColor,
tableBorderColor: tableBorderColor,
lightBgColor: lightBgColor,
),
_buildWarning(warningBgColor, warningBorderColor, debitColor,
warningTextColor),
_buildPeriodHeader(
primaryColor: primaryColor,
fromDate: fromDate,
toDate: toDate,
),
_buildTransactionsTable(
transactions: _transactions,
primaryColor: primaryColor,
secondaryColor: secondaryColor,
debitColor: debitColor,
tableBorderColor: tableBorderColor,
),
pw.SizedBox(height: 20),
pw.Text('END OF STATEMENT',
style: const pw.TextStyle(fontSize: 12)),
],
),
);
pdf.addPage(
pw.Page(
pageFormat: PdfPageFormat.a4.copyWith(
marginTop: 15 * PdfPageFormat.mm,
marginLeft: 10 * PdfPageFormat.mm,
marginRight: 10 * PdfPageFormat.mm,
marginBottom: 20 * PdfPageFormat.mm,
),
build: (context) => _buildLastPage(),
),
);
// --- 5. SAVE AND NOTIFY ---
try {
final Uint8List pdfBytes = await pdf.save();
final String timestamp =
DateFormat("ddMMyyyy_HHmm").format(DateTime.now());
final String fileName = 'Statement_${selectedUser.accountNo}_$timestamp.pdf';
String? filePath;
if (Platform.isAndroid) {
final directory = Directory('/storage/emulated/0/Download');
if (!await directory.exists()) {
await directory.create(recursive: true);
}
final file = File('${directory.path}/$fileName');
await file.writeAsBytes(pdfBytes);
filePath = file.path;
} else {
final tempDir = await getTemporaryDirectory();
final file = await File('${tempDir.path}/$fileName').create();
await file.writeAsBytes(pdfBytes);
filePath = file.path;
}
await flutterLocalNotificationsPlugin.show(
0,
'PDF Download Complete',
'Your account statement has been saved.',
const NotificationDetails(
android: AndroidNotificationDetails(
'download_channel',
'Download Notifications',
channelDescription: 'Notifications for PDF downloads',
importance: Importance.high,
priority: Priority.high,
showProgress: false,
ongoing: false,
autoCancel: true,
icon: 'notification_icon',
actions: [
AndroidNotificationAction('open_pdf', 'Open PDF',
showsUserInterface: true)
],
),
),
payload: filePath,
);
} catch (e) {
await flutterLocalNotificationsPlugin.show(
0,
'PDF Download Failed',
'Error saving PDF: $e',
const NotificationDetails(
android: AndroidNotificationDetails('download_channel', 'Download Notifications',
channelDescription: 'Notifications for PDF downloads',
importance: Importance.high,
priority: Priority.high,
showProgress: false,
ongoing: false,
icon: 'notification_icon'),
), ),
); );
} }
return;
} }
// Show downloading notification pw.Widget _buildHeader(pw.MemoryImage logoImage, PdfColor primaryColor,
await flutterLocalNotificationsPlugin.show( PdfColor lightGreyColor) {
0, return pw.Container(
'Downloading PDF', margin: const pw.EdgeInsets.only(bottom: 15),
'Your account statement is being downloaded...', padding: const pw.EdgeInsets.only(bottom: 12),
const NotificationDetails( decoration: pw.BoxDecoration(
android: AndroidNotificationDetails( border: pw.Border(bottom: pw.BorderSide(color: primaryColor, width: 2)),
'download_channel',
'Download Notifications',
channelDescription:
'Notifications for PDF downloads',
importance: Importance.low,
priority: Priority.low,
showProgress: true,
maxProgress: 0, // Indeterminate progress
ongoing: true,
icon: 'notification_icon',
), ),
), child: pw.Row(
); children: [
pw.Image(logoImage, height: 55, width: 55),
var logo = await rootBundle.load('assets/images/logo.png'); pw.SizedBox(width: 12),
var rubik = await rootBundle.load("assets/fonts/Rubik-Regular.ttf"); pw.Expanded(
final pdf = pw.Document(); child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
pdf.addPage(pw.MultiPage(
margin: const pw.EdgeInsets.all(20),
build: (pw.Context context) {
return [
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.start,
crossAxisAlignment: pw.CrossAxisAlignment.center,
children: [ children: [
pw.Image(pw.MemoryImage(logo.buffer.asUint8List()), pw.Text(
width: 50, height: 50), 'THE KANGRA CENTRAL CO-OPERATIVE BANK LTD.',
pw.SizedBox(width: 20), style: pw.TextStyle(
pw.Text('Account Statement - KCCB', fontSize: 18,
style: pw.TextStyle( color: primaryColor,
fontSize: 24, fontWeight: pw.FontWeight.bold)), fontWeight: pw.FontWeight.bold,
]), letterSpacing: 0.3),
pw.SizedBox(height: 20), ),
pw.Row( pw.Text(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween, 'Head Office: Dharmsala, District Kangra (H.P.), Pin. 176215',
children: [ style: pw.TextStyle(fontSize: 10, color: lightGreyColor),
pw.Text('Account Number: ${selectedUser.accountNo}', ),
style:
pw.TextStyle(font: pw.Font.ttf(rubik), fontSize: 15)),
pw.Text('Account Type: ${selectedUser.productType}',
style: pw.TextStyle(
fontSize: 15,
font: pw.Font.ttf(rubik),
)),
]),
pw.SizedBox(height: 20),
pw.Table(border: pw.TableBorder.all(), columnWidths: {
0: const pw.FractionColumnWidth(0.2),
1: const pw.FractionColumnWidth(0.45),
2: const pw.FractionColumnWidth(0.15),
3: const pw.FractionColumnWidth(0.20),
}, children: [
pw.TableRow(
children: [
pw.Padding(
padding: const pw.EdgeInsets.all(4),
child: pw.Text('Date')),
pw.Padding(
padding: const pw.EdgeInsets.all(4),
child: pw.Text('Description', softWrap: true)),
pw.Padding(
padding: const pw.EdgeInsets.all(4),
child: pw.Text('Amount')),
pw.Padding(
padding: const pw.EdgeInsets.all(4),
child: pw.Text('Balance')),
], ],
), ),
..._transactions.map<pw.TableRow>((tx) { ),
return pw.TableRow(children: [ pw.Column(
pw.Padding( crossAxisAlignment: pw.CrossAxisAlignment.end,
padding: const pw.EdgeInsets.all(10), children: [
child: pw.Text(tx.date ?? '', pw.Text(
style: pw.TextStyle( 'e-Statement Service',
fontSize: 12, style: pw.TextStyle(
font: pw.Font.ttf(rubik), fontSize: 11,
))), color: primaryColor,
pw.Padding( fontWeight: pw.FontWeight.bold),
padding: const pw.EdgeInsets.all(10), ),
child: pw.Text(tx.name ?? '', pw.SizedBox(height: 2),
style: pw.TextStyle( pw.Text(
fontSize: 12, font: pw.Font.ttf(rubik)))), 'Generated: ${DateFormat("dd/MM/yyyy HH:mm").format(DateTime.now())}',
pw.Padding( style: pw.TextStyle(fontSize: 9, color: lightGreyColor),
padding: const pw.EdgeInsets.all(10), ),
child: pw.Text("${tx.amount} ${tx.type}", ],
style: pw.TextStyle( ),
fontSize: 12, ],
font: pw.Font.ttf(rubik), ),
))), );
pw.Padding( }
padding: const pw.EdgeInsets.all(10),
child: pw.Text("${tx.balance} ${tx.balanceType}", pw.Widget _buildAccountDetails({
style: pw.TextStyle( required String customerName,
fontSize: 12, required String branchCode,
font: pw.Font.ttf(rubik), required String accountNo,
))), required String cifNumber,
]); required String address,
}), required PdfColor lightGreyColor,
]) required PdfColor tableBorderColor,
]; required PdfColor lightBgColor,
}) {
const cellPadding = pw.EdgeInsets.symmetric(horizontal: 12, vertical: 8);
final border = pw.BorderSide(color: tableBorderColor, width: 1);
return pw.Table(
border: pw.TableBorder(
top: border,
bottom: border,
left: border,
right: border,
horizontalInside: border,
verticalInside: border,
),
columnWidths: {
0: const pw.FlexColumnWidth(1),
1: const pw.FlexColumnWidth(1),
}, },
footer: (pw.Context context) { children: [
return pw.Container( pw.TableRow(
alignment: pw.Alignment.centerRight, children: [
margin: const pw.EdgeInsets.only(top: 10), _buildDetailCell('Customer Name', customerName, cellPadding, lightGreyColor),
child: pw.Text( _buildDetailCell('CIF Number', cifNumber, cellPadding, lightGreyColor),
'Kangra Central Co-Operative Bank Pvt Ltd. ©. All rights reserved.', ],
style: pw.TextStyle( ),
font: pw.Font.ttf(rubik), pw.TableRow(
fontSize: 8, children: [
_buildDetailCell('Account Number', accountNo, cellPadding, lightGreyColor),
_buildDetailCell('Branch Code', branchCode, cellPadding, lightGreyColor),
],
),
pw.TableRow(
children: [
pw.Container(
padding: cellPadding,
// Using a Column inside a single cell to potentially wrap long address
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text(
'Customer Address',
style: pw.TextStyle(fontSize: 10, color: lightGreyColor),
),
pw.SizedBox(height: 2),
pw.Text(
address,
style: pw.TextStyle(fontSize: 11, fontWeight: pw.FontWeight.bold),
),
],
),
),
// Empty container for the second column in this row, as it's a single spanning column
pw.Container(),
],
),
],
);
}
pw.Widget _buildDetailCell(String label, String value, pw.EdgeInsets padding,
PdfColor lightGreyColor, {bool vertical = false}) {
final children = [
pw.Text(
label,
style: pw.TextStyle(fontSize: 10, color: lightGreyColor),
),
if (vertical) pw.SizedBox(height: 2),
pw.Text(
value,
style: pw.TextStyle(fontSize: 11, fontWeight: pw.FontWeight.bold),
),
];
return pw.Padding(
padding: padding,
child: vertical
? pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
mainAxisAlignment: pw.MainAxisAlignment.start,
children: children)
: pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
children: children),
);
}
pw.Widget _buildWarning(PdfColor warningBgColor, PdfColor warningBorderColor,
PdfColor debitColor, PdfColor warningTextColor) {
return pw.Container(
margin: const pw.EdgeInsets.symmetric(vertical: 15),
padding: const pw.EdgeInsets.all(10),
decoration: pw.BoxDecoration(
color: warningBgColor,
border: pw.Border.all(color: warningBorderColor, width: 1),
borderRadius: pw.BorderRadius.circular(4),
),
child: pw.Row(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.SizedBox(width: 10),
pw.Expanded(
child: pw.RichText(
text: pw.TextSpan(
style: pw.TextStyle(
fontSize: 10,
color: warningTextColor,
lineSpacing: 1.5),
children: [
pw.TextSpan(
text: 'NEVER SHARE ',
style: pw.TextStyle(fontWeight: pw.FontWeight.bold)),
const pw.TextSpan(
text:
'your Card number, CVV, PIN, OTP, Internet Banking User ID, Password or URB with anyone even if the caller claims to be a bank employee. Sharing these details can lead to unauthorized access to your account.'),
],
),
), ),
), ),
); ],
})); ),
);
}
try { pw.Widget _buildPeriodHeader(
final Uint8List pdfBytes = await pdf.save(); {required PdfColor primaryColor, DateTime? fromDate, DateTime? toDate}) {
final String timestamp = DateTime.now().millisecondsSinceEpoch.toString(); String from = fromDate != null
final String fileName = 'account_statement_$timestamp.pdf'; ? DateFormat('dd/MM/yyyy').format(fromDate)
: 'the beginning';
String to =
toDate != null ? DateFormat('dd/MM/yyyy').format(toDate) : 'today';
String? filePath; return pw.Container(
margin: const pw.EdgeInsets.only(bottom: 12),
if (Platform.isAndroid) { padding: const pw.EdgeInsets.all(10),
final directory = Directory('/storage/emulated/0/Download'); decoration: pw.BoxDecoration(
if (!await directory.exists()) { border: pw.Border.symmetric(
await directory.create(recursive: true); horizontal: pw.BorderSide(color: primaryColor, width: 2)),
} color: PdfColor.fromHex("#f5f5f5"),
final file = File('${directory.path}/$fileName'); ),
await file.writeAsBytes(pdfBytes); alignment: pw.Alignment.center,
filePath = file.path; child: pw.RichText(
} else if (Platform.isIOS) { text: pw.TextSpan(
final tempDir = await getTemporaryDirectory(); style: pw.TextStyle(
final file = await File('${tempDir.path}/$fileName').create(); fontSize: 13,
await file.writeAsBytes(pdfBytes); color: primaryColor,
filePath = file.path; fontWeight: pw.FontWeight.bold),
} else { children: [
// For other platforms, we might just save to temporary directory const pw.TextSpan(text: 'Account statement from '),
final tempDir = await getTemporaryDirectory(); pw.TextSpan(
final file = await File('${tempDir.path}/$fileName').create(); text: from,
await file.writeAsBytes(pdfBytes); style: pw.TextStyle(fontWeight: pw.FontWeight.bold)),
filePath = file.path; const pw.TextSpan(text: ' to '),
} pw.TextSpan(
text: to,
// Update notification to download complete style: pw.TextStyle(fontWeight: pw.FontWeight.bold)),
await flutterLocalNotificationsPlugin.show(
0,
'PDF Download Complete',
'Your account statement has been saved.',
const NotificationDetails(
android: AndroidNotificationDetails(
'download_channel',
'Download Notifications',
channelDescription:
'Notifications for PDF downloads',
importance: Importance.high,
priority: Priority.high,
showProgress: false,
ongoing: false,
autoCancel: true,
icon: 'notification_icon',
actions: [
AndroidNotificationAction(
'open_pdf',
'Open PDF',
showsUserInterface: true,
)
], ],
), ),
), ),
payload: filePath,
);
} catch (e) {
await flutterLocalNotificationsPlugin.show(
0,
'PDF Download Failed',
'Error saving PDF: $e',
const NotificationDetails(
android: AndroidNotificationDetails(
'download_channel',
'Download Notifications',
channelDescription:
'Notifications for PDF downloads',
importance: Importance.high,
priority: Priority.high,
showProgress: false,
ongoing: false,
icon: 'notification_icon',
),
),
); );
} }
}
Widget buildDateBox(String label, DateTime? date) { pw.Widget _buildTransactionsTable({
required List<Transaction> transactions,
required PdfColor primaryColor,
required PdfColor secondaryColor,
required PdfColor debitColor,
required PdfColor tableBorderColor,
}) {
//final border = pw.BorderSide(color: tableBorderColor, width: 1);
return pw.Table(
border: pw.TableBorder.all(color: tableBorderColor, width: 1),
columnWidths: {
0: const pw.FlexColumnWidth(1.5),
1: const pw.FlexColumnWidth(4),
2: const pw.FlexColumnWidth(2.2),
3: const pw.FlexColumnWidth(2.2),
},
children: [
pw.TableRow(
decoration: pw.BoxDecoration(color: secondaryColor),
children: [
pw.Padding(
padding: const pw.EdgeInsets.all(10),
child: pw.Text('Date',
textAlign: pw.TextAlign.center,
style: pw.TextStyle(
fontSize: 11,
fontWeight: pw.FontWeight.bold,
color: PdfColors.white)),
),
pw.Padding(
padding: const pw.EdgeInsets.all(10),
child: pw.Text('Mode / Particulars',
textAlign: pw.TextAlign.left,
style: pw.TextStyle(
fontSize: 11,
fontWeight: pw.FontWeight.bold,
color: PdfColors.white)),
),
pw.Padding(
padding: const pw.EdgeInsets.all(10),
child: pw.Text('Withdrawals / Deposits',
textAlign: pw.TextAlign.center,
style: pw.TextStyle(
fontSize: 11,
fontWeight: pw.FontWeight.bold,
color: PdfColors.white)),
),
pw.Padding(
padding: const pw.EdgeInsets.all(10),
child: pw.Text('Balance',
textAlign: pw.TextAlign.center,
style: pw.TextStyle(
fontSize: 11,
fontWeight: pw.FontWeight.bold,
color: PdfColors.white)),
),
],
),
...transactions.map((tx) {
final amount = double.tryParse(tx.amount ?? '0') ?? 0;
final isDebit = tx.type == 'DR';
return pw.TableRow(
decoration: const pw.BoxDecoration(color: PdfColors.white),
children: [
pw.Padding(
padding: const pw.EdgeInsets.all(8),
child: pw.Text(tx.date ?? '',
textAlign: pw.TextAlign.center,
style: const pw.TextStyle(fontSize: 11)),
),
pw.Padding(
padding: const pw.EdgeInsets.all(8),
child: pw.Text(tx.name ?? '',
textAlign: pw.TextAlign.left,
style: const pw.TextStyle(fontSize: 11)),
),
pw.Padding(
padding: const pw.EdgeInsets.all(8),
child: pw.Text(
'${NumberFormat.currency(locale: 'en_IN', symbol: '').format(amount)} ${tx.type}',
textAlign: pw.TextAlign.right,
style: pw.TextStyle(
fontSize: 11,
color: isDebit ? debitColor : secondaryColor,
fontWeight: pw.FontWeight.bold,
),
),
),
pw.Padding(
padding: const pw.EdgeInsets.all(8),
child: pw.Text(
'${NumberFormat.currency(locale: 'en_IN', symbol: '').format(double.tryParse(tx.balance ?? '0') ?? 0)} ${tx.balanceType ?? ''}',
textAlign: pw.TextAlign.right,
style: pw.TextStyle(
fontSize: 11, fontWeight: pw.FontWeight.bold),
),
),
],
);
}),
],
);
}
pw.Widget _buildLastPage() {
return pw.Container(
padding: const pw.EdgeInsets.all(20),
decoration: pw.BoxDecoration(
border: pw.Border.all(color: PdfColors.black, width: 2),
color: PdfColor.fromHex("#fafafa"),
borderRadius: pw.BorderRadius.circular(4),
),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text(
'IMPORTANT INFORMATION:',
style: pw.TextStyle(
fontSize: 11,
fontWeight: pw.FontWeight.bold,
color: PdfColors.black,
),
),
pw.SizedBox(height: 15),
..._buildInfoPoints(PdfColors.black, [
'The Kangra Central Cooperative Bank Officials or representatives will NEVER ask you for your personal information i.e. your card details, passwords, PIN, CVV, OTP etc. Do not share such details with anyone over phone, SMS or email.',
'Always stay vigilant to suspicious emails. Do not open any suspicious emails.',
'Always stay vigilant when giving out sensitive personal or account information.',
'Beware of messages that instill a sense of urgency (e.g., account will expire unless you "verify" your information). Contact the Bank directly if unsure.',
'Always log out of secondary devices and reset your passwords frequently.',
'Use strong passwords: Create strong passwords that are difficult for hackers to guess.',
'Use public Wi-Fi with caution: Be careful when using public Wi-Fi networks.',
'Back up your data regularly to a secure, encrypted, off-site location.',
'Follow corporate security policies: Adhere to your company\'s security guidelines.',
'Assess third-party app permissions carefully before granting access.',
'Lock your computer and mobile phone when not in use.',
'Don\'t leave devices unattended. Keep all mobile devices, such as laptops and cell phones, physically secured.',
'Don\'t leave Bluetooth / Wireless turned on when not in use. Enable them only when needed and in a safe environment.',
]),
],
),
);
}
List<pw.Widget> _buildInfoPoints(
PdfColor primaryColor, List<String> points) {
return points.map((point) {
return pw.Padding(
padding: const pw.EdgeInsets.only(bottom: 10),
child: pw.Row(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Container(
width: 15,
child: pw.Text('*',
style: pw.TextStyle(
color: primaryColor,
fontSize: 12,
fontWeight: pw.FontWeight.bold))),
pw.Expanded(
child: pw.Text(
point,
style: pw.TextStyle(
fontSize: 11,
lineSpacing: 1.6,
fontWeight: pw.FontWeight.bold),
),
),
],
),
);
}).toList();
} Widget buildDateBox(String label, DateTime? date) {
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
decoration: BoxDecoration( decoration: BoxDecoration(

View File

@@ -417,13 +417,25 @@ class _DashboardScreenState extends State<DashboardScreen>
child: Scaffold( child: Scaffold(
backgroundColor: theme.scaffoldBackgroundColor, backgroundColor: theme.scaffoldBackgroundColor,
appBar: AppBar( appBar: AppBar(
backgroundColor: theme.scaffoldBackgroundColor, backgroundColor: const Color(0xFF01A04C),
leading: const Padding( leading: Padding(
padding: EdgeInsets.only(left: 10.0), padding: const EdgeInsets.only(left: 10.0),
child: CircleAvatar( //
radius: 15, child: CircleAvatar(
backgroundImage: AssetImage('assets/images/logo.png'), radius: 20,
), backgroundColor: Colors.white,
child: Padding(
padding: const EdgeInsets.all(2.0),
child: ClipOval(
child: Image.asset(
'assets/images/logo.png',
width: 40,
height: 40,
fit: BoxFit.cover,
),
),
),
),
), ),
title: Text( title: Text(
AppLocalizations.of(context).kccBankFull.replaceAll('-', '\u2011'), AppLocalizations.of(context).kccBankFull.replaceAll('-', '\u2011'),
@@ -431,7 +443,7 @@ class _DashboardScreenState extends State<DashboardScreen>
softWrap: true, // Explicitly allow wrapping softWrap: true, // Explicitly allow wrapping
maxLines: 2, // Allow text to wrap to a maximum of 2 lines maxLines: 2, // Allow text to wrap to a maximum of 2 lines
style: TextStyle( style: TextStyle(
color: theme.colorScheme.primary, color: theme.colorScheme.onPrimary,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
fontSize: 20, fontSize: 20,
), ),