add login

This commit is contained in:
atseirjo 2025-10-22 08:43:20 +02:00
parent b6bd692cd7
commit c0960cbd9f
21 changed files with 1128 additions and 221 deletions

6
.envExample Normal file
View File

@ -0,0 +1,6 @@
APPWRITE_ENDPOINT_URL=<Your Appwrite Endpoint>
APPWRITE_PROJECT_NAME=<Your Project Name>
APPWRITE_PROJECT_ID=<Your Project Id>
APPWRITE_DATABASE_ID=<Your Database Id>
APPWRITE_COLLECTION_ID=<Your Collection id>
PTVE_API_KEY=<Your PTV API key Geolocation Address>

View File

@ -7,6 +7,9 @@
# The following line activates a set of recommended lints for Flutter apps, # The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices. # packages, and plugins designed to encourage good coding practices.
analyzer:
errors:
avoid_print: ignore
include: package:flutter_lints/flutter.yaml include: package:flutter_lints/flutter.yaml
linter: linter:

View File

@ -0,0 +1,18 @@
import 'package:get/get.dart';
import '../controllers/login_controller.dart';
class LoginBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<LoginController>(() => LoginController());
}
}
/// Alternative: Permanent Binding für App-weite Nutzung
class LoginPermanentBinding extends Bindings {
@override
void dependencies() {
// Permanent - Controller bleibt im Speicher
Get.put<LoginController>(LoginController(), permanent: true);
}
}

View File

@ -1,6 +1,8 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:geolocator/geolocator.dart'; import 'package:geolocator/geolocator.dart';
import '../../services/geolocation.dart'; import '../../services/geolocation.dart';
import '../models/ptv_logistic_model.dart';
import '../services/ptv_api_simple.dart';
/// GetX Controller für Geolocation Funktionalität /// GetX Controller für Geolocation Funktionalität
class GeolocationController extends GetxController { class GeolocationController extends GetxController {
@ -9,12 +11,14 @@ class GeolocationController extends GetxController {
final _statusText = 'Noch keine Position ermittelt'.obs; final _statusText = 'Noch keine Position ermittelt'.obs;
final _isTracking = false.obs; final _isTracking = false.obs;
final _isLoading = false.obs; final _isLoading = false.obs;
final _ptvModel = Rxn<PTVModel>();
// Getter für reactive Variablen // Getter für reactive Variablen
Position? get currentPosition => _currentPosition.value; Position? get currentPosition => _currentPosition.value;
String get statusText => _statusText.value; String get statusText => _statusText.value;
bool get isTracking => _isTracking.value; bool get isTracking => _isTracking.value;
bool get isLoading => _isLoading.value; bool get isLoading => _isLoading.value;
PTVModel? get ptvModel => _ptvModel.value;
@override @override
void onClose() { void onClose() {
@ -34,7 +38,15 @@ class GeolocationController extends GetxController {
if (position != null) { if (position != null) {
_currentPosition.value = position; _currentPosition.value = position;
_statusText.value = GeolocationService.positionToString(position); _statusText.value = GeolocationService.positionToString(position);
var resultData = await PtvApiServiceSimple.getLocationsByPosition(
latitude: position.latitude,
longitude: position.longitude,
);
if(resultData != null) {
_ptvModel.value = PTVModel.fromJson(resultData);
} else {
_ptvModel.value = null;
}
// Erfolgs-Snackbar anzeigen // Erfolgs-Snackbar anzeigen
Get.snackbar( Get.snackbar(
'Position ermittelt', 'Position ermittelt',

View File

@ -0,0 +1,65 @@
import 'package:appwrite/appwrite.dart' as account_models;
import 'package:appwrite/models.dart' as user_models;
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:appwrite/appwrite.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
class LoginController extends GetxController {
final _account = Rxn<account_models.Account>();
final mailController = TextEditingController();
final passwordController = TextEditingController();
final nameController = TextEditingController();
final _endpoint = dotenv.env['APPWRITE_ENDPOINT_URL'] ?? '';
final _projectId = dotenv.env['APPWRITE_PROJECT_ID'] ?? '';
final _logedInUser = Rxn<user_models.User>();
account_models.Account? get account => _account.value;
user_models.User? get logedInUser => _logedInUser.value;
@override
void onInit() {
Client client = Client()
.setEndpoint(_endpoint)
.setProject(_projectId);
_account.value = Account(client);
super.onInit();
}
Future<void> login(String email, String password) async {
await _account.value!.createEmailPasswordSession(
email: email,
password: password,
);
final user = await _account.value!.get();
_logedInUser.value = user;
}
Future<void> register(String email, String password, String name) async {
await _account.value!.create(
userId: ID.unique(),
email: email,
password: password,
name: name,
);
await login(email, password);
}
Future<void> logout() async {
await _account.value!.deleteSession(sessionId: 'current');
_logedInUser.value = null;
}
@override
void onClose() {
mailController.dispose();
passwordController.dispose();
nameController.dispose();
super.onClose();
}
}

View File

@ -1,10 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'bindings/login_binding.dart';
import 'routes/app_routes.dart'; import 'routes/app_routes.dart';
import 'bindings/geolocation_binding.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'pages/examples/geolocation_example.dart';
void main() { void main() async {
WidgetsFlutterBinding.ensureInitialized();
await dotenv.load(fileName: ".env");
runApp(const MyApp()); runApp(const MyApp());
} }
@ -21,11 +23,9 @@ class MyApp extends StatelessWidget {
useMaterial3: true, useMaterial3: true,
), ),
// GetX Konfiguration // GetX Konfiguration
initialRoute: AppRoutes.geolocation, initialRoute: AppRoutes.login,
getPages: AppRoutes.pages, getPages: AppRoutes.pages,
initialBinding: GeolocationBinding(), // Optional: Globale Bindings initialBinding: LoginBinding(),
// Alternative: Direkte Navigation ohne Routen
home: const GeolocationExample(),
// GetX Optionen // GetX Optionen
enableLog: true, // Debugging enableLog: true, // Debugging
@ -34,41 +34,3 @@ class MyApp extends StatelessWidget {
); );
} }
} }
/// Alternative: Home Page mit Navigation zu Geolocation
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Tank App'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Tank Appwrite App',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 32),
ElevatedButton.icon(
onPressed: () => Get.to(() => const GeolocationExample()),
icon: const Icon(Icons.location_on),
label: const Text('Geolocation Beispiel'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
),
),
],
),
),
);
}
}

View File

@ -1,75 +0,0 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'routes/app_routes.dart';
import 'bindings/geolocation_binding.dart';
import 'pages/examples/geolocation_example.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Web Flutter Tank Appwrite App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
// GetX Konfiguration
initialRoute: AppRoutes.geolocation,
getPages: AppRoutes.pages,
initialBinding: GeolocationBinding(), // Optional: Globale Bindings
// Alternative: Direkte Navigation ohne Routen
//home: const GeolocationExample(),
// GetX Optionen
enableLog: true, // Debugging
defaultTransition: Transition.fade,
transitionDuration: const Duration(milliseconds: 300),
);
}
}
/// Alternative: Home Page mit Navigation zu Geolocation
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Tank App'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Tank Appwrite App',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 32),
ElevatedButton.icon(
onPressed: () => Get.to(() => const GeolocationExample()),
icon: const Icon(Icons.location_on),
label: const Text('Geolocation Beispiel'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 12,
),
),
),
],
),
),
);
}
}

View File

@ -0,0 +1,7 @@
class LoginSigninModel {
String email;
String password;
String name;
LoginSigninModel({this.email='test@test.at', this.password='123PassWd', this.name='TestUser'});
}

View File

@ -0,0 +1,168 @@
/// Hauptklasse für die PTV Location Response
class PTVModel {
List<Locations>? locations;
String? noMatchFeedbackId;
PTVModel({this.locations, this.noMatchFeedbackId});
PTVModel.fromJson(Map<String, dynamic> json) {
if (json['locations'] != null) {
locations = <Locations>[];
json['locations'].forEach((v) {
locations!.add(Locations.fromJson(v));
});
}
noMatchFeedbackId = json['noMatchFeedbackId'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (locations != null) {
data['locations'] = locations!.map((v) => v.toJson()).toList();
}
data['noMatchFeedbackId'] = noMatchFeedbackId;
return data;
}
}
class Locations {
ReferencePosition? referencePosition;
Address? address;
String? locationType;
Quality? quality;
String? formattedAddress;
String? feedbackId;
Locations(
{this.referencePosition,
this.address,
this.locationType,
this.quality,
this.formattedAddress,
this.feedbackId});
Locations.fromJson(Map<String, dynamic> json) {
referencePosition = json['referencePosition'] != null
? ReferencePosition.fromJson(json['referencePosition'])
: null;
address =
json['address'] != null ? Address.fromJson(json['address']) : null;
locationType = json['locationType'];
quality =
json['quality'] != null ? Quality.fromJson(json['quality']) : null;
formattedAddress = json['formattedAddress'];
feedbackId = json['feedbackId'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (referencePosition != null) {
data['referencePosition'] = referencePosition!.toJson();
}
if (address != null) {
data['address'] = address!.toJson();
}
data['locationType'] = locationType;
if (quality != null) {
data['quality'] = quality!.toJson();
}
data['formattedAddress'] = formattedAddress;
data['feedbackId'] = feedbackId;
return data;
}
}
class ReferencePosition {
double? latitude;
double? longitude;
ReferencePosition({this.latitude, this.longitude});
ReferencePosition.fromJson(Map<String, dynamic> json) {
latitude = json['latitude'];
longitude = json['longitude'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['latitude'] = latitude;
data['longitude'] = longitude;
return data;
}
}
class Address {
String? countryName;
String? state;
String? province;
String? city;
String? district;
String? subdistrict;
String? street;
String? houseNumber;
String? countryCodeIsoAlpha2;
String? countryCodeIsoAlpha3;
String? countryCode;
Address(
{this.countryName,
this.state,
this.province,
this.city,
this.district,
this.subdistrict,
this.street,
this.houseNumber,
this.countryCodeIsoAlpha2,
this.countryCodeIsoAlpha3,
this.countryCode});
Address.fromJson(Map<String, dynamic> json) {
countryName = json['countryName'];
state = json['state'];
province = json['province'];
city = json['city'];
district = json['district'];
subdistrict = json['subdistrict'];
street = json['street'];
houseNumber = json['houseNumber'];
countryCodeIsoAlpha2 = json['countryCodeIsoAlpha2'];
countryCodeIsoAlpha3 = json['countryCodeIsoAlpha3'];
countryCode = json['countryCode'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['countryName'] = countryName;
data['state'] = state;
data['province'] = province;
data['city'] = city;
data['district'] = district;
data['subdistrict'] = subdistrict;
data['street'] = street;
data['houseNumber'] = houseNumber;
data['countryCodeIsoAlpha2'] = countryCodeIsoAlpha2;
data['countryCodeIsoAlpha3'] = countryCodeIsoAlpha3;
data['countryCode'] = countryCode;
return data;
}
}
class Quality {
int? distance;
Quality({this.distance});
Quality.fromJson(Map<String, dynamic> json) {
distance = json['distance'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['distance'] = distance;
return data;
}
}

View File

@ -24,11 +24,12 @@ class GeolocationExample extends StatelessWidget {
), ),
], ],
), ),
body: Padding( body: SafeArea(
padding: const EdgeInsets.all(16.0), child: SingleChildScrollView(
child: Column( padding: const EdgeInsets.all(16.0),
crossAxisAlignment: CrossAxisAlignment.stretch, child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Status Card // Status Card
Obx(() => Card( Obx(() => Card(
child: Padding( child: Padding(
@ -107,8 +108,16 @@ class GeolocationExample extends StatelessWidget {
'Längengrad:', 'Längengrad:',
controller.currentPosition!.longitude.toStringAsFixed(6), controller.currentPosition!.longitude.toStringAsFixed(6),
Icons.location_on, Icons.location_on,
), ),
_buildPositionDetail( if (controller.ptvModel != null &&
controller.ptvModel!.locations != null &&
controller.ptvModel!.locations!.isNotEmpty)
_buildPositionDetail(
'Adresse:',
'${controller.ptvModel!.locations!.first.formattedAddress}',
Icons.add_home_rounded,
),
_buildPositionDetail(
'Genauigkeit:', 'Genauigkeit:',
'${controller.currentPosition!.accuracy.toStringAsFixed(1)} m', '${controller.currentPosition!.accuracy.toStringAsFixed(1)} m',
Icons.gps_fixed, Icons.gps_fixed,
@ -137,92 +146,94 @@ class GeolocationExample extends StatelessWidget {
const SizedBox(height: 16), const SizedBox(height: 16),
// Action Buttons // Action Buttons
Expanded( Column(
child: Column( children: [
children: [ // Aktuelle Position Button
// Aktuelle Position Button Obx(() => SizedBox(
Obx(() => SizedBox( width: double.infinity,
width: double.infinity, child: ElevatedButton.icon(
child: ElevatedButton.icon( onPressed: controller.isLoading ? null : controller.getCurrentPosition,
onPressed: controller.isLoading ? null : controller.getCurrentPosition, icon: controller.isLoading
icon: controller.isLoading ? const SizedBox(
? const SizedBox( width: 20,
width: 20, height: 20,
height: 20, child: CircularProgressIndicator(strokeWidth: 2),
child: CircularProgressIndicator(strokeWidth: 2), )
) : const Icon(Icons.my_location),
: const Icon(Icons.my_location), label: const Text('Aktuelle Position ermitteln'),
label: const Text('Aktuelle Position ermitteln'), style: ElevatedButton.styleFrom(
style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 12),
padding: const EdgeInsets.symmetric(vertical: 12),
),
), ),
)),
const SizedBox(height: 12),
// Tracking Toggle Button
Obx(() => SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: controller.isLoading ? null : controller.toggleTracking,
icon: Icon(controller.isTracking ? Icons.stop : Icons.play_arrow),
label: Text(controller.isTracking ? 'Tracking stoppen' : 'Tracking starten'),
style: ElevatedButton.styleFrom(
backgroundColor: controller.isTracking ? Colors.red : Colors.green,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
)),
const SizedBox(height: 12),
// Permissions Button
Obx(() => SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: controller.isLoading ? null : controller.checkPermissions,
icon: const Icon(Icons.security),
label: const Text('Berechtigungen prüfen'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
)),
const SizedBox(height: 12),
// Settings Buttons Row
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: controller.openLocationSettings,
icon: const Icon(Icons.location_city),
label: const Text('Location'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton.icon(
onPressed: controller.openAppSettings,
icon: const Icon(Icons.settings),
label: const Text('App'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
),
],
), ),
], )),
),
const SizedBox(height: 12),
// Tracking Toggle Button
Obx(() => SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: controller.isLoading ? null : controller.toggleTracking,
icon: Icon(controller.isTracking ? Icons.stop : Icons.play_arrow),
label: Text(controller.isTracking ? 'Tracking stoppen' : 'Tracking starten'),
style: ElevatedButton.styleFrom(
backgroundColor: controller.isTracking ? Colors.red : Colors.green,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
)),
const SizedBox(height: 12),
// Permissions Button
Obx(() => SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: controller.isLoading ? null : controller.checkPermissions,
icon: const Icon(Icons.security),
label: const Text('Berechtigungen prüfen'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
)),
const SizedBox(height: 12),
// Settings Buttons Row
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: controller.openLocationSettings,
icon: const Icon(Icons.location_city),
label: const Text('Location'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
),
const SizedBox(width: 8),
Expanded(
child: ElevatedButton.icon(
onPressed: controller.openAppSettings,
icon: const Icon(Icons.settings),
label: const Text('App'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
),
],
),
],
), ),
],
// Bottom Spacing
const SizedBox(height: 24),
],
),
), ),
), ),
); );
@ -245,6 +256,7 @@ class GeolocationExample extends StatelessWidget {
child: Text( child: Text(
value, value,
style: const TextStyle(fontFamily: 'monospace'), style: const TextStyle(fontFamily: 'monospace'),
overflow: TextOverflow.ellipsis,
), ),
), ),
], ],

View File

@ -0,0 +1,14 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../controllers/login_controller.dart';
class LoginPage extends GetView<LoginController> {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(color: Colors.red.shade300, child: Center(child: Text('Login Page')),),
);
}
}

View File

@ -1,11 +1,14 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../bindings/login_binding.dart';
import '../pages/examples/geolocation_example.dart'; import '../pages/examples/geolocation_example.dart';
import '../bindings/geolocation_binding.dart'; import '../bindings/geolocation_binding.dart';
import '../pages/login/login_view.dart';
/// App Routes Konfiguration /// App Routes Konfiguration
class AppRoutes { class AppRoutes {
static const String home = '/'; static const String home = '/';
static const String geolocation = '/geolocation'; static const String geolocation = '/geolocation';
static const String login = '/login';
/// Route Pages Definition /// Route Pages Definition
static List<GetPage> pages = [ static List<GetPage> pages = [
@ -16,13 +19,20 @@ class AppRoutes {
transition: Transition.cupertino, transition: Transition.cupertino,
transitionDuration: const Duration(milliseconds: 300), transitionDuration: const Duration(milliseconds: 300),
), ),
GetPage(
name: login,
page: () => const LoginPage(),
binding: LoginBinding(), // Dependency Injection
transition: Transition.cupertino,
transitionDuration: const Duration(milliseconds: 300),
),
]; ];
} }
/// Route Namen als Konstanten für typsichere Navigation /// Route Namen als Konstanten für typsichere Navigation
class Routes { // class Routes {
static const String geolocationExample = '/geolocation-example'; // static const String geolocationExample = '/geolocation-example';
} // }
/// Navigation Helper Klasse /// Navigation Helper Klasse
class AppNavigation { class AppNavigation {
@ -31,11 +41,6 @@ class AppNavigation {
Get.toNamed(AppRoutes.geolocation); Get.toNamed(AppRoutes.geolocation);
} }
/// Navigate back
static void back() {
Get.back();
}
/// Navigate and replace current route /// Navigate and replace current route
static void offGeolocation() { static void offGeolocation() {
Get.offNamed(AppRoutes.geolocation); Get.offNamed(AppRoutes.geolocation);
@ -45,4 +50,25 @@ class AppNavigation {
static void offAllToGeolocation() { static void offAllToGeolocation() {
Get.offAllNamed(AppRoutes.geolocation); Get.offAllNamed(AppRoutes.geolocation);
} }
/// Navigate to Login Page
static void toLogin() {
Get.toNamed(AppRoutes.login);
}
/// Navigate and replace current route
static void offLogin() {
Get.offNamed(AppRoutes.login);
}
/// Navigate and clear all previous routes
static void offAllToLogin() {
Get.offAllNamed(AppRoutes.login);
}
/// Navigate back
static void back() {
Get.back();
}
} }

View File

@ -0,0 +1,354 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/foundation.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
/// Einfacher PTV API Service mit statischen Methoden
class PtvApiServiceSimple {
/// Private Konstruktor
PtvApiServiceSimple._();
/// Base URL der PTV API
static const String _baseUrl = 'https://api.myptv.com/geocoding/v1/locations/by-position';
/// Standard Sprache
static const String _defaultLanguage = 'de';
/// Timeout in Sekunden
static const int _timeoutSeconds = 15;
/// API Key aus .env Datei
static String get _apiKey => dotenv.env['PTVE_API_KEY'] ?? '';
/// HTTP Headers
static Map<String, String> get _headers => {
'Accept': 'application/json',
'Content-Type': 'application/json',
'ApiKey': _apiKey,
};
/// Hauptmethode: Holt Location-Daten für gegebene Koordinaten
///
/// Beispiel URL: https://api.myptv.com/geocoding/v1/locations/by-position/48.208282/14.214758?language=de
///
/// [latitude] - Breitengrad (z.B. 48.208282)
/// [longitude] - Längengrad (z.B. 14.214758)
/// [language] - Sprache für Antwort (default: 'de')
///
/// Returns Map mit API-Response oder null bei Fehler
static Future<Map<String, dynamic>?> getLocationsByPosition({
required double latitude,
required double longitude,
String language = _defaultLanguage,
}) async {
try {
// Validiere Koordinaten
if (!_isValidCoordinate(latitude, longitude)) {
if (kDebugMode) {
print('❌ Ungültige Koordinaten: lat=$latitude, lng=$longitude');
}
return null;
}
// Lade .env falls API Key leer
if (_apiKey.isEmpty) {
await dotenv.load(fileName: ".env");
if (_apiKey.isEmpty) {
if (kDebugMode) {
print('❌ PTVE_API_KEY nicht in .env gefunden');
}
return null;
}
}
// Baue URL
final url = '$_baseUrl/$latitude/$longitude?language=$language';
if (kDebugMode) {
print('🌐 PTV API Request: $url');
print('🔑 API Key vorhanden: ${_apiKey.isNotEmpty}');
}
// HTTP GET Request
final response = await http
.get(
Uri.parse(url),
headers: _headers,
)
.timeout(Duration(seconds: _timeoutSeconds));
if (kDebugMode) {
print('📡 Response Status: ${response.statusCode}');
}
// Verarbeite Response
if (response.statusCode == 200) {
final jsonData = json.decode(response.body) as Map<String, dynamic>;
if (kDebugMode) {
print('✅ API Request erfolgreich');
print('📍 Locations gefunden: ${(jsonData['locations'] as List?)?.length ?? 0}');
}
return jsonData;
} else {
if (kDebugMode) {
print('❌ API Fehler: ${response.statusCode}');
print('📄 Response Body: ${response.body}');
}
return null;
}
} catch (e) {
if (kDebugMode) {
print('❌ Exception beim API Aufruf: $e');
}
return null;
}
}
/// Erweiterte Methode mit zusätzlichen Parametern
static Future<Map<String, dynamic>?> getLocationsByPositionAdvanced({
required double latitude,
required double longitude,
String language = _defaultLanguage,
int? maxResults,
int? radius,
}) async {
try {
// Baue URL mit zusätzlichen Query-Parametern
String url = '$_baseUrl/$latitude/$longitude?language=$language';
if (maxResults != null && maxResults > 0) {
url += '&maxResults=$maxResults';
}
if (radius != null && radius > 0) {
url += '&radius=$radius';
}
if (kDebugMode) {
print('🌐 PTV API Advanced Request: $url');
}
final response = await http
.get(
Uri.parse(url),
headers: _headers,
)
.timeout(Duration(seconds: _timeoutSeconds));
if (response.statusCode == 200) {
return json.decode(response.body) as Map<String, dynamic>;
} else {
if (kDebugMode) {
print('❌ Advanced API Fehler: ${response.statusCode}');
}
return null;
}
} catch (e) {
if (kDebugMode) {
print('❌ Exception bei erweiterter API Abfrage: $e');
}
return null;
}
}
/// Hilfsmethoden
/// Validiert GPS-Koordinaten
static bool _isValidCoordinate(double latitude, double longitude) {
return latitude >= -90 &&
latitude <= 90 &&
longitude >= -180 &&
longitude <= 180;
}
/// Prüft API-Verfügbarkeit mit Test-Request
static Future<bool> testApiConnection() async {
try {
if (kDebugMode) {
print('\n🧪 PTV API CONNECTION TEST 🧪');
}
// Test mit Wien Koordinaten
final result = await getLocationsByPosition(
latitude: 48.2082,
longitude: 16.3738,
);
final success = result != null;
if (kDebugMode) {
print(success ? '✅ API Test erfolgreich' : '❌ API Test fehlgeschlagen');
print('🧪 TEST ABGESCHLOSSEN\n');
}
return success;
} catch (e) {
if (kDebugMode) {
print('❌ Test Exception: $e');
print('🧪 TEST ABGESCHLOSSEN\n');
}
return false;
}
}
/// Debugging: Zeigt Service-Informationen
static void printServiceInfo() {
if (kDebugMode) {
print('\n📋 PTV API SERVICE INFO 📋');
print('Base URL: $_baseUrl');
print('Default Language: $_defaultLanguage');
print('Timeout: ${_timeoutSeconds}s');
print('API Key verfügbar: ${_apiKey.isNotEmpty}');
print('API Key Length: ${_apiKey.length}');
print('📋 SERVICE INFO END 📋\n');
}
}
/// Demonstration der API-Verwendung
static Future<void> runDemo() async {
if (kDebugMode) {
print('\n🚀 PTV API DEMO START 🚀');
// Service Info
printServiceInfo();
// Test Connection
await testApiConnection();
// Beispiel 1: Traun, Österreich
print('📍 Beispiel 1: Traun, Österreich');
final traunResult = await getLocationsByPosition(
latitude: 48.208282,
longitude: 14.214758,
);
if (traunResult != null) {
final locations = traunResult['locations'] as List?;
if (locations != null && locations.isNotEmpty) {
final firstLocation = locations.first;
print('✅ Gefunden: ${firstLocation['formattedAddress']}');
print('📍 Koordinaten: ${firstLocation['referencePosition']['latitude']}, ${firstLocation['referencePosition']['longitude']}');
final quality = firstLocation['quality'];
if (quality != null) {
print('🎯 Genauigkeit: ${quality['distance']}m');
}
}
} else {
print('❌ Keine Daten für Traun erhalten');
}
// Beispiel 2: Wien mit erweiterten Parametern
print('\n📍 Beispiel 2: Wien (erweitert)');
final wienResult = await getLocationsByPositionAdvanced(
latitude: 48.2082,
longitude: 16.3738,
maxResults: 3,
radius: 500,
);
if (wienResult != null) {
final locations = wienResult['locations'] as List?;
print('✅ Wien Locations gefunden: ${locations?.length ?? 0}');
if (locations != null) {
for (int i = 0; i < locations.length && i < 3; i++) {
final location = locations[i];
print(' ${i + 1}. ${location['formattedAddress']}');
}
}
}
print('🚀 DEMO ABGESCHLOSSEN 🚀\n');
}
}
}
/// Utility-Klasse für häufige PTV-Operationen
class PtvApiUtils {
/// Extrahiert die beste Location aus der API-Response
static Map<String, dynamic>? getBestLocation(Map<String, dynamic> apiResponse) {
final locations = apiResponse['locations'] as List?;
if (locations == null || locations.isEmpty) return null;
// Suche Location mit geringster Distanz
Map<String, dynamic>? bestLocation;
double? bestDistance;
for (final location in locations) {
final quality = location['quality'];
if (quality != null) {
final distance = (quality['distance'] as num?)?.toDouble();
if (distance != null && (bestDistance == null || distance < bestDistance)) {
bestDistance = distance;
bestLocation = location;
}
}
}
return bestLocation ?? locations.first;
}
/// Extrahiert formatierte Adresse
static String? getFormattedAddress(Map<String, dynamic> apiResponse) {
final bestLocation = getBestLocation(apiResponse);
return bestLocation?['formattedAddress'] as String?;
}
/// Extrahiert Koordinaten
static Map<String, double>? getCoordinates(Map<String, dynamic> apiResponse) {
final bestLocation = getBestLocation(apiResponse);
final refPos = bestLocation?['referencePosition'];
if (refPos != null) {
return {
'latitude': (refPos['latitude'] as num?)?.toDouble() ?? 0.0,
'longitude': (refPos['longitude'] as num?)?.toDouble() ?? 0.0,
};
}
return null;
}
/// Prüft Qualität der Location
static String getQualityLevel(Map<String, dynamic> apiResponse) {
final bestLocation = getBestLocation(apiResponse);
final quality = bestLocation?['quality'];
if (quality != null) {
final distance = (quality['distance'] as num?)?.toDouble() ?? 999999;
if (distance <= 10) return 'Sehr hoch';
if (distance <= 50) return 'Hoch';
if (distance <= 100) return 'Mittel';
if (distance <= 500) return 'Niedrig';
return 'Sehr niedrig';
}
return 'Unbekannt';
}
/// Konvertiert API-Response zu lesbarem String
static String formatLocationInfo(Map<String, dynamic> apiResponse) {
final bestLocation = getBestLocation(apiResponse);
if (bestLocation == null) return 'Keine Location gefunden';
final address = bestLocation['formattedAddress'] ?? 'Unbekannte Adresse';
final refPos = bestLocation['referencePosition'];
final quality = bestLocation['quality'];
String info = 'Adresse: $address\n';
if (refPos != null) {
info += 'Koordinaten: ${refPos['latitude']}, ${refPos['longitude']}\n';
}
if (quality != null) {
info += 'Genauigkeit: ${quality['distance']}m (${getQualityLevel(apiResponse)})\n';
}
return info;
}
}

View File

@ -6,6 +6,18 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <desktop_webview_window/desktop_webview_window_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
#include <window_to_front/window_to_front_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) desktop_webview_window_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopWebviewWindowPlugin");
desktop_webview_window_plugin_register_with_registrar(desktop_webview_window_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
g_autoptr(FlPluginRegistrar) window_to_front_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowToFrontPlugin");
window_to_front_plugin_register_with_registrar(window_to_front_registrar);
} }

View File

@ -3,6 +3,9 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
desktop_webview_window
url_launcher_linux
window_to_front
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -5,10 +5,22 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import desktop_webview_window
import device_info_plus
import flutter_web_auth_2
import geolocator_apple import geolocator_apple
import package_info_plus import package_info_plus
import path_provider_foundation
import url_launcher_macos
import window_to_front
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DesktopWebviewWindowPlugin.register(with: registry.registrar(forPlugin: "DesktopWebviewWindowPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FlutterWebAuth2Plugin.register(with: registry.registrar(forPlugin: "FlutterWebAuth2Plugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WindowToFrontPlugin.register(with: registry.registrar(forPlugin: "WindowToFrontPlugin"))
} }

View File

@ -1,6 +1,14 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
appwrite:
dependency: "direct main"
description:
name: appwrite
sha256: "358a443ba8366d48fc8cdec88645b97eb8f3941ea4244c2e6d524603e4d7f27e"
url: "https://pub.dev"
source: hosted
version: "17.0.0"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -49,6 +57,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.1" version: "1.19.1"
cookie_jar:
dependency: transitive
description:
name: cookie_jar
sha256: a6ac027d3ed6ed756bfce8f3ff60cb479e266f3b0fdabd6242b804b6765e52de
url: "https://pub.dev"
source: hosted
version: "4.0.8"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -73,6 +89,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.11" version: "0.7.11"
desktop_webview_window:
dependency: transitive
description:
name: desktop_webview_window
sha256: "57cf20d81689d5cbb1adfd0017e96b669398a669d927906073b0e42fc64111c0"
url: "https://pub.dev"
source: hosted
version: "0.2.3"
device_info_plus:
dependency: transitive
description:
name: device_info_plus
sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074
url: "https://pub.dev"
source: hosted
version: "10.1.2"
device_info_plus_platform_interface:
dependency: transitive
description:
name: device_info_plus_platform_interface
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
url: "https://pub.dev"
source: hosted
version: "7.0.3"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -89,6 +129,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
@ -102,6 +150,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_dotenv:
dependency: "direct main"
description:
name: flutter_dotenv
sha256: d4130c4a43e0b13fefc593bc3961f2cb46e30cb79e253d4a526b1b5d24ae1ce4
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -115,6 +171,22 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_web_auth_2:
dependency: transitive
description:
name: flutter_web_auth_2
sha256: "3c14babeaa066c371f3a743f204dd0d348b7d42ffa6fae7a9847a521aff33696"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
flutter_web_auth_2_platform_interface:
dependency: transitive
description:
name: flutter_web_auth_2_platform_interface
sha256: c63a472c8070998e4e422f6b34a17070e60782ac442107c70000dd1bed645f4d
url: "https://pub.dev"
source: hosted
version: "4.1.0"
flutter_web_plugins: flutter_web_plugins:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -296,6 +368,54 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.1" version: "1.9.1"
path_provider:
dependency: transitive
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: e122c5ea805bb6773bb12ce667611265980940145be920cd09a4b0ec0285cb16
url: "https://pub.dev"
source: hosted
version: "2.2.20"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: efaec349ddfc181528345c56f8eda9d6cccd71c177511b132c6a0ddaefaa2738
url: "https://pub.dev"
source: hosted
version: "2.4.3"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
url: "https://pub.dev"
source: hosted
version: "2.3.0"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
@ -304,6 +424,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.0.1" version: "7.0.1"
platform:
dependency: transitive
description:
name: platform
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.dev"
source: hosted
version: "3.1.6"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -381,6 +509,78 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
universal_io:
dependency: transitive
description:
name: universal_io
sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad"
url: "https://pub.dev"
source: hosted
version: "2.2.2"
url_launcher:
dependency: transitive
description:
name: url_launcher
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
url: "https://pub.dev"
source: hosted
version: "6.3.2"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "5c8b6c2d89a78f5a1cca70a73d9d5f86c701b36b42f9c9dac7bad592113c28e9"
url: "https://pub.dev"
source: hosted
version: "6.3.24"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: "6b63f1441e4f653ae799166a72b50b1767321ecc263a57aadf825a7a2a5477d9"
url: "https://pub.dev"
source: hosted
version: "6.3.5"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "8262208506252a3ed4ff5c0dc1e973d2c0e0ef337d0a074d35634da5d44397c9"
url: "https://pub.dev"
source: hosted
version: "3.2.4"
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: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77"
url: "https://pub.dev"
source: hosted
version: "3.1.4"
uuid: uuid:
dependency: transitive dependency: transitive
description: description:
@ -413,6 +613,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
url: "https://pub.dev"
source: hosted
version: "3.0.3"
win32: win32:
dependency: transitive dependency: transitive
description: description:
@ -421,6 +637,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.15.0" version: "5.15.0"
win32_registry:
dependency: transitive
description:
name: win32_registry
sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852"
url: "https://pub.dev"
source: hosted
version: "1.1.5"
window_to_front:
dependency: transitive
description:
name: window_to_front
sha256: "7aef379752b7190c10479e12b5fd7c0b9d92adc96817d9e96c59937929512aee"
url: "https://pub.dev"
source: hosted
version: "0.0.3"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -439,4 +671,4 @@ packages:
version: "6.6.1" version: "6.6.1"
sdks: sdks:
dart: ">=3.9.2 <4.0.0" dart: ">=3.9.2 <4.0.0"
flutter: ">=3.19.0" flutter: ">=3.35.0"

View File

@ -16,6 +16,8 @@ dependencies:
get: ^4.7.2 get: ^4.7.2
http: ^1.5.0 http: ^1.5.0
geolocator: ^14.0.2 geolocator: ^14.0.2
flutter_dotenv: ^6.0.0
appwrite: 17.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -28,3 +30,4 @@ flutter:
assets: assets:
- assets/img/ - assets/img/
- .env

View File

@ -0,0 +1,61 @@
// ignore_for_file: avoid_print
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:web_flutter_tank_appwrite_app/services/ptv_api_simple.dart';
/// Quick Test der PTV API Simple Integration
void main() async {
print('🚀 PTV API SIMPLE TEST START 🚀');
try {
// Lade .env
await dotenv.load(fileName: ".env");
print('✅ .env Datei geladen');
// Test Service Info
PtvApiServiceSimple.printServiceInfo();
// Connection Test
print('\n🧪 CONNECTION TEST 🧪');
final connectionOk = await PtvApiServiceSimple.testApiConnection();
print('🔗 Verbindung: ${connectionOk ? "OK" : "FEHLER"}');
if (connectionOk) {
print('\n📍 API TEST MIT BEISPIELKOORDINATEN 📍');
// Test mit Traun Koordinaten
final result = await PtvApiServiceSimple.getLocationsByPosition(
latitude: 48.208282,
longitude: 14.214758,
);
if (result != null) {
print('✅ API Response erhalten');
// Verwende Utility-Funktionen
final address = PtvApiUtils.getFormattedAddress(result);
final coords = PtvApiUtils.getCoordinates(result);
final quality = PtvApiUtils.getQualityLevel(result);
print('📝 Adresse: $address');
print('📍 Koordinaten: ${coords?['latitude']}, ${coords?['longitude']}');
print('⭐ Qualität: $quality');
print('\n📄 Komplette Info:');
print(PtvApiUtils.formatLocationInfo(result));
} else {
print('❌ Keine API Response - prüfe API Key in .env');
}
} else {
print('❌ API Connection Test fehlgeschlagen');
print('⚠️ Prüfe PTVE_API_KEY in .env Datei');
}
} catch (e) {
print('❌ Test Exception: $e');
}
print('\n🚀 TEST ABGESCHLOSSEN 🚀');
}

View File

@ -6,9 +6,18 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <desktop_webview_window/desktop_webview_window_plugin.h>
#include <geolocator_windows/geolocator_windows.h> #include <geolocator_windows/geolocator_windows.h>
#include <url_launcher_windows/url_launcher_windows.h>
#include <window_to_front/window_to_front_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
DesktopWebviewWindowPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DesktopWebviewWindowPlugin"));
GeolocatorWindowsRegisterWithRegistrar( GeolocatorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GeolocatorWindows")); registry->GetRegistrarForPlugin("GeolocatorWindows"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
WindowToFrontPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("WindowToFrontPlugin"));
} }

View File

@ -3,7 +3,10 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
desktop_webview_window
geolocator_windows geolocator_windows
url_launcher_windows
window_to_front
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST