import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart' as mapbox; import 'package:shared_preferences/shared_preferences.dart'; import 'package:geolocator/geolocator.dart' as geo; import '../classes/alert.dart'; import '../variable/globals.dart' as globals; import '../classes/MyDrawer.dart'; import '../classes/auth_service.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:covas_mobile/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import '../locale_provider.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await dotenv.load(fileName: ".env"); // Set the access token globally mapbox.MapboxOptions.setAccessToken(dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? ''); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Directions Example', theme: ThemeData(primarySwatch: Colors.blue), home: const MapboxPages(title: 'Event Location', place: "Flutter"), ); } } class MapboxPages extends StatefulWidget { const MapboxPages({Key? key, required this.title, required this.place}) : super(key: key); final String title; final String place; @override State createState() => _MapboxPagesState(); } class _MapboxPagesState extends State with ShowAlertDialog { final AuthService _authService = AuthService(); mapbox.MapboxMap? mapboxMap; mapbox.PointAnnotationManager? pointAnnotationManager; mapbox.PolylineAnnotationManager? polylineAnnotationManager; double longitude = 0.0; double latitude = 0.0; bool isLoading = true; mapbox.Point? userPosition; bool isUserPositionInitialized = false; String selectedMode = 'driving'; List> routeCoordinates = []; @override void initState() { super.initState(); _authService.checkTokenStatus(context); _getUserLocation(); } Future _getEventInfo() async { SharedPreferences prefs = await SharedPreferences.getInstance(); var accessToken = prefs.getString("access_token") ?? ""; if (accessToken.isNotEmpty) { var urlGet = Uri.parse("${globals.api}/events/${widget.title}"); var responseGet = await http.get(urlGet, headers: {HttpHeaders.cookieHeader: 'access_token=${accessToken}'}); if (responseGet.statusCode == 200) { var events = jsonDecode(utf8.decode(responseGet.bodyBytes)); latitude = events["latitude"]; longitude = events["longitude"]; setState(() => isLoading = false); } else { _handleErrorResponse(responseGet.statusCode); } } else { showAlertDialog(context, "Error", "Invalid cache."); } } void _handleErrorResponse(int statusCode) { final errorMessage = "Error $statusCode fetching event"; showAlertDialog(context, "Error", errorMessage); } Future _getUserLocation() async { await dotenv.load(fileName: ".env"); // Set the access token globally mapbox.MapboxOptions.setAccessToken( dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? ''); try { bool serviceEnabled = await geo.Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) return; geo.LocationPermission permission = await geo.Geolocator.checkPermission(); if (permission == geo.LocationPermission.denied) { permission = await geo.Geolocator.requestPermission(); } if (permission == geo.LocationPermission.deniedForever) return; geo.Position position = await geo.Geolocator.getCurrentPosition(); setState(() { userPosition = mapbox.Point( coordinates: mapbox.Position(position.longitude, position.latitude)); isUserPositionInitialized = true; }); _getEventInfo(); } catch (e) { showAlertDialog(context, "Error", "Failed to get location"); } } Future _fetchRoute( mapbox.Point origin, mapbox.Point destination, String mode) async { final url = Uri.parse( 'https://api.mapbox.com/directions/v5/mapbox/$mode/' '${origin.coordinates.lng},${origin.coordinates.lat};' '${destination.coordinates.lng},${destination.coordinates.lat}' '?geometries=geojson&access_token=${dotenv.env['MAPBOX_ACCESS_TOKEN']}', ); final response = await http.get(url); if (response.statusCode == 200) { final data = jsonDecode(response.body); // Vérifie si 'routes' existe et contient au moins 1 élément if (data['routes'] != null && (data['routes'] as List).isNotEmpty) { final geometry = data['routes'][0]['geometry']['coordinates']; setState(() { routeCoordinates = (geometry as List) .map>((coord) => [coord[0], coord[1]]) .toList(); }); } else { debugPrint("⚠️ Aucune route trouvée entre ${origin} et $destination."); // Optionnel : afficher un snackbar/toast à l’utilisateur } } else { debugPrint("❌ Erreur API Mapbox: ${response.statusCode}"); } } Future _zoomToFitRoute(List> coordinates) async { if (mapboxMap == null || coordinates.isEmpty) return; double minLat = coordinates.first[1]; double maxLat = coordinates.first[1]; double minLng = coordinates.first[0]; double maxLng = coordinates.first[0]; for (var coord in coordinates) { if (coord[1] < minLat) minLat = coord[1]; if (coord[1] > maxLat) maxLat = coord[1]; if (coord[0] < minLng) minLng = coord[0]; if (coord[0] > maxLng) maxLng = coord[0]; } final bounds = mapbox.CoordinateBounds( southwest: mapbox.Point(coordinates: mapbox.Position(minLng, minLat)), northeast: mapbox.Point(coordinates: mapbox.Position(maxLng, maxLat)), infiniteBounds: true); // Calculer une CameraOptions automatiquement à partir des bounds final cameraOptions = await mapboxMap!.cameraForCoordinateBounds( bounds, mapbox.MbxEdgeInsets( top: 50, left: 50, right: 50, bottom: 50), // marges 0.0, 0.0, null, null); // Appliquer la caméra avec animation await mapboxMap!.flyTo( cameraOptions, mapbox.MapAnimationOptions(duration: 1000), ); } Future _drawRouteAndMarkers() async { if (mapboxMap == null || !isUserPositionInitialized) return; // Managers pointAnnotationManager ??= await mapboxMap!.annotations.createPointAnnotationManager(); polylineAnnotationManager ??= await mapboxMap!.annotations.createPolylineAnnotationManager(); // Clear old annotations await pointAnnotationManager!.deleteAll(); await polylineAnnotationManager!.deleteAll(); final destination = mapbox.Point(coordinates: mapbox.Position(longitude, latitude)); // Add user marker final userIcon = await _loadMarkerImage('images/marker.png'); await pointAnnotationManager!.create(mapbox.PointAnnotationOptions( geometry: mapbox.Point( coordinates: mapbox.Position( userPosition!.coordinates.lng, userPosition!.coordinates.lat)), image: userIcon, iconSize: 0.4, )); // Ajoute directement la flèche rouge à la position de l’événement final eventIcon = await _loadMarkerImage('images/marker-red.png'); await pointAnnotationManager!.create(mapbox.PointAnnotationOptions( geometry: mapbox.Point(coordinates: mapbox.Position(longitude, latitude)), image: eventIcon, iconSize: 0.2, )); // Fetch and draw route await _fetchRoute(userPosition!, destination, selectedMode); if (routeCoordinates.isNotEmpty) { await polylineAnnotationManager!.create(mapbox.PolylineAnnotationOptions( geometry: mapbox.LineString( coordinates: routeCoordinates.map((c) => mapbox.Position(c[0], c[1])).toList(), ), lineColor: Colors.blue.value, lineWidth: 4.0, )); await _zoomToFitRoute(routeCoordinates); } } Future _loadMarkerImage(String assetPath) async { final ByteData data = await rootBundle.load(assetPath); return data.buffer.asUint8List(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(widget.place)), drawer: MyDrawer(), body: isLoading ? const Center(child: CircularProgressIndicator()) : Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text("Mode : "), DropdownButton( value: selectedMode, items: const [ DropdownMenuItem( value: 'driving', child: Text("🚗 Voiture")), DropdownMenuItem( value: 'walking', child: Text("🚶 Marche")), DropdownMenuItem( value: 'cycling', child: Text("🚴 Vélo")), ], onChanged: (value) { if (value != null) { setState(() { selectedMode = value; }); } }, ), ], ), Expanded( child: mapbox.MapWidget( onMapCreated: (controller) async { mapboxMap = controller; // Crée un manager si nécessaire pointAnnotationManager ??= await mapboxMap!.annotations .createPointAnnotationManager(); // Ajoute directement la flèche rouge à la position de l’événement final eventIcon = await _loadMarkerImage('images/marker-red.png'); await pointAnnotationManager! .create(mapbox.PointAnnotationOptions( geometry: mapbox.Point( coordinates: mapbox.Position(longitude, latitude)), image: eventIcon, iconSize: 0.2, )); }, cameraOptions: mapbox.CameraOptions( center: mapbox.Point( coordinates: mapbox.Position(longitude, latitude)), zoom: 14.0, ), ), ), ], ), floatingActionButton: FloatingActionButton( onPressed: _drawRouteAndMarkers, child: const Icon(Icons.directions), ), ); } }