import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:printing/printing.dart'; import 'package:provider/provider.dart'; import '../services/app_state.dart'; import '../services/fuel_report.dart'; import '../services/fuel_report_pdf.dart'; class ReportScreen extends StatefulWidget { const ReportScreen({super.key}); @override State createState() => _ReportScreenState(); } class _ReportScreenState extends State { static final _dateFormat = DateFormat.yMMMd(); DateTime? _startDate; DateTime? _endDate; bool _busy = false; Future _pickDate({required bool isStart}) async { final now = DateTime.now(); final picked = await showDatePicker( context: context, initialDate: (isStart ? _startDate : _endDate) ?? now, firstDate: DateTime(2000), lastDate: now, ); if (picked == null) return; setState(() { if (isStart) { _startDate = picked; } else { _endDate = picked; } }); } Future _share(FuelReport report) async { setState(() => _busy = true); try { final bytes = await buildFuelReportPdf(report); await Printing.sharePdf(bytes: bytes, filename: fuelReportFileName(report)); } finally { if (mounted) setState(() => _busy = false); } } Future _print(FuelReport report) async { setState(() => _busy = true); try { final bytes = await buildFuelReportPdf(report); await Printing.layoutPdf(onLayout: (_) async => bytes, name: fuelReportFileName(report)); } finally { if (mounted) setState(() => _busy = false); } } @override Widget build(BuildContext context) { final appState = context.watch(); String? rangeError; FuelReport? report; if (_startDate != null && _endDate != null) { if (_endDate!.isBefore(_startDate!)) { rangeError = 'End date must be on or after the start date.'; } else { report = buildFuelReport( vehicles: appState.vehicles, fuelEntries: appState.fuelEntries, startDate: _startDate!, endDate: _endDate!, ); } } return Scaffold( appBar: AppBar(title: const Text('Fuel Report')), body: ListView( padding: const EdgeInsets.all(16), children: [ Row( children: [ Expanded( child: OutlinedButton( onPressed: () => _pickDate(isStart: true), child: Text(_startDate == null ? 'Start date' : _dateFormat.format(_startDate!)), ), ), const SizedBox(width: 12), Expanded( child: OutlinedButton( onPressed: () => _pickDate(isStart: false), child: Text(_endDate == null ? 'End date' : _dateFormat.format(_endDate!)), ), ), ], ), if (rangeError != null) ...[ const SizedBox(height: 8), Text(rangeError, style: TextStyle(color: Theme.of(context).colorScheme.error)), ], const SizedBox(height: 16), if (report == null) const Padding( padding: EdgeInsets.symmetric(vertical: 24), child: Text('Choose a start and end date to generate a report.'), ) else if (report.rows.isEmpty) const Padding( padding: EdgeInsets.symmetric(vertical: 24), child: Text('No vehicles had a receipted fuel entry in that date range.'), ) else ...[ for (final row in report.rows) Card( child: ListTile( title: Text(row.vehicle.displayLabel), subtitle: Text( 'VIN: ${row.vehicle.vin}\n${row.entryCount} receipted ' 'entr${row.entryCount == 1 ? 'y' : 'ies'}', ), isThreeLine: true, trailing: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [ Text('${row.totalGallons.toStringAsFixed(3)} gal'), Text(NumberFormat.simpleCurrency().format(row.totalCost)), ], ), ), ), const Divider(height: 32), ListTile( title: const Text('Total', style: TextStyle(fontWeight: FontWeight.bold)), trailing: Text( '${report.totalGallons.toStringAsFixed(3)} gal ยท ' '${NumberFormat.simpleCurrency().format(report.totalCost)}', style: const TextStyle(fontWeight: FontWeight.bold), ), ), const SizedBox(height: 16), Row( children: [ Expanded( child: FilledButton.icon( onPressed: _busy ? null : () => _share(report!), icon: const Icon(Icons.share_outlined), label: const Text('Share'), ), ), const SizedBox(width: 12), Expanded( child: OutlinedButton.icon( onPressed: _busy ? null : () => _print(report!), icon: const Icon(Icons.print_outlined), label: const Text('Print'), ), ), ], ), ], ], ), ); } }