pre final ad services and correct call the services async
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_tank_web_app/services/geolocation_service.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:geolocator/geolocator.dart';
|
import 'package:geolocator/geolocator.dart';
|
||||||
import '../models/locationiq_model.dart';
|
import '../models/locationiq_model.dart';
|
||||||
@@ -62,43 +63,26 @@ class EditController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _requestLocationIQ() async {
|
Future<void> _requestLocationIQ() async {
|
||||||
bool serviceEnabled;
|
var geolocationService = GeolocationService();
|
||||||
LocationPermission permission;
|
var locationIQService = LocationIQService();
|
||||||
|
isLoadingLocation.value = true;
|
||||||
try {
|
try {
|
||||||
isLoadingLocation.value = true;
|
await geolocationService.getCurrentLocation();
|
||||||
|
if (geolocationService.hasLocation) {
|
||||||
|
print(
|
||||||
|
'Aktuelle Position: ${geolocationService.latitude}, ${geolocationService.longitude}',
|
||||||
|
);
|
||||||
|
await locationIQService.fetchLocationIQ(
|
||||||
|
geolocationService.latitude,
|
||||||
|
geolocationService.longitude,
|
||||||
|
);
|
||||||
|
|
||||||
// 1. Prüfen, ob Standortdienste aktiviert sind
|
currentLocationIQ = locationIQService.locationIQ;
|
||||||
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
locationController.text =
|
||||||
if (!serviceEnabled) {
|
currentLocationIQ?.address?.shortAddress ?? '';
|
||||||
return Future.error('Standortdienste sind deaktiviert.');
|
} else {
|
||||||
|
throw Exception('Standort nicht verfügbar');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Berechtigungen prüfen
|
|
||||||
permission = await Geolocator.checkPermission();
|
|
||||||
if (permission == LocationPermission.denied) {
|
|
||||||
permission = await Geolocator.requestPermission();
|
|
||||||
if (permission == LocationPermission.denied) {
|
|
||||||
return Future.error('Berechtigung verweigert.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Position abrufen
|
|
||||||
Position position = await Geolocator.getCurrentPosition(
|
|
||||||
locationSettings: const LocationSettings(
|
|
||||||
accuracy: LocationAccuracy.high,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// 4. Standort über Backend-Proxy abrufen
|
|
||||||
var lat = position.latitude;
|
|
||||||
var lon = position.longitude;
|
|
||||||
|
|
||||||
print('📍 Verwende LocationIQ für Geocoding...');
|
|
||||||
final LocationIQService locationIQService = LocationIQService();
|
|
||||||
await locationIQService.fetchLocationIQ(lat, lon);
|
|
||||||
currentLocationIQ = locationIQService.locationIQ;
|
|
||||||
locationController.text = currentLocationIQ?.address?.shortAddress ?? '';
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Get.snackbar(
|
Get.snackbar(
|
||||||
"Fehler",
|
"Fehler",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:geolocator/geolocator.dart';
|
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import '../models/econtrol_model.dart';
|
import '../models/econtrol_model.dart';
|
||||||
|
import '../services/geolocation_service.dart';
|
||||||
import '../services/econtrol_service.dart';
|
import '../services/econtrol_service.dart';
|
||||||
|
|
||||||
class GasstationsController extends GetxController {
|
class GasstationsController extends GetxController {
|
||||||
@@ -23,59 +22,19 @@ class GasstationsController extends GetxController {
|
|||||||
|
|
||||||
Future<void> _loadStationInfosList() async {
|
Future<void> _loadStationInfosList() async {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
bool serviceEnabled;
|
var gelocationService = GeolocationService();
|
||||||
if (eControlData.isNotEmpty) {
|
var eControlService = EControlService();
|
||||||
eControlData.clear();
|
// check if location is available
|
||||||
}
|
await gelocationService.getCurrentLocation();
|
||||||
final eControlService = EControlService();
|
if (gelocationService.hasLocation) {
|
||||||
LocationPermission permission;
|
var listResult = await eControlService.getEControlData(
|
||||||
|
gelocationService.latitude,
|
||||||
try {
|
gelocationService.longitude,
|
||||||
isLoadingLocation.value = true;
|
|
||||||
|
|
||||||
// 1. Prüfen, ob Standortdienste aktiviert sind
|
|
||||||
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
|
||||||
if (!serviceEnabled) {
|
|
||||||
return Future.error('Standortdienste sind deaktiviert.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Berechtigungen prüfen
|
|
||||||
permission = await Geolocator.checkPermission();
|
|
||||||
if (permission == LocationPermission.denied) {
|
|
||||||
permission = await Geolocator.requestPermission();
|
|
||||||
if (permission == LocationPermission.denied) {
|
|
||||||
return Future.error('Berechtigung verweigert.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Position abrufen
|
|
||||||
Position position = await Geolocator.getCurrentPosition(
|
|
||||||
locationSettings: const LocationSettings(
|
|
||||||
accuracy: LocationAccuracy.high,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// 4. Standort über Backend-Proxy abrufen
|
|
||||||
var lat = position.latitude;
|
|
||||||
var lon = position.longitude;
|
|
||||||
// Simulate fetching data from an API or database
|
|
||||||
await eControlService.getEControlData(
|
|
||||||
lat,
|
|
||||||
lon,
|
|
||||||
'DIE',
|
'DIE',
|
||||||
);
|
);
|
||||||
eControlData.addAll(eControlService.eControlData);
|
eControlData.value = listResult
|
||||||
} catch (e) {
|
.map((json) => EControlModel.fromJson(json))
|
||||||
Get.snackbar(
|
.toList();
|
||||||
"Fehler",
|
|
||||||
"Standort konnte nicht abgerufen werden: $e",
|
|
||||||
snackPosition: SnackPosition.BOTTOM,
|
|
||||||
backgroundColor: Colors.red[100],
|
|
||||||
colorText: Colors.red[900],
|
|
||||||
);
|
|
||||||
print("Fehler beim Abrufen des Standorts: $e");
|
|
||||||
} finally {
|
|
||||||
isLoadingLocation.value = false;
|
|
||||||
}
|
}
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
update();
|
update();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import '../controller/gasstations_controller.dart';
|
import '../controller/gasstations_controller.dart';
|
||||||
|
import '../widgets/gasstation_card_widget.dart';
|
||||||
|
|
||||||
class GasstationsPage extends GetView<GasstationsController> {
|
class GasstationsPage extends GetView<GasstationsController> {
|
||||||
static const String namedRoute = '/gasstations-page';
|
static const String namedRoute = '/gasstations-page';
|
||||||
@@ -9,43 +10,161 @@ class GasstationsPage extends GetView<GasstationsController> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var staCtrl = controller;
|
var staCtrl = controller;
|
||||||
return Scaffold(
|
return SafeArea(
|
||||||
appBar: AppBar(
|
child: Scaffold(
|
||||||
toolbarHeight: 100,
|
appBar: AppBar(
|
||||||
backgroundColor: Colors.blueGrey,
|
backgroundColor: Colors.blueGrey[800],
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
title: const Text('Gas Stations'),
|
elevation: 0,
|
||||||
),
|
title: const Row(
|
||||||
body: Container(
|
children: [
|
||||||
decoration: BoxDecoration(
|
Icon(Icons.local_gas_station, size: 28),
|
||||||
gradient: LinearGradient(
|
SizedBox(width: 12),
|
||||||
begin: Alignment.topCenter,
|
Text(
|
||||||
end: Alignment.bottomCenter,
|
'Tankstellen',
|
||||||
colors: [
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 22),
|
||||||
Colors.blueGrey[800]!,
|
),
|
||||||
Colors.blueGrey[600]!,
|
|
||||||
Colors.blueGrey[300]!,
|
|
||||||
Colors.blue[100]!,
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Center(
|
body: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [
|
||||||
|
Colors.blueGrey[800]!,
|
||||||
|
Colors.blueGrey[600]!,
|
||||||
|
Colors.blueGrey[400]!,
|
||||||
|
Colors.blueGrey[200]!,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
if (staCtrl.isLoading.value) {
|
if (staCtrl.isLoading.value) {
|
||||||
return const CircularProgressIndicator();
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const CircularProgressIndicator(
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||||
|
strokeWidth: 3,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
'Lade Tankstellen...',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
} else if (staCtrl.eControlData.isEmpty) {
|
} else if (staCtrl.eControlData.isEmpty) {
|
||||||
return const Text('No gas stations found.');
|
return Center(
|
||||||
|
child: Container(
|
||||||
|
margin: const EdgeInsets.all(24),
|
||||||
|
padding: const EdgeInsets.all(32),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.1),
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: const Offset(0, 5),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.search_off,
|
||||||
|
size: 64,
|
||||||
|
color: Colors.blueGrey[300],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Keine Tankstellen gefunden',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.blueGrey[800],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'Bitte überprüfen Sie Ihre Standortfreigabe',
|
||||||
|
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
return ListView.builder(
|
return Column(
|
||||||
itemCount: staCtrl.eControlData.length,
|
children: [
|
||||||
itemBuilder: (context, index) {
|
// Info Header
|
||||||
final station = staCtrl.eControlData[index];
|
Container(
|
||||||
return ListTile(
|
padding: const EdgeInsets.all(16),
|
||||||
title: Text(station.name ?? 'Unknown Station'),
|
child: Container(
|
||||||
subtitle: Text(station.location?.address ?? 'No address'),
|
padding: const EdgeInsets.symmetric(
|
||||||
trailing: Text(station.open == true ? 'Open' : 'Closed'),
|
horizontal: 20,
|
||||||
);
|
vertical: 12,
|
||||||
},
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white.withOpacity(0.95),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black.withOpacity(0.1),
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: const Offset(0, 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.info_outline,
|
||||||
|
color: Colors.blueGrey[700],
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
'Top 5 von ${staCtrl.eControlData.length} Tankstellen',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.blueGrey[800],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// List
|
||||||
|
Expanded(
|
||||||
|
child: ListView.builder(
|
||||||
|
padding: const EdgeInsets.only(bottom: 16),
|
||||||
|
itemCount: staCtrl.eControlData.length > 5
|
||||||
|
? 5
|
||||||
|
: staCtrl.eControlData.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
return GasStationCard(
|
||||||
|
station: staCtrl.eControlData[index],
|
||||||
|
index: index,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,70 +1,17 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import '../models/econtrol_model.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
class EControlService {
|
class EControlService {
|
||||||
late List<EControlModel> eControlData;
|
Future<List<dynamic>> getEControlData(
|
||||||
|
double latitude,
|
||||||
Future<void> getEControlData(double latitude, double longitude, String fuelType) async {
|
double longitude,
|
||||||
// Simulate fetching data from an API or database
|
String fuelType,
|
||||||
await Future.delayed(Duration(seconds: 2)); // Simulate network delay
|
) async {
|
||||||
eControlData = [
|
|
||||||
EControlModel(
|
|
||||||
id: 1,
|
|
||||||
name: 'E-Control Station 1',
|
|
||||||
location: Location(
|
|
||||||
latitude: 47.93875449671056,
|
|
||||||
longitude: 13.762706553431048,
|
|
||||||
),
|
|
||||||
contact: Contact(
|
|
||||||
telephone: '+43 123 456789',
|
|
||||||
mail: '',
|
|
||||||
website: 'https://www.econtrol.at',
|
|
||||||
),
|
|
||||||
openingHours: [
|
|
||||||
OpeningHours(
|
|
||||||
day: 'Tuesday',
|
|
||||||
label: '08:00',
|
|
||||||
order: 1,
|
|
||||||
from: '08:00',
|
|
||||||
to: '18:00',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
offerInformation: OfferInformation(
|
|
||||||
service: true,
|
|
||||||
selfService: true,
|
|
||||||
unattended: true,
|
|
||||||
),
|
|
||||||
paymentMethods: PaymentMethods(
|
|
||||||
cash: true,
|
|
||||||
debitCard: true,
|
|
||||||
creditCard: false,
|
|
||||||
others: '',
|
|
||||||
),
|
|
||||||
paymentArrangements: PaymentArrangements(
|
|
||||||
cooperative: false,
|
|
||||||
clubCard: true,
|
|
||||||
),
|
|
||||||
position: 1,
|
|
||||||
open: true,
|
|
||||||
distance: 0.5,
|
|
||||||
prices: [Prices(fuelType: 'DIE', amount: 1.445, label: 'Diesel')],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
// REST Service... URL
|
// REST Service... URL
|
||||||
String apiUrl = 'https://api.e-control.at/sprit/1.0/search/gas-stations/by-address?latitude=$latitude&longitude=$longitude&fuelType=$fuelType&includeClosed=false';
|
String apiUrl =
|
||||||
try {
|
'https://api.e-control.at/sprit/1.0/search/gas-stations/by-address?latitude=$latitude&longitude=$longitude&fuelType=$fuelType&includeClosed=false';
|
||||||
var response = await http.get(Uri.parse(apiUrl));
|
var response = await http.get(Uri.parse(apiUrl));
|
||||||
if (response.statusCode == 200) {
|
print('${response.statusCode}');
|
||||||
eControlData.clear(); // Clear existing data before adding new results
|
return jsonDecode(response.body);
|
||||||
// Parse the response and update eControlData
|
|
||||||
print('E-Control API response: ${response.body}');
|
|
||||||
eControlData = (response.body as List).map((json) => EControlModel.fromJson(json)).toList();
|
|
||||||
print('E-Control data parsed successfully: ${eControlData.length} stations found');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
print('Error fetching E-Control data: $e');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
lib/services/geolocation_service.dart
Normal file
54
lib/services/geolocation_service.dart
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
|
|
||||||
|
class GeolocationService {
|
||||||
|
late double latitude;
|
||||||
|
late double longitude;
|
||||||
|
late bool hasLocation;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Future<void> getCurrentLocation() async {
|
||||||
|
|
||||||
|
bool serviceEnabled = false;
|
||||||
|
LocationPermission permission;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Prüfen, ob Standortdienste aktiviert sind
|
||||||
|
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||||
|
if (!serviceEnabled) {
|
||||||
|
return Future.error('Standortdienste sind deaktiviert.');
|
||||||
|
}
|
||||||
|
|
||||||
|
hasLocation = serviceEnabled;
|
||||||
|
|
||||||
|
// 2. Berechtigungen prüfen
|
||||||
|
permission = await Geolocator.checkPermission();
|
||||||
|
if (permission == LocationPermission.denied) {
|
||||||
|
permission = await Geolocator.requestPermission();
|
||||||
|
hasLocation = true;
|
||||||
|
if (permission == LocationPermission.denied) {
|
||||||
|
hasLocation = false;
|
||||||
|
return Future.error('Berechtigung verweigert.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Position abrufen
|
||||||
|
Position position = await Geolocator.getCurrentPosition(
|
||||||
|
locationSettings: const LocationSettings(
|
||||||
|
accuracy: LocationAccuracy.high,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 4. Standort über Backend-Proxy abrufen
|
||||||
|
latitude = position.latitude;
|
||||||
|
longitude = position.longitude;
|
||||||
|
if(latitude > 0 && longitude > 0) {
|
||||||
|
hasLocation = true;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print("Fehler beim Abrufen des Standorts: $e");
|
||||||
|
hasLocation = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,32 +11,6 @@ class LocationIQService {
|
|||||||
|
|
||||||
Future<void> fetchLocationIQ(double lat, double lon) async {
|
Future<void> fetchLocationIQ(double lat, double lon) async {
|
||||||
// https://eu1.locationiq.com/v1/reverse?key=$locationIQKey&lat=47.93875449671056&lon=13.762706553431048&format=json
|
// https://eu1.locationiq.com/v1/reverse?key=$locationIQKey&lat=47.93875449671056&lon=13.762706553431048&format=json
|
||||||
// Simulierte Antwort (für Testzwecke)
|
|
||||||
locationIQ = LocationIQ(
|
|
||||||
placeId: '12345',
|
|
||||||
licence: 'Data © OpenStreetMap contributors',
|
|
||||||
osmType: 'node',
|
|
||||||
osmId: '67890',
|
|
||||||
lat: lat.toString(),
|
|
||||||
lon: lon.toString(),
|
|
||||||
displayName: 'Test Location',
|
|
||||||
address: Address(
|
|
||||||
houseNumber: '123',
|
|
||||||
road: 'Test Street',
|
|
||||||
village: 'Test Village',
|
|
||||||
county: 'Test County',
|
|
||||||
state: 'Test State',
|
|
||||||
postcode: '12345',
|
|
||||||
country: 'Test Country',
|
|
||||||
countryCode: 'TC',
|
|
||||||
),
|
|
||||||
boundingbox: [
|
|
||||||
'47.93875449671056',
|
|
||||||
'47.93875449671056',
|
|
||||||
'13.762706553431048',
|
|
||||||
'13.762706553431048',
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Http Request
|
// Http Request
|
||||||
var httpClient = http.Client();
|
var httpClient = http.Client();
|
||||||
|
|||||||
235
lib/widgets/gasstation_card_widget.dart
Normal file
235
lib/widgets/gasstation_card_widget.dart
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import '../models/econtrol_model.dart';
|
||||||
|
|
||||||
|
class GasStationCard extends StatelessWidget {
|
||||||
|
final EControlModel station;
|
||||||
|
final int index;
|
||||||
|
|
||||||
|
const GasStationCard({super.key, required this.station, required this.index});
|
||||||
|
|
||||||
|
Color _getPriceColor(double? price) {
|
||||||
|
if (price == null) return Colors.grey;
|
||||||
|
if (price < 1.50) return Colors.green;
|
||||||
|
if (price < 1.70) return Colors.orange;
|
||||||
|
return Colors.red;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatDistance(double? distance) {
|
||||||
|
if (distance == null) return 'N/A';
|
||||||
|
if (distance < 1) {
|
||||||
|
return '${(distance * 1000).toStringAsFixed(0)} m';
|
||||||
|
} else {
|
||||||
|
return '${distance.toStringAsFixed(2)} km';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final price = station.prices?.isNotEmpty == true
|
||||||
|
? station.prices!.first.amount
|
||||||
|
: null;
|
||||||
|
final priceLabel = station.prices?.isNotEmpty == true
|
||||||
|
? station.prices!.first.label
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
elevation: 4,
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
colors: [Colors.white, Colors.grey[50]!],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Header Row with Position and Status
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 6,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.blueGrey[700],
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'#${index + 1}',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 12,
|
||||||
|
vertical: 6,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: station.open == true
|
||||||
|
? Colors.green[400]
|
||||||
|
: Colors.red[400],
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
station.open == true
|
||||||
|
? Icons.check_circle
|
||||||
|
: Icons.cancel,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
station.open == true ? 'Geöffnet' : 'Geschlossen',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
|
// Station Name
|
||||||
|
Text(
|
||||||
|
station.name ?? 'Unbekannte Tankstelle',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
|
||||||
|
// Address
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.location_on,
|
||||||
|
size: 16,
|
||||||
|
color: Colors.blueGrey[600],
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'${station.location?.address ?? ''}, '
|
||||||
|
'${station.location?.postalCode ?? ''} '
|
||||||
|
'${station.location?.city ?? ''}',
|
||||||
|
style: TextStyle(fontSize: 14, color: Colors.grey[700]),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
|
// Price and Distance Row
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
// Price
|
||||||
|
if (price != null)
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 10,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: _getPriceColor(price).withAlpha(50),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: _getPriceColor(price),
|
||||||
|
width: 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.local_gas_station,
|
||||||
|
color: _getPriceColor(price),
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'€ ${price.toStringAsFixed(3)}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: _getPriceColor(price),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (priceLabel?.isNotEmpty == true)
|
||||||
|
Text(
|
||||||
|
priceLabel!,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: Colors.grey[600],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Distance
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16,
|
||||||
|
vertical: 10,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.blue[50],
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.navigation,
|
||||||
|
color: Colors.blue[700],
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
_formatDistance(station.distance),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.blue[700],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user