129 lines
3.5 KiB
Dart
129 lines
3.5 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:uuid/uuid.dart';
|
|
|
|
import '../models/fuel_entry.dart';
|
|
import '../models/vehicle.dart';
|
|
import 'storage_service.dart';
|
|
|
|
/// Single source of truth for the app's in-memory data (vehicles + fuel
|
|
/// entries), backed by [StorageService] for persistence. Screens read from
|
|
/// this via Provider and call its mutating methods, which take care of
|
|
/// writing through to disk and notifying listeners.
|
|
class AppState extends ChangeNotifier {
|
|
final StorageService storage = StorageService();
|
|
final _uuid = const Uuid();
|
|
|
|
List<Vehicle> vehicles = [];
|
|
List<FuelEntry> fuelEntries = [];
|
|
bool isLoading = true;
|
|
|
|
Future<void> init() async {
|
|
await storage.init();
|
|
final data = await storage.loadData();
|
|
vehicles = data.vehicles;
|
|
fuelEntries = data.entries;
|
|
isLoading = false;
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> addVehicle({
|
|
required String make,
|
|
required String model,
|
|
required String color,
|
|
required String licensePlate,
|
|
}) async {
|
|
vehicles.add(Vehicle(
|
|
id: _uuid.v4(),
|
|
make: make,
|
|
model: model,
|
|
color: color,
|
|
licensePlate: licensePlate,
|
|
));
|
|
await _persist();
|
|
}
|
|
|
|
Future<void> updateVehicle(Vehicle updated) async {
|
|
final index = vehicles.indexWhere((v) => v.id == updated.id);
|
|
if (index != -1) {
|
|
vehicles[index] = updated;
|
|
await _persist();
|
|
}
|
|
}
|
|
|
|
Future<void> deleteVehicle(String vehicleId) async {
|
|
for (final entry in fuelEntries.where((e) => e.vehicleId == vehicleId)) {
|
|
await storage.deleteReceiptImage(entry.receiptImagePath);
|
|
}
|
|
fuelEntries.removeWhere((e) => e.vehicleId == vehicleId);
|
|
vehicles.removeWhere((v) => v.id == vehicleId);
|
|
await _persist();
|
|
}
|
|
|
|
Future<FuelEntry> addFuelEntry({
|
|
required String vehicleId,
|
|
required DateTime date,
|
|
required double gallons,
|
|
required double pricePerGallon,
|
|
required double totalCost,
|
|
File? receiptImage,
|
|
}) async {
|
|
final id = _uuid.v4();
|
|
String? storedImagePath;
|
|
if (receiptImage != null) {
|
|
storedImagePath = await storage.storeReceiptImage(receiptImage, id);
|
|
}
|
|
|
|
final entry = FuelEntry(
|
|
id: id,
|
|
vehicleId: vehicleId,
|
|
date: date,
|
|
gallons: gallons,
|
|
pricePerGallon: pricePerGallon,
|
|
totalCost: totalCost,
|
|
receiptImagePath: storedImagePath,
|
|
);
|
|
fuelEntries.add(entry);
|
|
await _persist();
|
|
return entry;
|
|
}
|
|
|
|
Future<void> deleteFuelEntry(String entryId) async {
|
|
final entry = fuelEntries.firstWhere((e) => e.id == entryId);
|
|
await storage.deleteReceiptImage(entry.receiptImagePath);
|
|
fuelEntries.removeWhere((e) => e.id == entryId);
|
|
await _persist();
|
|
}
|
|
|
|
List<FuelEntry> entriesForVehicle(String vehicleId) {
|
|
final list = fuelEntries.where((e) => e.vehicleId == vehicleId).toList();
|
|
list.sort((a, b) => b.date.compareTo(a.date));
|
|
return list;
|
|
}
|
|
|
|
double totalGallonsForVehicle(String vehicleId) => fuelEntries
|
|
.where((e) => e.vehicleId == vehicleId)
|
|
.fold(0.0, (sum, e) => sum + e.gallons);
|
|
|
|
double get totalGallonsAllVehicles =>
|
|
fuelEntries.fold(0.0, (sum, e) => sum + e.gallons);
|
|
|
|
Vehicle? vehicleById(String id) {
|
|
try {
|
|
return vehicles.firstWhere((v) => v.id == id);
|
|
} catch (_) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Future<void> changeSaveDirectory(String newPath) async {
|
|
await storage.setSaveDirectory(newPath);
|
|
notifyListeners();
|
|
}
|
|
|
|
Future<void> _persist() async {
|
|
await storage.saveData(vehicles: vehicles, entries: fuelEntries);
|
|
notifyListeners();
|
|
}
|
|
}
|