Release version 1.0.0
This commit is contained in:
196
lib/screens/home_screen.dart
Normal file
196
lib/screens/home_screen.dart
Normal file
@@ -0,0 +1,196 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../models/refuel.dart';
|
||||
import '../services/session_manager.dart';
|
||||
import '../widgets/stat_card.dart';
|
||||
import '../widgets/gas_price_chart.dart';
|
||||
import '../widgets/consumption_chart.dart';
|
||||
|
||||
class HomeScreen extends StatelessWidget {
|
||||
const HomeScreen({super.key});
|
||||
|
||||
String _formatDouble(double value) {
|
||||
var s = value.toStringAsFixed(2);
|
||||
if (s.endsWith('.00')) {
|
||||
s = s.substring(0, s.length - 3);
|
||||
} else if (s.endsWith('0')) {
|
||||
s = s.substring(0, s.length - 1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
double? _allTimeConsumption(List<Refuel> refuels) {
|
||||
if (refuels.length < 2) return null;
|
||||
final distance = refuels.last.mileage - refuels.first.mileage;
|
||||
if (distance <= 0) return null;
|
||||
final liters =
|
||||
refuels.skip(1).fold<double>(0.0, (sum, r) => sum + r.liters);
|
||||
return liters / distance * 100;
|
||||
}
|
||||
|
||||
double? _lastConsumption(List<Refuel> refuels) {
|
||||
if (refuels.length < 2) return null;
|
||||
final last = refuels[refuels.length - 1];
|
||||
final prev = refuels[refuels.length - 2];
|
||||
final distance = last.mileage - prev.mileage;
|
||||
if (distance <= 0) return null;
|
||||
return last.liters / distance * 100;
|
||||
}
|
||||
|
||||
int? _kmSinceLast(List<Refuel> refuels) {
|
||||
if (refuels.length < 2) return null;
|
||||
return refuels.last.mileage - refuels[refuels.length - 2].mileage;
|
||||
}
|
||||
|
||||
int _kmForPeriod(List<Refuel> refuels, DateTime from) {
|
||||
final list = refuels
|
||||
.where((r) => r.createdAt != null && r.createdAt!.isAfter(from))
|
||||
.toList();
|
||||
if (list.length < 2) return 0;
|
||||
return list.last.mileage - list.first.mileage;
|
||||
}
|
||||
|
||||
int _kmAllTime(List<Refuel> refuels) {
|
||||
if (refuels.length < 2) return 0;
|
||||
return refuels.last.mileage - refuels.first.mileage;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<SessionManager>(
|
||||
builder: (context, session, _) {
|
||||
final vehicle = session.defaultVehicle;
|
||||
if (vehicle == null) {
|
||||
return const Scaffold(
|
||||
body: Center(child: Text('No default vehicle selected')),
|
||||
);
|
||||
}
|
||||
|
||||
final refuels = session.refuels
|
||||
.where((r) => r.vehicleId == vehicle.id)
|
||||
.toList();
|
||||
refuels.sort((a, b) => a.mileage.compareTo(b.mileage));
|
||||
|
||||
final allCons = _allTimeConsumption(refuels);
|
||||
final lastCons = _lastConsumption(refuels);
|
||||
final kmLast = _kmSinceLast(refuels);
|
||||
final now = DateTime.now();
|
||||
final km1m = _kmForPeriod(refuels, now.subtract(const Duration(days: 30)));
|
||||
final km6m =
|
||||
_kmForPeriod(refuels, now.subtract(const Duration(days: 182)));
|
||||
final km1y =
|
||||
_kmForPeriod(refuels, now.subtract(const Duration(days: 365)));
|
||||
final kmAll = _kmAllTime(refuels);
|
||||
|
||||
final lastRefuels =
|
||||
refuels.length > 14 ? refuels.sublist(refuels.length - 14) : refuels;
|
||||
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 32),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Card(
|
||||
color: Theme.of(context).colorScheme.secondaryContainer,
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.directions_car),
|
||||
title: Text(vehicle.name,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
subtitle: Text(vehicle.registrationPlate),
|
||||
trailing:
|
||||
const Icon(Icons.star, color: Colors.amber),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text('Refuel stats',
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 8),
|
||||
GridView.count(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 8,
|
||||
mainAxisSpacing: 8,
|
||||
childAspectRatio: 2,
|
||||
children: [
|
||||
StatCard(
|
||||
title: 'Avg consumption (all time)',
|
||||
value: allCons != null
|
||||
? '${_formatDouble(allCons)} L/100km'
|
||||
: '-',
|
||||
),
|
||||
StatCard(
|
||||
title: 'Since last refuel',
|
||||
value: lastCons != null
|
||||
? '${_formatDouble(lastCons)} L/100km'
|
||||
: '-',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const Text('Kilometers driven',
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
Builder(builder: (context) {
|
||||
final kmCards = <Widget>[
|
||||
StatCard(
|
||||
title: 'Since last refuel',
|
||||
value: kmLast != null ? '$kmLast km' : '-',
|
||||
),
|
||||
StatCard(title: 'Past month', value: '$km1m km'),
|
||||
StatCard(title: 'Past 6 months', value: '$km6m km'),
|
||||
StatCard(title: 'Past year', value: '$km1y km'),
|
||||
StatCard(title: 'All time', value: '$kmAll km'),
|
||||
];
|
||||
if (kmCards.length.isOdd) {
|
||||
kmCards.add(const SizedBox.shrink());
|
||||
}
|
||||
return GridView.count(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 8,
|
||||
mainAxisSpacing: 8,
|
||||
childAspectRatio: 2,
|
||||
children: kmCards,
|
||||
);
|
||||
}),
|
||||
if (lastRefuels.isNotEmpty) ...[
|
||||
const SizedBox(height: 24),
|
||||
Text('Gas price (last ${lastRefuels.length} refuels)',
|
||||
style: const TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(
|
||||
height: 220,
|
||||
child: GasPriceChart(refuels: lastRefuels),
|
||||
),
|
||||
if (lastRefuels.length > 1) ...[
|
||||
const SizedBox(height: 24),
|
||||
const Text('Consumption trend',
|
||||
style: TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(
|
||||
height: 220,
|
||||
child: ConsumptionChart(refuels: lastRefuels),
|
||||
),
|
||||
],
|
||||
],
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user