first commit

This commit is contained in:
Aditya Siregar
2025-07-30 22:38:44 +07:00
commit 73320561b0
444 changed files with 64633 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
import 'dart:ui' as ui;
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class AppIconGenerator {
static Future<Uint8List> generateAppIcon() async {
final recorder = ui.PictureRecorder();
final canvas = Canvas(recorder);
const size = Size(1024, 1024);
// Create a white background
final paint = Paint()..color = Colors.white;
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint);
// Create a blue circle background
paint.color = const Color(0xFF2196F3); // Material Blue
canvas.drawCircle(
Offset(size.width / 2, size.height / 2),
size.width * 0.4,
paint,
);
// Draw the gift box icon
_drawGiftBox(canvas, size);
// Draw the text
_drawText(canvas, size);
final picture = recorder.endRecording();
final image = await picture.toImage(1024, 1024);
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
return byteData!.buffer.asUint8List();
}
static void _drawGiftBox(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = size.width * 0.02;
final centerX = size.width / 2;
final centerY = size.height / 2 - size.height * 0.1;
final boxSize = size.width * 0.15;
// Draw gift box
final boxRect = Rect.fromCenter(
center: Offset(centerX, centerY),
width: boxSize,
height: boxSize,
);
canvas.drawRect(boxRect, paint);
// Draw bow
final bowWidth = boxSize * 0.8;
final bowHeight = boxSize * 0.3;
final bowRect = Rect.fromCenter(
center: Offset(centerX, centerY - boxSize / 2),
width: bowWidth,
height: bowHeight,
);
canvas.drawRRect(
RRect.fromRectAndRadius(bowRect, Radius.circular(bowHeight / 2)),
paint,
);
// Draw 'e' inside the box
final textPainter = TextPainter(
text: TextSpan(
text: 'e',
style: TextStyle(
color: Colors.white,
fontSize: boxSize * 0.4,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
Offset(
centerX - textPainter.width / 2,
centerY - textPainter.height / 2,
),
);
}
static void _drawText(Canvas canvas, Size size) {
// Draw "ENAKLO"
final enakloPainter = TextPainter(
text: TextSpan(
text: 'ENAKLO',
style: TextStyle(
color: Colors.white,
fontSize: size.width * 0.08,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
enakloPainter.layout();
enakloPainter.paint(
canvas,
Offset(
size.width / 2 - enakloPainter.width / 2,
size.height / 2 + size.height * 0.05,
),
);
// Draw "POS"
final posPainter = TextPainter(
text: TextSpan(
text: 'POS',
style: TextStyle(
color: Colors.white,
fontSize: size.width * 0.06,
fontWeight: FontWeight.w500,
),
),
textDirection: TextDirection.ltr,
);
posPainter.layout();
posPainter.paint(
canvas,
Offset(
size.width / 2 - posPainter.width / 2,
size.height / 2 + size.height * 0.15,
),
);
}
}
+19
View File
@@ -0,0 +1,19 @@
import 'package:intl/intl.dart';
class DateFormatter {
static String formatDateTime(DateTime dateTime) {
return '${dateTime.year}-${_addZeroPrefix(dateTime.month)}-${_addZeroPrefix(dateTime.day)}';
}
static String _addZeroPrefix(int value) {
return value.toString().padLeft(2, '0');
}
static String formatDateTime2(String dateTimeString) {
final dateTime = DateTime.parse(dateTimeString);
final formatter = DateFormat(
'dd MMMM yyyy, HH:mm',
);
return formatter.format(dateTime);
}
}
+79
View File
@@ -0,0 +1,79 @@
import 'dart:developer';
import 'dart:io';
import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pdf/widgets.dart';
class HelperPdfService {
static Future<File> saveDocument({
required String name,
required Document pdf,
}) async {
try {
log("Starting PDF save process for: $name");
log("PDF document object: $pdf");
final bytes = await pdf.save();
log("PDF bytes generated successfully, size: ${bytes.length} bytes");
if (bytes.isEmpty) {
log("WARNING: PDF bytes are empty!");
return Future.error("PDF bytes are empty");
}
final dir = await getApplicationDocumentsDirectory();
log("Documents directory: ${dir.path}");
final file = File('${dir.path}/$name');
log("Saving PDF to: ${file.path}");
await file.writeAsBytes(bytes);
log("PDF saved successfully to: ${file.path}");
// Verify file was created
if (await file.exists()) {
final fileSize = await file.length();
log("File exists and size is: $fileSize bytes");
} else {
log("ERROR: File was not created!");
return Future.error("File was not created");
}
return file;
} catch (e) {
log("Failed to save document: $e");
log("Error stack trace: ${StackTrace.current}");
return Future.error("Failed to save document: $e");
}
}
static Future openFile(File file) async {
try {
final url = file.path;
log("Attempting to open file: $url");
if (!await file.exists()) {
log("ERROR: File does not exist: $url");
return;
}
final fileSize = await file.length();
log("File exists and size is: $fileSize bytes");
log("Calling OpenFile.open...");
final result = await OpenFile.open(url, type: "application/pdf");
log("OpenFile result: $result");
if (result.type == ResultType.done) {
log("File opened successfully");
} else {
log("File opening failed with result: ${result.type}");
log("Error message: ${result.message}");
}
} catch (e) {
log("Failed to open file: $e");
log("Error stack trace: ${StackTrace.current}");
}
}
}
+154
View File
@@ -0,0 +1,154 @@
import 'dart:io';
import 'package:enaklo_pos/core/extensions/date_time_ext.dart';
import 'package:enaklo_pos/core/extensions/int_ext.dart';
import 'package:flutter/services.dart';
import 'package:enaklo_pos/core/utils/helper_pdf_service.dart';
import 'package:enaklo_pos/data/models/response/item_sales_response_model.dart';
import 'package:pdf/widgets.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
class ItemSalesInvoice {
static late Font ttf;
static Future<File> generate(
List<ItemSales> itemSales, String searchDateFormatted) async {
final pdf = Document();
// var data = await rootBundle.load("assets/fonts/noto-sans.ttf");
// ttf = Font.ttf(data);
final ByteData dataImage = await rootBundle.load('assets/images/logo.png');
final Uint8List bytes = dataImage.buffer.asUint8List();
// Membuat objek Image dari gambar
final image = pw.MemoryImage(bytes);
pdf.addPage(
MultiPage(
build: (context) => [
buildHeader(image, searchDateFormatted),
SizedBox(height: 1 * PdfPageFormat.cm),
buildInvoice(itemSales),
Divider(),
SizedBox(height: 0.25 * PdfPageFormat.cm),
],
footer: (context) => buildFooter(),
),
);
return HelperPdfService.saveDocument(
name:
'Enaklo POS | Item Sales Report | ${DateTime.now().millisecondsSinceEpoch}.pdf',
pdf: pdf);
}
static Widget buildHeader(MemoryImage image, String searchDateFormatted) =>
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 1 * PdfPageFormat.cm),
Text('Enaklo POS | Item Sales Report',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
)),
SizedBox(height: 0.2 * PdfPageFormat.cm),
Text(
"Data: $searchDateFormatted",
),
Text(
'Created At: ${DateTime.now().toFormattedDate3()}',
),
],
),
Image(
image,
width: 80.0,
height: 80.0,
fit: BoxFit.fill,
),
]);
static Widget buildInvoice(List<ItemSales> itemSales) {
final headers = ['Id', 'Order', 'Product', 'Qty', 'Price', 'Total'];
final data = itemSales.map((item) {
return [
item.id!,
item.orderId,
item.productName,
item.price!.currencyFormatRp,
item.quantity,
(item.price! * item.quantity!).currencyFormatRp
];
}).toList();
return Table.fromTextArray(
headers: headers,
data: data,
border: null,
headerStyle: TextStyle(
fontWeight: FontWeight.bold, color: PdfColor.fromHex('FFFFFF')),
headerDecoration: BoxDecoration(color: PdfColors.blue),
cellHeight: 30,
cellAlignments: {
0: Alignment.centerLeft,
1: Alignment.center,
2: Alignment.center,
3: Alignment.centerLeft,
4: Alignment.centerLeft,
5: Alignment.centerLeft,
},
);
}
static Widget buildFooter() => Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Divider(),
SizedBox(height: 2 * PdfPageFormat.mm),
buildSimpleText(
title: 'Address',
value:
'Jalan Melati No. 12, Mranggen, Demak, Central Java, 89568'),
SizedBox(height: 1 * PdfPageFormat.mm),
],
);
static buildSimpleText({
required String title,
required String value,
}) {
final style = TextStyle(fontWeight: FontWeight.bold);
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: pw.CrossAxisAlignment.end,
children: [
Text(title, style: style),
SizedBox(width: 2 * PdfPageFormat.mm),
Text(value),
],
);
}
static buildText({
required String title,
required String value,
double width = double.infinity,
TextStyle? titleStyle,
bool unite = false,
}) {
final style = titleStyle ?? TextStyle(fontWeight: FontWeight.bold);
return Container(
width: width,
child: Row(
children: [
Expanded(child: Text(title, style: style)),
Text(value, style: unite ? style : null),
],
),
);
}
}
+43
View File
@@ -0,0 +1,43 @@
import 'dart:developer';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:permission_handler/permission_handler.dart';
class PermessionHelper {
Future<bool> checkPermission() async {
final deviceInfo = await DeviceInfoPlugin().androidInfo;
bool permissionStatus;
if (deviceInfo.version.sdkInt > 32) {
permissionStatus = await Permission.photos.request().isGranted;
} else {
permissionStatus = await Permission.storage.request().isGranted;
}
if (permissionStatus) {
log('Izin penyimpanan sudah diberikan.');
} else {
if (deviceInfo.version.sdkInt > 32) {
log('deviceInfo.version.sdkInt > 32.');
permissionStatus = await Permission.photos.request().isGranted;
} else {
permissionStatus = await Permission.storage.request().isGranted;
}
// } else {
// openAppSettings();
// }
}
log('permissionStatus: $permissionStatus');
return permissionStatus;
}
void permessionPrinter() async {
Map<Permission, PermissionStatus> statuses = await [
Permission.bluetooth,
Permission.bluetoothScan,
Permission.bluetoothAdvertise,
Permission.bluetoothConnect,
].request();
log("statuses: $statuses");
}
}
+157
View File
@@ -0,0 +1,157 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_esc_pos_network/flutter_esc_pos_network.dart';
import 'package:print_bluetooth_thermal/print_bluetooth_thermal.dart';
import 'package:enaklo_pos/data/models/response/print_model.dart';
class PrinterService {
static final PrinterService _instance = PrinterService._internal();
factory PrinterService() => _instance;
PrinterService._internal();
/// Connect to Bluetooth printer
Future<bool> connectBluetoothPrinter(String macAddress) async {
try {
// Check if already connected
bool isConnected = await PrintBluetoothThermal.connectionStatus;
if (isConnected) {
log("Already connected to Bluetooth printer");
return true;
}
// Connect to the printer
bool connected = await PrintBluetoothThermal.connect(
macPrinterAddress: macAddress);
if (connected) {
log("Successfully connected to Bluetooth printer: $macAddress");
} else {
log("Failed to connect to Bluetooth printer: $macAddress");
}
return connected;
} catch (e) {
log("Error connecting to Bluetooth printer: $e");
return false;
}
}
/// Print using Bluetooth printer
Future<bool> printBluetooth(List<int> printData) async {
try {
bool isConnected = await PrintBluetoothThermal.connectionStatus;
if (!isConnected) {
log("Not connected to Bluetooth printer");
return false;
}
bool printResult = await PrintBluetoothThermal.writeBytes(printData);
if (printResult) {
log("Successfully printed via Bluetooth");
} else {
log("Failed to print via Bluetooth");
}
return printResult;
} catch (e) {
log("Error printing via Bluetooth: $e");
return false;
}
}
/// Print using Network printer
Future<bool> printNetwork(String ipAddress, List<int> printData) async {
try {
final printer = PrinterNetworkManager(ipAddress);
PosPrintResult connect = await printer.connect();
if (connect == PosPrintResult.success) {
PosPrintResult printing = await printer.printTicket(printData);
printer.disconnect();
if (printing == PosPrintResult.success) {
log("Successfully printed via Network printer: $ipAddress");
return true;
} else {
log("Failed to print via Network printer: ${printing.msg}");
return false;
}
} else {
log("Failed to connect to Network printer: ${connect.msg}");
return false;
}
} catch (e) {
log("Error printing via Network: $e");
return false;
}
}
/// Print with automatic printer type detection
Future<bool> printWithPrinter(PrintModel printer, List<int> printData, BuildContext context) async {
try {
if (printer.type == 'Bluetooth') {
bool connected = await connectBluetoothPrinter(printer.address);
if (!connected) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to connect to ${printer.name}')),
);
return false;
}
bool printResult = await printBluetooth(printData);
if (!printResult) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to print to ${printer.name}')),
);
}
return printResult;
} else {
bool printResult = await printNetwork(printer.address, printData);
if (!printResult) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to print to ${printer.name}')),
);
}
return printResult;
}
} catch (e) {
log("Error printing with printer ${printer.name}: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error printing to ${printer.name}: $e')),
);
return false;
}
}
/// Disconnect from Bluetooth printer
Future<bool> disconnectBluetooth() async {
try {
bool result = await PrintBluetoothThermal.disconnect;
log("Bluetooth printer disconnected: $result");
return result;
} catch (e) {
log("Error disconnecting Bluetooth printer: $e");
return false;
}
}
/// Check if Bluetooth is enabled
Future<bool> isBluetoothEnabled() async {
try {
return await PrintBluetoothThermal.bluetoothEnabled;
} catch (e) {
log("Error checking Bluetooth status: $e");
return false;
}
}
/// Get paired Bluetooth devices
Future<List<BluetoothInfo>> getPairedBluetoothDevices() async {
try {
return await PrintBluetoothThermal.pairedBluetooths;
} catch (e) {
log("Error getting paired Bluetooth devices: $e");
return [];
}
}
}
+223
View File
@@ -0,0 +1,223 @@
import 'dart:io';
import 'dart:developer';
import 'package:enaklo_pos/core/extensions/date_time_ext.dart';
import 'package:enaklo_pos/core/extensions/int_ext.dart';
import 'package:enaklo_pos/data/models/response/summary_response_model.dart';
import 'package:flutter/services.dart';
import 'package:enaklo_pos/core/utils/helper_pdf_service.dart';
import 'package:pdf/widgets.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:flutter/foundation.dart';
class RevenueInvoice {
static late Font ttf;
static Future<File> generate(
SummaryModel summaryModel,
String searchDateFormatted,
) async {
try {
log("Starting PDF generation for summary report");
log("Summary model: ${summaryModel.toMap()}");
log("Search date formatted: $searchDateFormatted");
final pdf = Document();
log("PDF document created");
// Load logo image
log("Loading logo image...");
final ByteData dataImage = await rootBundle.load('assets/images/logo.png');
final Uint8List bytes = dataImage.buffer.asUint8List();
final image = pw.MemoryImage(bytes);
log("Logo image loaded successfully, size: ${bytes.length} bytes");
log("Adding page to PDF...");
pdf.addPage(
MultiPage(
build: (context) => [
buildHeader(summaryModel, image, searchDateFormatted),
SizedBox(height: 1 * PdfPageFormat.cm),
buildTotal(summaryModel),
],
footer: (context) => buildFooter(summaryModel),
),
);
log("PDF page added successfully");
log("Saving PDF document...");
return HelperPdfService.saveDocument(
name:
'Enaklo POS | Summary Sales Report | ${DateTime.now().millisecondsSinceEpoch}.pdf',
pdf: pdf,
);
} catch (e) {
log("Error generating PDF: $e");
log("Error stack trace: ${StackTrace.current}");
return Future.error("Failed to generate PDF: $e");
}
}
static Widget buildHeader(
SummaryModel invoice,
MemoryImage image,
String searchDateFormatted,
) =>
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 1 * PdfPageFormat.cm),
Text('Enaklo POS | Summary Sales Report',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
)),
SizedBox(height: 0.2 * PdfPageFormat.cm),
Text(
"Data: $searchDateFormatted",
),
Text(
'Created At: ${DateTime.now().toFormattedDate3()}',
),
],
),
Image(
image,
width: 80.0,
height: 80.0,
fit: BoxFit.fill,
),
]);
static Widget buildTotal(SummaryModel summaryModel) {
log("Building total section with summary model: ${summaryModel.toMap()}");
// Helper function to safely parse string to int
int safeParseInt(String? value) {
if (value == null || value.isEmpty) return 0;
try {
return int.parse(value.replaceAll('.00', ''));
} catch (e) {
log("Error parsing value '$value' to int: $e");
return 0;
}
}
return Container(
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildText(
title: 'Revenue',
value: safeParseInt(summaryModel.totalRevenue).currencyFormatRp,
unite: true,
),
Divider(),
buildText(
title: 'Sub Total',
titleStyle: TextStyle(fontWeight: FontWeight.normal),
value: safeParseInt(summaryModel.totalSubtotal).currencyFormatRp,
unite: true,
),
buildText(
title: 'Discount',
titleStyle: TextStyle(fontWeight: FontWeight.normal),
value: "- ${safeParseInt(summaryModel.totalDiscount).currencyFormatRp}",
unite: true,
textStyle: TextStyle(
color: PdfColor.fromHex('#FF0000'),
fontWeight: FontWeight.bold,
),
),
buildText(
title: 'Tax',
titleStyle: TextStyle(fontWeight: FontWeight.normal),
value: "- ${safeParseInt(summaryModel.totalTax).currencyFormatRp}",
textStyle: TextStyle(
color: PdfColor.fromHex('#FF0000'),
fontWeight: FontWeight.bold,
),
unite: true,
),
buildText(
title: 'Service Charge',
titleStyle: TextStyle(
fontWeight: FontWeight.normal,
),
value: safeParseInt(summaryModel.totalServiceCharge).currencyFormatRp,
unite: true,
),
Divider(),
buildText(
title: 'Total ',
titleStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
),
value: (summaryModel.total ?? 0).currencyFormatRp,
unite: true,
),
SizedBox(height: 2 * PdfPageFormat.mm),
Container(height: 1, color: PdfColors.grey400),
SizedBox(height: 0.5 * PdfPageFormat.mm),
Container(height: 1, color: PdfColors.grey400),
],
),
);
}
static Widget buildFooter(SummaryModel summaryModel) => Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Divider(),
SizedBox(height: 2 * PdfPageFormat.mm),
buildSimpleText(
title: 'Address',
value:
'Jalan Melati No. 12, Mranggen, Demak, Central Java, 89568'),
SizedBox(height: 1 * PdfPageFormat.mm),
],
);
static buildSimpleText({
required String title,
required String value,
}) {
final style = TextStyle(fontWeight: FontWeight.bold);
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: pw.CrossAxisAlignment.end,
children: [
Text(title, style: style),
SizedBox(width: 2 * PdfPageFormat.mm),
Text(value),
],
);
}
static buildText({
required String title,
required String value,
double width = double.infinity,
TextStyle? titleStyle,
TextStyle? textStyle,
bool unite = false,
}) {
final style = titleStyle ?? TextStyle(fontWeight: FontWeight.bold);
final style2 = textStyle ?? TextStyle(fontWeight: FontWeight.bold);
return Container(
width: width,
child: Row(
children: [
Expanded(child: Text(title, style: style)),
Text(value, style: style2),
],
),
);
}
}
@@ -0,0 +1,161 @@
import 'dart:io';
import 'package:enaklo_pos/core/extensions/date_time_ext.dart';
import 'package:enaklo_pos/core/extensions/int_ext.dart';
import 'package:flutter/services.dart';
import 'package:enaklo_pos/core/utils/helper_pdf_service.dart';
import 'package:enaklo_pos/data/models/response/order_remote_datasource.dart';
import 'package:pdf/widgets.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
class TransactionSalesInvoice {
static late Font ttf;
static Future<File> generate(
List<ItemOrder> itemOrders, String searchDateFormatted) async {
final pdf = Document();
// var data = await rootBundle.load("assets/fonts/noto-sans.ttf");
// ttf = Font.ttf(data);
final ByteData dataImage = await rootBundle.load('assets/images/logo.png');
final Uint8List bytes = dataImage.buffer.asUint8List();
// Membuat objek Image dari gambar
final image = pw.MemoryImage(bytes);
pdf.addPage(
MultiPage(
build: (context) => [
buildHeader(image, searchDateFormatted),
SizedBox(height: 1 * PdfPageFormat.cm),
buildInvoice(itemOrders),
Divider(),
SizedBox(height: 0.25 * PdfPageFormat.cm),
],
footer: (context) => buildFooter(),
),
);
return HelperPdfService.saveDocument(
name:
'Enaklo POS | Transaction Sales Report | ${DateTime.now().millisecondsSinceEpoch}.pdf',
pdf: pdf);
}
static Widget buildHeader(MemoryImage image, String searchDateFormatted) =>
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 1 * PdfPageFormat.cm),
Text('Enaklo POS | Transaction Sales Report',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
)),
SizedBox(height: 0.2 * PdfPageFormat.cm),
Text(
"Data: $searchDateFormatted",
),
Text(
'Created At: ${DateTime.now().toFormattedDate3()}',
),
],
),
Image(
image,
width: 80.0,
height: 80.0,
fit: BoxFit.fill,
),
]);
static Widget buildInvoice(List<ItemOrder> itemOrders) {
final headers = [
'Total',
'Sub Total',
'Tax',
'Discount',
'Service',
'Time'
];
final data = itemOrders.map((item) {
return [
item.total!.currencyFormatRp,
item.subTotal!.currencyFormatRp,
item.tax!.currencyFormatRp,
int.parse(item.discountAmount!.replaceAll('.00', '')).currencyFormatRp,
item.serviceCharge!.currencyFormatRp,
item.transactionTime!.toFormattedDate2(),
];
}).toList();
return Table.fromTextArray(
headers: headers,
data: data,
border: null,
headerStyle: TextStyle(
fontWeight: FontWeight.bold, color: PdfColor.fromHex('FFFFFF')),
headerDecoration: BoxDecoration(color: PdfColors.blue),
cellHeight: 30,
cellAlignments: {
0: Alignment.center,
1: Alignment.center,
2: Alignment.center,
3: Alignment.center,
4: Alignment.center,
5: Alignment.center,
},
);
}
static Widget buildFooter() => Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Divider(),
SizedBox(height: 2 * PdfPageFormat.mm),
buildSimpleText(
title: 'Address',
value:
'Jalan Melati No. 12, Mranggen, Demak, Central Java, 89568'),
SizedBox(height: 1 * PdfPageFormat.mm),
],
);
static buildSimpleText({
required String title,
required String value,
}) {
final style = TextStyle(fontWeight: FontWeight.bold);
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: pw.CrossAxisAlignment.end,
children: [
Text(title, style: style),
SizedBox(width: 2 * PdfPageFormat.mm),
Text(value),
],
);
}
static buildText({
required String title,
required String value,
double width = double.infinity,
TextStyle? titleStyle,
bool unite = false,
}) {
final style = titleStyle ?? TextStyle(fontWeight: FontWeight.bold);
return Container(
width: width,
child: Row(
children: [
Expanded(child: Text(title, style: style)),
Text(value, style: unite ? style : null),
],
),
);
}
}