Added report functionality and Dropbox connection

This commit is contained in:
Courtney Arnold 2026-08-13 17:43:42 -05:00
parent 483fbeafb6
commit c7494eda92
8 changed files with 614 additions and 2 deletions

View file

@ -0,0 +1,172 @@
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<ReportScreen> createState() => _ReportScreenState();
}
class _ReportScreenState extends State<ReportScreen> {
static final _dateFormat = DateFormat.yMMMd();
DateTime? _startDate;
DateTime? _endDate;
bool _busy = false;
Future<void> _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<void> _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<void> _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<AppState>();
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'),
),
),
],
),
],
],
),
);
}
}