import 'dart:io'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import '../models/fuel_entry.dart'; import '../services/app_state.dart'; import '../services/ocr_service.dart'; import '../services/receipt_parser.dart'; import 'add_edit_vehicle_screen.dart'; import 'confirm_fuel_entry_screen.dart'; import 'receipt_image_screen.dart'; class VehicleDetailScreen extends StatelessWidget { final String vehicleId; const VehicleDetailScreen({super.key, required this.vehicleId}); Future _captureReceipt(BuildContext context) async { final picker = ImagePicker(); XFile? photo; try { photo = await picker.pickImage(source: ImageSource.camera, imageQuality: 85); } catch (e) { if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Could not open camera: $e')), ); } return; } if (photo == null) return; final imageFile = File(photo.path); if (!context.mounted) return; showDialog( context: context, barrierDismissible: false, builder: (_) => const Center(child: CircularProgressIndicator()), ); final ocrService = OcrService(); String recognizedText = ''; try { recognizedText = await ocrService.recognizeText(imageFile); } catch (_) { recognizedText = ''; } finally { ocrService.dispose(); } final parsed = ReceiptParser.parse(recognizedText); if (context.mounted) { Navigator.of(context).pop(); Navigator.of(context).push( MaterialPageRoute( builder: (_) => ConfirmFuelEntryScreen( vehicleId: vehicleId, imageFile: imageFile, parsed: parsed, ), ), ); } } Future _deleteEntry(BuildContext context, FuelEntry entry) async { final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Delete fuel entry?'), content: const Text('This will remove the entry and its receipt photo.'), actions: [ TextButton(onPressed: () => Navigator.of(context).pop(false), child: const Text('Cancel')), TextButton( onPressed: () => Navigator.of(context).pop(true), child: const Text('Delete', style: TextStyle(color: Colors.red)), ), ], ), ); if (confirmed == true && context.mounted) { await context.read().deleteFuelEntry(entry.id); } } @override Widget build(BuildContext context) { final appState = context.watch(); final vehicle = appState.vehicleById(vehicleId); if (vehicle == null) { return const Scaffold(body: Center(child: Text('Vehicle not found'))); } final entries = appState.entriesForVehicle(vehicleId); final totalGallons = appState.totalGallonsForVehicle(vehicleId); final dateFormat = DateFormat.yMMMd().add_jm(); final currencyFormat = NumberFormat.simpleCurrency(); return Scaffold( appBar: AppBar( title: Text(vehicle.displayName), actions: [ IconButton( icon: const Icon(Icons.edit_outlined), tooltip: 'Edit vehicle', onPressed: () => Navigator.of(context).push( MaterialPageRoute(builder: (_) => AddEditVehicleScreen(vehicle: vehicle)), ), ), ], ), body: Column( children: [ Container( width: double.infinity, padding: const EdgeInsets.all(16), color: Theme.of(context).colorScheme.surfaceContainerHighest, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Total gallons logged', style: Theme.of(context).textTheme.labelLarge), Text( '${totalGallons.toStringAsFixed(3)} gal', style: Theme.of(context).textTheme.headlineMedium, ), ], ), ), Expanded( child: entries.isEmpty ? const Center(child: Text('No fuel entries yet. Tap the camera button to log a receipt.')) : ListView.separated( padding: const EdgeInsets.all(12), itemCount: entries.length, separatorBuilder: (_, _) => const SizedBox(height: 8), itemBuilder: (context, index) { final entry = entries[index]; return Card( clipBehavior: Clip.antiAlias, child: ListTile( leading: entry.receiptImagePath != null ? GestureDetector( onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (_) => ReceiptImageScreen(imagePath: entry.receiptImagePath!), ), ), child: ClipRRect( borderRadius: BorderRadius.circular(6), child: Image.file( File(entry.receiptImagePath!), width: 48, height: 48, fit: BoxFit.cover, ), ), ) : const CircleAvatar(child: Icon(Icons.receipt_long)), title: Text('${entry.gallons.toStringAsFixed(3)} gal • ${currencyFormat.format(entry.totalCost)}'), subtitle: Text( '${currencyFormat.format(entry.pricePerGallon)}/gal\n${dateFormat.format(entry.date)}', ), isThreeLine: true, trailing: IconButton( icon: const Icon(Icons.delete_outline), onPressed: () => _deleteEntry(context, entry), ), ), ); }, ), ), ], ), floatingActionButton: FloatingActionButton.extended( onPressed: () => _captureReceipt(context), icon: const Icon(Icons.camera_alt_outlined), label: const Text('Log Fuel Receipt'), ), ); } }