import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; // For environment variables import 'package:flutter/services.dart'; // For loading assets import 'package:http/http.dart' as http; import 'package:mapbox_gl/mapbox_gl.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:geolocator/geolocator.dart'; // For getting the user's location import '../classes/alert.dart'; // Assuming this contains your error dialog code. import '../variable/globals.dart' as globals; import '../classes/MyDrawer.dart'; import '../classes/auth_service.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:provider/provider.dart'; import '../locale_provider.dart'; // void main() async { await dotenv.load(fileName: ".env"); // Load .env file 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(); late MapboxMapController mapController; late String mapboxAccessToken; List routeCoordinates = []; String selectedMode = 'driving'; double longitude = 0.0; double latitude = 0.0; bool isLoading = true; late LatLng userPosition; bool isUserPositionInitialized = false; Line? currentRouteLine; @override void initState() { super.initState(); _authService.checkTokenStatus(context); _getUserLocation(); } void _initToken() { mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? ''; if (mapboxAccessToken.isEmpty) { showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.map_token ?? "Map Access Token is not available."); } } 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, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.invalid_cache ?? "Invalid cache."); } } void _handleErrorResponse(int statusCode) { final messages = { 400: AppLocalizations.of(context)?.request_error ?? "Poorly constructed query", 406: AppLocalizations.of(context)?.incorrect_password ?? "Incorrect password", 404: AppLocalizations.of(context)?.unknown_user ?? "Unknown user", 403: AppLocalizations.of(context)?.disabled_user ?? "Disabled user", 410: AppLocalizations.of(context)?.invalid_token ?? "Invalid token", 500: AppLocalizations.of(context)?.internal_error_server ?? "Internal error server" }; final errorMessage = messages[statusCode] ?? AppLocalizations.of(context)?.unknown_error_auth ?? "Unknown error auth"; showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", errorMessage); } Future _getUserLocation() async { try { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.geo_disabled ?? "Location services are disabled."); return; } LocationPermission permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.permission_denied ?? "Location permissions are denied."); return; } } if (permission == LocationPermission.deniedForever) { showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.enable_permission ?? "Location permissions are permanently denied. Enable them in settings."); return; } const LocationSettings locationSettings = LocationSettings( accuracy: LocationAccuracy.medium, timeLimit: Duration(seconds: 5)); Position? position; try { position = await Geolocator.getCurrentPosition( locationSettings: locationSettings); } on LocationServiceDisabledException { // Handle location services disabled position = await Geolocator.getLastKnownPosition(); if (position == null) { showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.no_last_position ?? "No last known position available.."); } } catch (e) { // Handle other errors position = await Geolocator.getLastKnownPosition(); if (position == null) { showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.no_last_position ?? "No last known position available"); } } if (position != null) { setState(() { userPosition = LatLng(position!.latitude, position!.longitude); isUserPositionInitialized = true; }); } _initToken(); _getEventInfo(); } catch (e) { showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.failed_location ?? "Failed to get user location"); } } Future _fetchRoute( LatLng origin, LatLng destination, String mode) async { final url = Uri.parse( 'https://api.mapbox.com/directions/v5/mapbox/$mode/${origin.longitude},${origin.latitude};${destination.longitude},${destination.latitude}?geometries=geojson&access_token=$mapboxAccessToken', ); final response = await http.get(url); if (response.statusCode == 200) { final data = jsonDecode(response.body); final geometry = data['routes'][0]['geometry']['coordinates']; setState(() { routeCoordinates = geometry.map((coord) { return LatLng(coord[1], coord[0]); }).toList(); }); } else { showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.failed_fetch ?? "Failed to fetch the route"); } } // Called when the map is created void _onStyleLoaded() async { // Log the map controller and coordinates // Check if the mapController is really initialized if (mapController != null) { try { // Ensure the coordinates are valid if (latitude != 0.0 && longitude != 0.0) { // Load marker image as Uint8List final userMarkerImage = await _loadMarkerImage('images/marker.png'); // Register the image with Mapbox await mapController.addImage('event-marker', userMarkerImage); final symbolOptions = SymbolOptions( geometry: LatLng(latitude, longitude), iconImage: "event-marker", // Use the registered custom marker iconSize: 0.4, // Optional: Adjust size ); // Debugging symbol options // Add symbol to map mapController!.addSymbol(symbolOptions); } else { showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.invalid_coordinates_symbol ?? "Error: Invalid coordinates, cannot add symbol."); } } catch (e) { // Handle any exception that occurs when adding the symbol showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.error_symbol ?? "Error when adding symbol."); } } else { showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.error_symbol ?? "Error when adding symbol."); } } Future _drawRouteAndMarkers() async { // Remove previous route line if it exists if (currentRouteLine != null) { await mapController.removeLine(currentRouteLine!); currentRouteLine = null; } if (!isUserPositionInitialized) { showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.position_not_init ?? "User position is not yet initialized. Try again."); return; } if (mapController != null && userPosition != null && latitude != 0.0 && longitude != 0.0) { final destination = LatLng(latitude, longitude); // Register the custom images // Add event marker final eventMarkerImage = await _loadMarkerImage('images/marker-red.png'); // Register the image with Mapbox await mapController.addImage('user-marker', eventMarkerImage); await mapController.addSymbol(SymbolOptions( geometry: userPosition, iconImage: 'user-marker', // Custom icon for event iconSize: 0.2, )); // Fetch and draw route await _fetchRoute(userPosition, destination, selectedMode); if (routeCoordinates.isNotEmpty) { currentRouteLine = await mapController.addLine( LineOptions( geometry: routeCoordinates, lineColor: '#3b9ddd', lineWidth: 5.0, lineOpacity: 0.8, ), ); _zoomToFitRoute(routeCoordinates); } } else { showAlertDialog( context, AppLocalizations.of(context)?.error ?? "Error", AppLocalizations.of(context)?.invalid_coordinates ?? "Invalid coordinates or user position."); } } void _zoomToFitRoute(List coordinates) { // Calculate the bounding box double minLat = coordinates.first.latitude; double maxLat = coordinates.first.latitude; double minLng = coordinates.first.longitude; double maxLng = coordinates.first.longitude; for (LatLng coord in coordinates) { if (coord.latitude < minLat) minLat = coord.latitude; if (coord.latitude > maxLat) maxLat = coord.latitude; if (coord.longitude < minLng) minLng = coord.longitude; if (coord.longitude > maxLng) maxLng = coord.longitude; } // Define the bounds LatLng southwest = LatLng(minLat, minLng); LatLng northeast = LatLng(maxLat, maxLng); mapController.moveCamera( CameraUpdate.newLatLngBounds( LatLngBounds(southwest: southwest, northeast: northeast), left: 50, // Padding on the left top: 50, // Padding on the top right: 50, // Padding on the right bottom: 50, // Padding on the bottom ), ); } // Load image from assets 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), actions: [ DropdownButton( value: selectedMode, items: [ DropdownMenuItem( value: 'walking', child: Row( children: [ Icon(Icons.directions_walk, color: Colors.blue), SizedBox(width: 8), Text(AppLocalizations.of(context)?.walking ?? 'Walking'), ], ), ), DropdownMenuItem( value: 'cycling', child: Row( children: [ Icon(Icons.directions_bike, color: Colors.green), SizedBox(width: 8), Text(AppLocalizations.of(context)?.cycling ?? 'Cycling'), ], ), ), DropdownMenuItem( value: 'driving', child: Row( children: [ Icon(Icons.directions_car, color: Colors.red), SizedBox(width: 8), Text(AppLocalizations.of(context)?.driving ?? 'Driving'), ], ), ), ], onChanged: (mode) { setState(() { selectedMode = mode!; }); }, ) ], ), drawer: MyDrawer(), body: Stack( children: [ isLoading ? Center(child: CircularProgressIndicator()) : MapboxMap( accessToken: mapboxAccessToken, onMapCreated: (controller) { mapController = controller; }, onStyleLoadedCallback: _onStyleLoaded, initialCameraPosition: CameraPosition( target: LatLng(latitude, longitude), zoom: 14.0, ), ), Positioned( bottom: 20, right: 20, child: FloatingActionButton( onPressed: _drawRouteAndMarkers, child: Icon(Icons.directions), tooltip: AppLocalizations.of(context)?.get_direction ?? 'Get Directions and Markers', ), ), ], ), ); } }