106 lines
4.0 KiB
Dart
106 lines
4.0 KiB
Dart
import 'dart:typed_data';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:pdf/pdf.dart';
|
|
import 'package:pdf/widgets.dart' as pw;
|
|
import 'package:printing/printing.dart';
|
|
import 'package:intl/intl.dart';
|
|
import '../models/user_profile.dart';
|
|
import '../models/cycle_entry.dart';
|
|
|
|
class PdfService {
|
|
static Future<void> generateCycleReport(UserProfile? user, List<CycleEntry> entries) async {
|
|
final pdf = pw.Document();
|
|
final font = await PdfGoogleFonts.outfitRegular();
|
|
final boldFont = await PdfGoogleFonts.outfitBold();
|
|
|
|
// Group entries by month
|
|
final entriesByMonth = <String, List<CycleEntry>>{};
|
|
for (var entry in entries) {
|
|
final month = DateFormat('MMMM yyyy').format(entry.date);
|
|
if (!entriesByMonth.containsKey(month)) {
|
|
entriesByMonth[month] = [];
|
|
}
|
|
entriesByMonth[month]!.add(entry);
|
|
}
|
|
|
|
pdf.addPage(
|
|
pw.MultiPage(
|
|
pageFormat: PdfPageFormat.a4,
|
|
theme: pw.ThemeData.withFont(
|
|
base: font,
|
|
bold: boldFont,
|
|
),
|
|
build: (pw.Context context) {
|
|
return [
|
|
pw.Header(
|
|
level: 0,
|
|
child: pw.Row(
|
|
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
pw.Text('Cycle Report', style: pw.TextStyle(fontSize: 24, fontWeight: pw.FontWeight.bold)),
|
|
pw.Text(DateFormat.yMMMd().format(DateTime.now()), style: const pw.TextStyle(color: PdfColors.grey)),
|
|
],
|
|
),
|
|
),
|
|
if (user != null)
|
|
pw.Padding(
|
|
padding: const pw.EdgeInsets.only(bottom: 20),
|
|
child: pw.Column(
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
|
children: [
|
|
pw.Text('Name: ${user.name}'),
|
|
pw.Text('Average Cycle Length: ${user.averageCycleLength} days'),
|
|
pw.Text('Average Period Length: ${user.averagePeriodLength} days'),
|
|
],
|
|
),
|
|
),
|
|
|
|
...entriesByMonth.entries.map((entry) {
|
|
final month = entry.key;
|
|
final monthEntries = entry.value;
|
|
// Sort by date
|
|
monthEntries.sort((a, b) => a.date.compareTo(b.date));
|
|
|
|
return pw.Column(
|
|
crossAxisAlignment: pw.CrossAxisAlignment.start,
|
|
children: [
|
|
pw.SizedBox(height: 10),
|
|
pw.Text(month, style: pw.TextStyle(fontSize: 18, fontWeight: pw.FontWeight.bold, color: PdfColors.blueGrey800)),
|
|
pw.SizedBox(height: 5),
|
|
pw.Table.fromTextArray(
|
|
context: context,
|
|
headerStyle: pw.TextStyle(fontWeight: pw.FontWeight.bold),
|
|
headers: ['Date', 'Phase', 'Details', 'Notes'],
|
|
data: monthEntries.map((e) {
|
|
final details = <String>[];
|
|
if (e.isPeriodDay) details.add('Period');
|
|
if (e.mood != null) details.add('Mood: ${e.mood!.label}');
|
|
if (e.symptomCount > 0) details.add('${e.symptomCount} symptoms');
|
|
|
|
return [
|
|
DateFormat('d, E').format(e.date),
|
|
'${e.isPeriodDay ? "Menstrual" : "-"}', // Simplified for report
|
|
details.join(', '),
|
|
e.notes ?? '',
|
|
];
|
|
}).toList(),
|
|
columnWidths: {
|
|
0: const pw.FlexColumnWidth(1),
|
|
1: const pw.FlexColumnWidth(1),
|
|
2: const pw.FlexColumnWidth(2),
|
|
3: const pw.FlexColumnWidth(2),
|
|
},
|
|
),
|
|
pw.SizedBox(height: 15),
|
|
],
|
|
);
|
|
}),
|
|
];
|
|
},
|
|
),
|
|
);
|
|
|
|
await Printing.sharePdf(bytes: await pdf.save(), filename: 'cycle_report.pdf');
|
|
}
|
|
}
|