pre final ad services and correct call the services async

This commit is contained in:
2026-02-20 09:51:03 +01:00
parent 8349e2b496
commit 46f7416781
7 changed files with 479 additions and 207 deletions

View File

@@ -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",

View File

@@ -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();

View File

@@ -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,
);
},
),
),
],
); );
} }
}), }),

View File

@@ -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');
}
} }
} }

View 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;
}
}
}

View File

@@ -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();

View 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],
),
),
],
),
),
],
),
],
),
),
),
);
}
}