googel map routs to near gasstation

This commit is contained in:
2026-10-09 19:20:44 +02:00
parent 141999d61c
commit 832747fce6
9 changed files with 58427 additions and 57178 deletions
+22 -13
View File
@@ -9,6 +9,8 @@ class GasstationsController extends GetxController {
final isLoadingLocation = false.obs; final isLoadingLocation = false.obs;
final errorMessage = ''.obs; final errorMessage = ''.obs;
final eControlData = <EControlModel>[].obs; final eControlData = <EControlModel>[].obs;
double? currentLatitude;
double? currentLongitude;
@override @override
void onInit() { void onInit() {
@@ -32,19 +34,24 @@ class GasstationsController extends GetxController {
// Standort-Abfrage mit Timeout versehen (z. B. 8 Sekunden), // Standort-Abfrage mit Timeout versehen (z. B. 8 Sekunden),
// falls der Browser/User bei der Rechtefreigabe blockiert oder hängt // falls der Browser/User bei der Rechtefreigabe blockiert oder hängt
await geolocationService await geolocationService.getCurrentLocation().timeout(
.getCurrentLocation() const Duration(seconds: 8),
.timeout( onTimeout: () {
const Duration(seconds: 8), throw TimeoutException('Standortabfrage hat zu lange gedauert.');
onTimeout: () { },
throw TimeoutException('Standortabfrage hat zu lange gedauert.'); );
},
);
if (geolocationService.hasLocation) { if (geolocationService.hasLocation) {
currentLatitude = geolocationService.latitude;
currentLongitude = geolocationService.longitude;
// for test only später wieder deaktivieren statische koordinaten
// currentLatitude = 47.938783246743355;
// currentLongitude = 13.762770926423814;
// ende Test only
final fuelData = await eControlService.getBothFuelData( final fuelData = await eControlService.getBothFuelData(
geolocationService.latitude, currentLatitude!,
geolocationService.longitude, currentLongitude!,
); );
List<EControlModel> dieselStations = (fuelData['DIE'] as List) List<EControlModel> dieselStations = (fuelData['DIE'] as List)
@@ -68,7 +75,10 @@ class GasstationsController extends GetxController {
if (combinedMap.containsKey(supStation.id)) { if (combinedMap.containsKey(supStation.id)) {
final existingPrices = combinedMap[supStation.id!]!.prices ?? []; final existingPrices = combinedMap[supStation.id!]!.prices ?? [];
final supPrices = supStation.prices ?? []; final supPrices = supStation.prices ?? [];
combinedMap[supStation.id!]!.prices = [...existingPrices, ...supPrices]; combinedMap[supStation.id!]!.prices = [
...existingPrices,
...supPrices,
];
} else { } else {
combinedMap[supStation.id!] = supStation; combinedMap[supStation.id!] = supStation;
} }
@@ -84,8 +94,7 @@ class GasstationsController extends GetxController {
errorMessage.value = errorMessage.value =
'Standortabfrage zeitlich überschritten. Bitte erlauben Sie den Standortzugriff im Browser.'; 'Standortabfrage zeitlich überschritten. Bitte erlauben Sie den Standortzugriff im Browser.';
} catch (e) { } catch (e) {
errorMessage.value = errorMessage.value = 'Fehler beim Laden der Daten: ${e.toString()}';
'Fehler beim Laden der Daten: ${e.toString()}';
} finally { } finally {
// Stellt sicher, dass das Lade-Icon auf jeden Fall verschwindet // Stellt sicher, dass das Lade-Icon auf jeden Fall verschwindet
isLoading.value = false; isLoading.value = false;
+3 -5
View File
@@ -83,11 +83,7 @@ class GasstationsPage extends GetView<GasstationsController> {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon( Icon(Icons.cloud_off, size: 64, color: Colors.red[400]),
Icons.cloud_off,
size: 64,
color: Colors.red[400],
),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
'Service nicht erreichbar', 'Service nicht erreichbar',
@@ -220,6 +216,8 @@ class GasstationsPage extends GetView<GasstationsController> {
return GasStationCard( return GasStationCard(
station: staCtrl.eControlData[index], station: staCtrl.eControlData[index],
index: index, index: index,
originLatitude: staCtrl.currentLatitude!,
originLongitude: staCtrl.currentLongitude!,
); );
}, },
), ),
+7 -2
View File
@@ -7,8 +7,10 @@ class EControlService {
double longitude, double longitude,
String fuelType, String fuelType,
) async { ) async {
String apiUrl = // For Test purposes only - later deactivate
'https://api.e-control.at/sprit/1.0/search/gas-stations/by-address?latitude=$latitude&longitude=$longitude&fuelType=$fuelType&includeClosed=false'; // latitude = 47.938783246743355;
// longitude = 13.762770926423814;
String apiUrl = '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) { if (response.statusCode == 200) {
return jsonDecode(response.body); return jsonDecode(response.body);
@@ -21,6 +23,9 @@ class EControlService {
double latitude, double latitude,
double longitude, double longitude,
) async { ) async {
// For Test purposes only - later deactivate
// latitude = 47.938783246743355;
// longitude = 13.762770926423814;
final results = await Future.wait([ final results = await Future.wait([
getEControlData(latitude, longitude, 'DIE'), getEControlData(latitude, longitude, 'DIE'),
getEControlData(latitude, longitude, 'SUP'), getEControlData(latitude, longitude, 'SUP'),
+212 -146
View File
@@ -1,11 +1,21 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import '../models/econtrol_model.dart'; import '../models/econtrol_model.dart';
class GasStationCard extends StatelessWidget { class GasStationCard extends StatelessWidget {
final EControlModel station; final EControlModel station;
final int index; final int index;
final double originLatitude;
final double originLongitude;
const GasStationCard({super.key, required this.station, required this.index}); const GasStationCard({
super.key,
required this.station,
required this.index,
required this.originLatitude,
required this.originLongitude,
});
Color _getPriceColor(double? price) { Color _getPriceColor(double? price) {
if (price == null) return Colors.grey; if (price == null) return Colors.grey;
@@ -23,6 +33,53 @@ class GasStationCard extends StatelessWidget {
} }
} }
Future<void> _openInGoogleMaps(BuildContext context) async {
final location = station.location;
final destination =
location?.latitude != null && location?.longitude != null
? '${location!.latitude},${location.longitude}'
: [
station.name,
location?.address,
location?.postalCode,
location?.city,
]
.whereType<String>()
.where((value) => value.trim().isNotEmpty)
.join(', ');
if (destination.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Für diese Tankstelle ist kein Standort verfügbar.'),
),
);
return;
}
final origin = '$originLatitude,$originLongitude';
final mapsUri = Uri.https('www.google.com', '/maps/dir/', {
'api': '1',
'origin': origin,
'destination': destination,
'travelmode': 'driving',
});
final didLaunch = await launchUrl(
mapsUri,
mode: LaunchMode.platformDefault,
webOnlyWindowName: '_blank',
);
if (!didLaunch && context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Google Maps konnte nicht geöffnet werden.'),
),
);
}
}
// Hilfsmethode zur Erstellung eines einzelnen Preis-Badges // Hilfsmethode zur Erstellung eines einzelnen Preis-Badges
Widget _buildPriceBadge(Prices? priceInfo) { Widget _buildPriceBadge(Prices? priceInfo) {
if (priceInfo == null || priceInfo.amount == null) { if (priceInfo == null || priceInfo.amount == null) {
@@ -77,12 +134,18 @@ class GasStationCard extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Suchen nach Diesel und Super/Benzin im Prices-Array // Suchen nach Diesel und Super/Benzin im Prices-Array
final dieselPrice = station.prices?.firstWhere( final dieselPrice = station.prices?.firstWhere(
(p) => p.fuelType == 'DIE' || (p.label?.toLowerCase().contains('diesel') ?? false), (p) =>
orElse: () => station.prices?.isNotEmpty == true ? station.prices!.first : Prices(), p.fuelType == 'DIE' ||
(p.label?.toLowerCase().contains('diesel') ?? false),
orElse: () =>
station.prices?.isNotEmpty == true ? station.prices!.first : Prices(),
); );
final supPrice = station.prices?.firstWhere( final supPrice = station.prices?.firstWhere(
(p) => p.fuelType == 'SUP' || (p.label?.toLowerCase().contains('benzin') ?? false) || (p.label?.toLowerCase().contains('super') ?? false), (p) =>
p.fuelType == 'SUP' ||
(p.label?.toLowerCase().contains('benzin') ?? false) ||
(p.label?.toLowerCase().contains('super') ?? false),
orElse: () => Prices(), orElse: () => Prices(),
); );
@@ -90,164 +153,167 @@ class GasStationCard extends StatelessWidget {
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
elevation: 4, elevation: 4,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Container( clipBehavior: Clip.antiAlias,
decoration: BoxDecoration( child: InkWell(
borderRadius: BorderRadius.circular(16), onTap: () => _openInGoogleMaps(context),
gradient: LinearGradient( child: Ink(
begin: Alignment.topLeft, decoration: BoxDecoration(
end: Alignment.bottomRight, gradient: LinearGradient(
colors: [Colors.white, Colors.grey[50]!], begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.white, Colors.grey[50]!],
),
), ),
), child: Padding(
child: Padding( padding: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16), child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: [ // Header Row
// Header Row Row(
Row( mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
children: [ Container(
Container( padding: const EdgeInsets.symmetric(
padding: const EdgeInsets.symmetric( horizontal: 12,
horizontal: 12, vertical: 6,
vertical: 6, ),
), decoration: BoxDecoration(
decoration: BoxDecoration( color: Colors.blueGrey[700],
color: Colors.blueGrey[700], borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.circular(20), ),
), child: Text(
child: Text( '#${index + 1}',
'#${index + 1}', style: const TextStyle(
style: const TextStyle( color: Colors.white,
color: Colors.white, fontWeight: FontWeight.bold,
fontWeight: FontWeight.bold, fontSize: 12,
fontSize: 12, ),
), ),
), ),
), Container(
Container( padding: const EdgeInsets.symmetric(
padding: const EdgeInsets.symmetric( horizontal: 12,
horizontal: 12, vertical: 6,
vertical: 6, ),
), decoration: BoxDecoration(
decoration: BoxDecoration( color: station.open == true
color: station.open == true ? Colors.green[400]
? Colors.green[400] : Colors.red[400],
: Colors.red[400], borderRadius: BorderRadius.circular(20),
borderRadius: BorderRadius.circular(20), ),
), child: Row(
child: Row( mainAxisSize: MainAxisSize.min,
mainAxisSize: MainAxisSize.min, children: [
children: [ Icon(
Icon( station.open == true
station.open == true ? Icons.check_circle
? Icons.check_circle : Icons.cancel,
: Icons.cancel,
color: Colors.white,
size: 16,
),
const SizedBox(width: 4),
Text(
station.open == true ? 'Geöffnet' : 'Geschlossen',
style: const TextStyle(
color: Colors.white, color: Colors.white,
fontWeight: FontWeight.bold, size: 16,
fontSize: 12,
), ),
), 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: 18,
fontWeight: FontWeight.bold,
color: Colors.black87,
), ),
), const SizedBox(height: 12),
const SizedBox(height: 8),
// Address // Station Name
Row( Text(
children: [ station.name ?? 'Unbekannte Tankstelle',
Icon( style: const TextStyle(
Icons.location_on, fontSize: 18,
size: 16, fontWeight: FontWeight.bold,
color: Colors.blueGrey[600], color: Colors.black87,
), ),
const SizedBox(width: 4), ),
Expanded( const SizedBox(height: 8),
child: Text(
'${station.location?.address ?? ''}, '
'${station.location?.postalCode ?? ''} '
'${station.location?.city ?? ''}',
style: TextStyle(fontSize: 13, color: Colors.grey[700]),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
),
const SizedBox(height: 12),
// Prices & Distance Row // Address
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
children: [ Icon(
// Diesel & Benzin Preise nebeneinander Icons.location_on,
Expanded( size: 16,
child: Wrap( color: Colors.blueGrey[600],
spacing: 8,
runSpacing: 8,
children: [
if (dieselPrice?.amount != null)
_buildPriceBadge(dieselPrice),
if (supPrice?.amount != null)
_buildPriceBadge(supPrice),
],
), ),
), const SizedBox(width: 4),
Expanded(
child: Text(
'${station.location?.address ?? ''}, '
'${station.location?.postalCode ?? ''} '
'${station.location?.city ?? ''}',
style: TextStyle(fontSize: 13, color: Colors.grey[700]),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
),
const SizedBox(height: 12),
// Distance Badge // Prices & Distance Row
Container( Row(
padding: const EdgeInsets.symmetric( mainAxisAlignment: MainAxisAlignment.spaceBetween,
horizontal: 12, children: [
vertical: 8, // Diesel & Benzin Preise nebeneinander
Expanded(
child: Wrap(
spacing: 8,
runSpacing: 8,
children: [
if (dieselPrice?.amount != null)
_buildPriceBadge(dieselPrice),
if (supPrice?.amount != null)
_buildPriceBadge(supPrice),
],
),
), ),
decoration: BoxDecoration(
color: Colors.blue[50], // Distance Badge
borderRadius: BorderRadius.circular(12), Container(
), padding: const EdgeInsets.symmetric(
child: Row( horizontal: 12,
mainAxisSize: MainAxisSize.min, vertical: 8,
children: [ ),
Icon( decoration: BoxDecoration(
Icons.navigation, color: Colors.blue[50],
color: Colors.blue[700], borderRadius: BorderRadius.circular(12),
size: 18, ),
), child: Row(
const SizedBox(width: 6), mainAxisSize: MainAxisSize.min,
Text( children: [
_formatDistance(station.distance), Icon(
style: TextStyle( Icons.navigation,
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.blue[700], color: Colors.blue[700],
size: 18,
), ),
), const SizedBox(width: 6),
], Text(
_formatDistance(station.distance),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.blue[700],
),
),
],
),
), ),
), ],
], ),
), ],
], ),
), ),
), ),
), ),
+74 -10
View File
@@ -316,10 +316,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.19" version: "0.12.20"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
@@ -332,10 +332,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.18.0" version: "1.19.0"
native_toolchain_c: native_toolchain_c:
dependency: transitive dependency: transitive
description: description:
@@ -505,10 +505,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.11" version: "0.7.12"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@@ -517,6 +517,70 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
sha256: e8cc955c97e06df9879140a1fe301f3d493f01b7c62dec7f46834be31d7e9fda
url: "https://pub.dev"
source: hosted
version: "6.3.3"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "611e87fb320b70d1dd721dc46af89c98aceccea9b31fde49e084591414e0c610"
url: "https://pub.dev"
source: hosted
version: "6.3.33"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: "8faa1aab294f1ab4040b43660c887b0418d5fa4f0cffef76a484e6aa1092eb4a"
url: "https://pub.dev"
source: hosted
version: "6.4.2"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: "10f86fef4c2c43563fa6c211ff9cf757adf4d3ab762c56bd430664a947d70cd0"
url: "https://pub.dev"
source: hosted
version: "3.2.3"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "5e835a3b869c2d70325349c81c5a45c28e20791265b67b2669da6b08c5cd5201"
url: "https://pub.dev"
source: hosted
version: "3.2.6"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34"
url: "https://pub.dev"
source: hosted
version: "2.4.3"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "6c5ad3f22cd4c38e089b81963b3cd7bb83b111b2df5dce008bb066162f42e429"
url: "https://pub.dev"
source: hosted
version: "3.1.6"
uuid: uuid:
dependency: transitive dependency: transitive
description: description:
@@ -529,10 +593,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b sha256: "92b9910f66ed1057fd4da7b040ae7c74cafacf885bdc81be496928d5049b032d"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.4.3"
vm_service: vm_service:
dependency: transitive dependency: transitive
description: description:
@@ -582,5 +646,5 @@ packages:
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
sdks: sdks:
dart: ">=3.10.7 <4.0.0" dart: ">=3.12.0 <4.0.0"
flutter: ">=3.38.4" flutter: ">=3.44.0"
+1
View File
@@ -18,6 +18,7 @@ dependencies:
google_fonts: ^7.1.0 google_fonts: ^7.1.0
http: ^1.6.0 http: ^1.6.0
intl: ^0.20.2 intl: ^0.20.2
url_launcher: ^6.3.2
dev_dependencies: dev_dependencies:
flutter_lints: ^6.0.0 flutter_lints: ^6.0.0
Binary file not shown.
+10 -5
View File
@@ -1,5 +1,10 @@
(()=>{var _={blink:!0,gecko:!1,webkit:!1,unknown:!1},K=()=>navigator.vendor==="Google Inc."||navigator.userAgent.includes("Edg/")?"blink":navigator.vendor==="Apple Computer, Inc."?"webkit":navigator.vendor===""&&navigator.userAgent.includes("Firefox")?"gecko":"unknown",C=K(),R=()=>typeof ImageDecoder>"u"?!1:C==="blink",B=()=>typeof Intl.v8BreakIterator<"u"&&typeof Intl.Segmenter<"u",z=()=>{let i=[0,97,115,109,1,0,0,0,1,5,1,95,1,120,0];return WebAssembly.validate(new Uint8Array(i))},M=()=>{let i=document.createElement("canvas");return i.width=1,i.height=1,i.getContext("webgl2")!=null?2:i.getContext("webgl")!=null?1:-1},D=()=>window.chrome&&chrome.runtime&&chrome.runtime.id,w={browserEngine:C,hasImageCodecs:R(),hasChromiumBreakIterators:B(),supportsWasmGC:z(),crossOriginIsolated:window.crossOriginIsolated,webGLVersion:M(),isChromeExtension:D()};function c(...i){return new URL(I(...i),document.baseURI).toString()}function I(...i){return i.filter(e=>!!e).map((e,n)=>n===0?S(e):F(S(e))).filter(e=>e.length).join("/")}function F(i){let e=0;for(;e<i.length&&i.charAt(e)==="/";)e++;return i.substring(e)}function S(i){let e=i.length;for(;e>0&&i.charAt(e-1)==="/";)e--;return i.substring(0,e)}function E(i,e){return i.canvasKitBaseUrl?i.canvasKitBaseUrl:e.engineRevision&&!e.useLocalCanvasKit?I("https://www.gstatic.com/flutter-canvaskit",e.engineRevision):"canvaskit"}var v=class{constructor(){this._scriptLoaded=!1}setTrustedTypesPolicy(e){this._ttPolicy=e}async loadEntrypoint(e){let{entrypointUrl:n=c("main.dart.js"),onEntrypointLoaded:t,nonce:r}=e||{};return this._loadJSEntrypoint(n,t,r)}async load(e,n,t,r,a){a??=l=>{l.initializeEngine(t).then(u=>u.runApp())};let{entrypointBaseUrl:s}=t,{entryPointBaseUrl:o}=t;if(!s&&o&&(console.warn("[deprecated] `entryPointBaseUrl` is deprecated and will be removed in a future release. Use `entrypointBaseUrl` instead."),s=o),e.compileTarget==="dart2wasm")return this._loadWasmEntrypoint(e,n,s,a);{let l=e.mainJsPath??"main.dart.js",u=c(s,l);return this._loadJSEntrypoint(u,a,r)}}didCreateEngineInitializer(e){typeof this._didCreateEngineInitializerResolve=="function"&&(this._didCreateEngineInitializerResolve(e),this._didCreateEngineInitializerResolve=null,delete _flutter.loader.didCreateEngineInitializer),typeof this._onEntrypointLoaded=="function"&&this._onEntrypointLoaded(e)}_loadJSEntrypoint(e,n,t){let r=typeof n=="function";if(!this._scriptLoaded){this._scriptLoaded=!0;let a=this._createScriptTag(e,t);if(r)console.debug("Injecting <script> tag. Using callback."),this._onEntrypointLoaded=n,document.head.append(a);else return new Promise((s,o)=>{console.debug("Injecting <script> tag. Using Promises. Use the callback approach instead!"),this._didCreateEngineInitializerResolve=s,a.addEventListener("error",o),document.head.append(a)})}}async _loadWasmEntrypoint(e,n,t,r){if(!this._scriptLoaded){this._scriptLoaded=!0,this._onEntrypointLoaded=r;let{mainWasmPath:a,jsSupportRuntimePath:s}=e,o=c(t,a),l=c(t,s);this._ttPolicy!=null&&(l=this._ttPolicy.createScriptURL(l));let d=(await import(l)).compileStreaming(fetch(o)),p;e.renderer==="skwasm"?p=(async()=>{let h=await n.skwasm;return window._flutter_skwasmInstance=h,{skwasm:h.wasmExports,skwasmWrapper:h,ffi:{memory:h.wasmMemory}}})():p=Promise.resolve({}),await(await(await d).instantiate(await p,{loadDynamicModule:async(h,j)=>{let A=fetch(c(t,h)),L=c(t,j);this._ttPolicy!=null&&(L=this._ttPolicy.createScriptURL(L));let x=import(L);return[await A,await x]}})).invokeMain()}}_createScriptTag(e,n){let t=document.createElement("script");t.type="application/javascript",n&&(t.nonce=n);let r=e;return this._ttPolicy!=null&&(r=this._ttPolicy.createScriptURL(e)),t.src=r,t}};async function T(i,e,n){if(e<0)return i;let t,r=new Promise((a,s)=>{t=setTimeout(()=>{s(new Error(`${n} took more than ${e}ms to resolve. Moving on.`,{cause:T}))},e)});return Promise.race([i,r]).finally(()=>{clearTimeout(t)})}var g=class{setTrustedTypesPolicy(e){this._ttPolicy=e}loadServiceWorker(e){if(!e||!("serviceWorker"in navigator))return Promise.resolve();let n=()=>{console.warn(`Loading the service worker using Flutter bootstrap is deprecated and will stop working in a future release. (()=>{var C={blink:!0,gecko:!1,webkit:!1,unknown:!1},A=()=>navigator.vendor==="Google Inc."||navigator.userAgent.includes("Edg/")?"blink":navigator.vendor==="Apple Computer, Inc."?"webkit":navigator.vendor===""&&navigator.userAgent.includes("Firefox")?"gecko":"unknown",T=A(),x=()=>typeof ImageDecoder>"u"?!1:T==="blink",R=()=>typeof Intl.v8BreakIterator<"u"&&typeof Intl.Segmenter<"u",j=()=>typeof window.TextCluster<"u",B=()=>{let n=[0,97,115,109,1,0,0,0,1,5,1,95,1,120,0];return WebAssembly.validate(new Uint8Array(n))},K=()=>{let n=document.createElement("canvas");return n.width=1,n.height=1,n.getContext("webgl2")!=null?2:n.getContext("webgl")!=null?1:-1},$=()=>window.chrome&&chrome.runtime&&chrome.runtime.id,f={browserEngine:T,hasImageCodecs:x(),hasChromiumBreakIterators:R(),hasTextCluster:j(),supportsWasmGC:B(),crossOriginIsolated:window.crossOriginIsolated,webGLVersion:K(),isChromeExtension:$()};function m(...n){return new URL(W(...n),document.baseURI).toString()}function W(...n){return n.filter(e=>!!e).map((e,s)=>s===0?L(e):F(L(e))).filter(e=>e.length).join("/")}function F(n){let e=0;for(;e<n.length&&n.charAt(e)==="/";)e++;return n.substring(e)}function L(n){let e=n.length;for(;e>0&&n.charAt(e-1)==="/";)e--;return n.substring(0,e)}function _(n,e){return n.canvasKitBaseUrl?n.canvasKitBaseUrl:e.engineRevision&&!e.useLocalCanvasKit?W("https://www.gstatic.com/flutter-canvaskit",e.engineRevision):"canvaskit"}var g=class{constructor(){this._scriptLoaded=!1}setTrustedTypesPolicy(e){this._ttPolicy=e}async loadEntrypoint(e){let{entrypointUrl:s=m("main.dart.js"),onEntrypointLoaded:t,nonce:r}=e||{};return this._loadJSEntrypoint(s,t,r)}async load(e,s,t,r,i){i??=a=>{a.initializeEngine(t).then(l=>l.runApp())};let{entrypointBaseUrl:o}=t,{entryPointBaseUrl:c}=t;if(!o&&c&&(console.warn("[deprecated] `entryPointBaseUrl` is deprecated and will be removed in a future release. Use `entrypointBaseUrl` instead."),o=c),e.compileTarget==="dart2wasm")return this._loadWasmEntrypoint(e,s,o,i);{let a=e.mainJsPath??"main.dart.js",l=m(o,a);return this._loadJSEntrypoint(l,i,r)}}didCreateEngineInitializer(e){typeof this._didCreateEngineInitializerResolve=="function"&&(this._didCreateEngineInitializerResolve(e),this._didCreateEngineInitializerResolve=null,delete _flutter.loader.didCreateEngineInitializer),typeof this._onEntrypointLoaded=="function"&&this._onEntrypointLoaded(e)}_loadJSEntrypoint(e,s,t){let r=typeof s=="function";if(!this._scriptLoaded){this._scriptLoaded=!0;let i=this._createScriptTag(e,t);if(r)console.debug("Injecting <script> tag. Using callback."),this._onEntrypointLoaded=s,document.head.append(i);else return new Promise((o,c)=>{console.debug("Injecting <script> tag. Using Promises. Use the callback approach instead!"),this._didCreateEngineInitializerResolve=o,i.addEventListener("error",c),document.head.append(i)})}}async _loadWasmEntrypoint(e,s,t,r){if(!this._scriptLoaded){this._scriptLoaded=!0,this._onEntrypointLoaded=r;let{mainWasmPath:i,jsSupportRuntimePath:o}=e,c=m(t,i),a=m(t,o);this._ttPolicy!=null&&(a=this._ttPolicy.createScriptURL(a));let p=(await import(a)).compileStreaming(fetch(c)),d;e.renderer==="skwasm"?d=(async()=>{let u=await s.skwasm;return window._flutter_skwasmInstance=u,{skwasm:u.wasmExports,skwasmWrapper:u,ffi:{memory:u.wasmMemory}}})():d=Promise.resolve({}),await(await(await p).instantiate(await d)).invokeMain()}}_createScriptTag(e,s){let t=document.createElement("script");t.type="application/javascript",s&&(t.nonce=s);let r=e;return this._ttPolicy!=null&&(r=this._ttPolicy.createScriptURL(e)),t.src=r,t}};async function E(n,e,s){if(e<0)return n;let t,r=new Promise((i,o)=>{t=setTimeout(()=>{o(new Error(`${s} took more than ${e}ms to resolve. Moving on.`,{cause:E}))},e)});return Promise.race([n,r]).finally(()=>{clearTimeout(t)})}var v=class{setTrustedTypesPolicy(e){this._ttPolicy=e}loadServiceWorker(e){if(!e||!("serviceWorker"in navigator))return Promise.resolve();let s=()=>{console.warn(`Loading the service worker using Flutter bootstrap is deprecated and will stop working in a future release.
For more details, see: https://github.com/flutter/flutter/issues/156910`)},t=()=>{let{serviceWorkerVersion:r,serviceWorkerUrl:a=c(`flutter_service_worker.js?v=${r}`),timeoutMillis:s=4e3}=e,o=a;this._ttPolicy!=null&&(o=this._ttPolicy.createScriptURL(o));let l=navigator.serviceWorker.register(o).then(u=>this._getNewServiceWorker(u,r)).then(this._waitForServiceWorkerActivation);return T(l,s,"prepareServiceWorker")};return e.serviceWorkerUrl!=null?(n(),t()):navigator.serviceWorker.getRegistration().then(r=>r?t():Promise.resolve())}async _getNewServiceWorker(e,n){if(!e.active&&(e.installing||e.waiting))return console.debug("Installing/Activating first service worker."),e.installing||e.waiting;if(e.active.scriptURL.endsWith(n))return console.debug("Loading from existing service worker."),e.active;{let t=await e.update();return console.debug("Updating service worker."),t.installing||t.waiting||t.active}}async _waitForServiceWorkerActivation(e){if(!e||e.state==="activated")if(e){console.debug("Service worker already active.");return}else throw new Error("Cannot activate a null service worker!");return new Promise((n,t)=>{e.addEventListener("statechange",()=>{e.state==="activated"&&(console.debug("Activated new service worker."),n())})})}};var y=class{constructor(e,n="flutter-js"){let t=e||[/\.js$/,/\.mjs$/];window.trustedTypes&&(this.policy=trustedTypes.createPolicy(n,{createScriptURL:function(r){if(r.startsWith("blob:"))return r;let a=new URL(r,window.location),s=a.pathname.split("/").pop();if(t.some(l=>l.test(s)))return a.toString();console.error("URL rejected by TrustedTypes policy",n,":",r,"(download prevented)")}}))}};var k=i=>{let e=WebAssembly.compileStreaming(fetch(i));return(n,t)=>((async()=>{let r=await e,a=await WebAssembly.instantiate(r,n);t(a,r)})(),{})};var U=(i,e,n,t)=>(window.flutterCanvasKitLoaded=(async()=>{if(window.flutterCanvasKit)return window.flutterCanvasKit;let r=n.hasChromiumBreakIterators&&n.hasImageCodecs;if(!r&&e.canvasKitVariant=="chromium")throw"Chromium CanvasKit variant specifically requested, but unsupported in this browser";let a=r&&e.canvasKitVariant!=="full",s=t;e.canvasKitVariant=="experimentalWebParagraph"?s=c(s,"experimental_webparagraph"):a&&(s=c(s,"chromium"));let o=c(s,"canvaskit.js");i.flutterTT.policy&&(o=i.flutterTT.policy.createScriptURL(o));let l=k(c(s,"canvaskit.wasm")),u=await import(o);return window.flutterCanvasKit=await u.default({instantiateWasm:l}),window.flutterCanvasKit})(),window.flutterCanvasKitLoaded);var W=async(i,e,n,t)=>{let a=!n.hasImageCodecs||!n.hasChromiumBreakIterators?"skwasm_heavy":e.enableWimp?"wimp":"skwasm",s=c(t,`${a}.js`),o=s;i.flutterTT.policy&&(o=i.flutterTT.policy.createScriptURL(o));let l=k(c(t,`${a}.wasm`));return await(await import(o)).default({skwasmSingleThreaded:e.enableWimp||!n.crossOriginIsolated||n.isChromeExtension||e.forceSingleThreadedSkwasm,instantiateWasm:l,locateFile:(d,p)=>d.endsWith(".ww.js")?URL.createObjectURL(new Blob([` For more details, see: https://github.com/flutter/flutter/issues/156910`)},t=()=>{let{serviceWorkerVersion:r,serviceWorkerUrl:i=m(`flutter_service_worker.js?v=${r}`),timeoutMillis:o=4e3}=e,c=i;this._ttPolicy!=null&&(c=this._ttPolicy.createScriptURL(c));let a=navigator.serviceWorker.register(c).then(l=>this._getNewServiceWorker(l,r)).then(this._waitForServiceWorkerActivation);return E(a,o,"prepareServiceWorker")};return e.serviceWorkerUrl!=null?(s(),t()):navigator.serviceWorker.getRegistration().then(r=>r?t():Promise.resolve())}async _getNewServiceWorker(e,s){if(!e.active&&(e.installing||e.waiting))return console.debug("Installing/Activating first service worker."),e.installing||e.waiting;if(e.active.scriptURL.endsWith(s))return console.debug("Loading from existing service worker."),e.active;{let t=await e.update();return console.debug("Updating service worker."),t.installing||t.waiting||t.active}}async _waitForServiceWorkerActivation(e){if(!e||e.state==="activated")if(e){console.debug("Service worker already active.");return}else throw new Error("Cannot activate a null service worker!");return new Promise((s,t)=>{e.addEventListener("statechange",()=>{e.state==="activated"&&(console.debug("Activated new service worker."),s())})})}};var y=class{constructor(e,s="flutter-js"){let t=e||[/\.js$/,/\.mjs$/];window.trustedTypes&&(this.policy=trustedTypes.createPolicy(s,{createScriptURL:function(r){if(r.startsWith("blob:"))return r;let i=new URL(r,window.location),o=i.pathname.split("/").pop();if(t.some(a=>a.test(o)))return i.toString();console.error("URL rejected by TrustedTypes policy",s,":",r,"(download prevented)")}}))}};var b=(n,e)=>{let s=window._flutter?.buildConfig?.wasmHashes,t=s?.[e];if(!t&&e.includes("/")){let a=e.split("/").pop();t=s?.[a]}let r="crossOriginStorage"in navigator&&"requestFileHandles"in navigator.crossOriginStorage;r&&console.log("Cross-Origin Storage is supported. See https://wicg.github.io/cross-origin-storage/ for more details.");let i=async a=>{let l={algorithm:"SHA-256",value:a};try{let[p]=await navigator.crossOriginStorage.requestFileHandles([l]),d=await p.getFile();return new Response(d,{headers:{"Content-Type":"application/wasm"}})}catch(p){p.name==="NotAllowedError"?console.warn(`Not allowed to retrieve ${e} (hash: ${a}).`):p.name!=="NotFoundError"&&console.warn(`Unexpected error during retrieval of ${e} (hash: ${a}).`,p)}},o=async()=>{if(r&&t){let l=await i(t);if(l)return l}let a=await fetch(n);if(r&&t&&a.ok){let l={algorithm:"SHA-256",value:t},p=a.clone();(async()=>{try{let d=await p.blob(),[h]=await navigator.crossOriginStorage.requestFileHandles([l],{create:!0}),w=await h.createWritable();await w.write(d),await w.close()}catch(d){console.warn(`Error storing ${e} (hash: ${t}):`,d)}})()}return a},c=WebAssembly.compileStreaming(o());return(a,l)=>((async()=>{let p=await c,d=await WebAssembly.instantiate(p,a);l(d,p)})(),{})};var I=(n,e,s,t)=>(window.flutterCanvasKitLoaded=(async()=>{if(window.flutterCanvasKit)return window.flutterCanvasKit;let r=s.hasChromiumBreakIterators&&s.hasImageCodecs;if(!r&&e.canvasKitVariant=="chromium")throw"Chromium CanvasKit variant specifically requested, but unsupported in this browser";let i=r&&e.canvasKitVariant!=="full",o=i&&e.preferWebParagraph&&s.hasTextCluster,c=t;o?c=m(c,"webparagraph"):i&&(c=m(c,"chromium"));let a=m(c,"canvaskit.js");n.flutterTT.policy&&(a=n.flutterTT.policy.createScriptURL(a));let l="canvaskit.wasm";o?l="webparagraph/canvaskit.wasm":i&&(l="chromium/canvaskit.wasm");let p=b(m(c,"canvaskit.wasm"),l),d=await import(a);return window.flutterCanvasKit=await d.default({instantiateWasm:p}),window.flutterCanvasKit})(),window.flutterCanvasKitLoaded);var U=async(n,e,s,t)=>{let i=!s.hasImageCodecs||!s.hasChromiumBreakIterators?"skwasm_heavy":e.enableWimp?"wimp":"skwasm",o=m(t,`${i}.js`),c=o;n.flutterTT.policy&&(c=n.flutterTT.policy.createScriptURL(c));let a=b(m(t,`${i}.wasm`),`${i}.wasm`),l=await import(c);return!s.crossOriginIsolated&&!e.forceSingleThreadedSkwasm&&!e.suppressMultithreadingWarning&&console.warn(`Flutter Web: Skwasm uses multi-threading and web workers for better performance, but your page needs to be cross-origin isolated to support multi-threading. Skwasm will run in single-threaded mode.
To enable multithreading, serve your app with these HTTP response headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
See https://web.dev/articles/coop-coep for guidance.
To silence this warning, set \`suppressMultithreadingWarning: true\` in your Flutter configuration.`),await l.default({skwasmSingleThreaded:e.enableWimp||!s.crossOriginIsolated||s.isChromeExtension||e.forceSingleThreadedSkwasm,instantiateWasm:a,locateFile:(p,d)=>p.endsWith(".ww.js")?URL.createObjectURL(new Blob([`
"use strict"; "use strict";
let eventListener; let eventListener;
@@ -27,16 +32,16 @@ eventListener = (message) => {
addEventListener("message", eventListener); addEventListener("message", eventListener);
}; };
addEventListener("message", eventListener); addEventListener("message", eventListener);
`],{type:"application/javascript"})):c(t,d),mainScriptUrlOrBlob:s})};var P=w.supportsWasmGC,G=P&&w.webGLVersion>0,b=class{async loadEntrypoint(e){let{serviceWorker:n,...t}=e||{},r=new y,a=new g;a.setTrustedTypesPolicy(r.policy),await a.loadServiceWorker(n).catch(o=>{console.warn("Exception while loading service worker:",o)});let s=new v;return s.setTrustedTypesPolicy(r.policy),this.didCreateEngineInitializer=s.didCreateEngineInitializer.bind(s),s.loadEntrypoint(t)}async load({serviceWorkerSettings:e,onEntrypointLoaded:n,nonce:t,config:r}={}){r??={};let a=_flutter.buildConfig;if(!a)throw"FlutterLoader.load requires _flutter.buildConfig to be set";let s=r.wasmAllowList?.[w.browserEngine]??_[w.browserEngine],o=m=>{switch(m){case"skwasm":return G&&s;default:return!0}},l=m=>m.compileTarget==="dart2wasm"&&!P||r.renderer&&r.renderer!=m.renderer?!1:o(m.renderer),u=a.builds.find(l);if(!u)throw"FlutterLoader could not find a build compatible with configuration and environment.";let d={};d.flutterTT=new y,e&&(d.serviceWorkerLoader=new g,d.serviceWorkerLoader.setTrustedTypesPolicy(d.flutterTT.policy),await d.serviceWorkerLoader.loadServiceWorker(e).catch(m=>{console.warn("Exception while loading service worker:",m)}));let p=E(r,a);u.renderer==="canvaskit"?d.canvasKit=U(d,r,w,p):u.renderer==="skwasm"&&(d.skwasm=W(d,r,w,p));let f=new v;return f.setTrustedTypesPolicy(d.flutterTT.policy),this.didCreateEngineInitializer=f.didCreateEngineInitializer.bind(f),f.load(u,d,r,t,n)}};window._flutter||(window._flutter={});window._flutter.loader||(window._flutter.loader=new b);})(); `],{type:"application/javascript"})):m(t,p),mainScriptUrlOrBlob:o})};var P=f.supportsWasmGC,k=class{async loadEntrypoint(e){let{serviceWorker:s,...t}=e||{},r=new y,i=new v;i.setTrustedTypesPolicy(r.policy),await i.loadServiceWorker(s).catch(c=>{console.warn("Exception while loading service worker:",c)});let o=new g;return o.setTrustedTypesPolicy(r.policy),this.didCreateEngineInitializer=o.didCreateEngineInitializer.bind(o),o.loadEntrypoint(t)}async load({serviceWorkerSettings:e,onEntrypointLoaded:s,nonce:t,config:r}={}){r??={};let i=_flutter.buildConfig;if(!i)throw"FlutterLoader.load requires _flutter.buildConfig to be set";let o=r.wasmAllowList?.[f.browserEngine]??C[f.browserEngine],c=u=>{switch(u){case"skwasm":return P?f.webGLVersion>0?o?null:`Skwasm is disabled by your wasmAllowList configuration for browser engine "${f.browserEngine}".`:"Skwasm requires WebGL support; this browser does not provide it.":"Skwasm requires WasmGC support; this browser does not implement it yet.";default:return null}},a=u=>u.compileTarget==="dart2wasm"&&!P?"dart2wasm requires WasmGC support; this browser does not implement it yet.":r.renderer&&r.renderer!=u.renderer?`The application is configured to use the "${r.renderer}" renderer; this build targets "${u.renderer}".`:c(u.renderer),l,p=[];for(let u of i.builds){let S=a(u);if(S===null){l=u;break}p.push({candidate:u,reason:S})}if(r.verboseBuildSelection)for(let u of p)console.warn(`Flutter Web: build ${u.candidate.compileTarget}/${u.candidate.renderer} was skipped: ${u.reason}`);if(!l)throw console.warn("Flutter Web: no compatible build found for this browser."+(r.verboseBuildSelection?"":" Set `verboseBuildSelection: true` in your Flutter configuration to see why each candidate was rejected.")),new Error("FlutterLoader could not find a build compatible with configuration and environment.");let d={};d.flutterTT=new y,e&&(d.serviceWorkerLoader=new v,d.serviceWorkerLoader.setTrustedTypesPolicy(d.flutterTT.policy),await d.serviceWorkerLoader.loadServiceWorker(e).catch(u=>{console.warn("Exception while loading service worker:",u)}));let h=_(r,i);l.renderer==="canvaskit"?d.canvasKit=I(d,r,f,h):l.renderer==="skwasm"&&(d.skwasm=U(d,r,f,h));let w=new g;return w.setTrustedTypesPolicy(d.flutterTT.policy),this.didCreateEngineInitializer=w.didCreateEngineInitializer.bind(w),w.load(l,d,r,t,s)}};window._flutter||(window._flutter={});window._flutter.loader||(window._flutter.loader=new k);})();
//# sourceMappingURL=flutter.js.map //# sourceMappingURL=flutter.js.map
if (!window._flutter) { if (!window._flutter) {
window._flutter = {}; window._flutter = {};
} }
_flutter.buildConfig = {"engineRevision":"a4ce257c68517c1410f4b48ac9852ab5642a3f8d","builds":[{"compileTarget":"dart2js","renderer":"canvaskit","mainJsPath":"main.dart.js"}]}; _flutter.buildConfig = {"engineRevision":"af7e796e161ae0bb1ff0758c71a7105418bd9ded","wasmHashes":{"canvaskit.wasm":"fbed517a43e82452404446683f00f2e876d835aed84410695759e67b6bb01cd3","chromium/canvaskit.wasm":"ae8ff1d858140f7b1300ced3fa89fb8c9dce0a400a0f4f1e11f6dcfb3315fdcf","skwasm.wasm":"e540fd5e8303b7b68ec2718cb49e9c421f8ade3075b15e02a7059a62654df9a1","skwasm_heavy.wasm":"565f5cc1cca6ab120f11934b105f01fec4b58b480c82e0889dca93af8e6f8635","webparagraph/canvaskit.wasm":"0ce1b05082efdc8529550e8a01f6ff0593972d55525035010e26f5600aa9f254","wimp.wasm":"e924eaafd801d41e017d178f3fd5cf8a417f641fe35c9ed34a4e1d7582283e0c"},"builds":[{"compileTarget":"dart2js","renderer":"canvaskit","mainJsPath":"main.dart.js"}]};
_flutter.loader.load({ _flutter.loader.load({
serviceWorkerSettings: { serviceWorkerSettings: {
serviceWorkerVersion: "1649555018" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */ serviceWorkerVersion: "2476194018" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
} }
}); });
+58093 -56992
View File
File diff suppressed because one or more lines are too long