diff --git a/covas_mobile_new/images/flutter.png b/covas_mobile_new/images/flutter.png new file mode 100644 index 0000000..e02f153 Binary files /dev/null and b/covas_mobile_new/images/flutter.png differ diff --git a/covas_mobile_new/images/marker-red.png b/covas_mobile_new/images/marker-red.png new file mode 100644 index 0000000..5937369 Binary files /dev/null and b/covas_mobile_new/images/marker-red.png differ diff --git a/covas_mobile_new/images/marker.png b/covas_mobile_new/images/marker.png new file mode 100644 index 0000000..fa91bb4 Binary files /dev/null and b/covas_mobile_new/images/marker.png differ diff --git a/covas_mobile_new/images/search.png b/covas_mobile_new/images/search.png new file mode 100644 index 0000000..9454673 Binary files /dev/null and b/covas_mobile_new/images/search.png differ diff --git a/covas_mobile_new/lib/pages/MapboxPages.dart b/covas_mobile_new/lib/pages/MapboxPages.dart index a345fcc..75a8c1e 100644 --- a/covas_mobile_new/lib/pages/MapboxPages.dart +++ b/covas_mobile_new/lib/pages/MapboxPages.dart @@ -1,14 +1,15 @@ import 'dart:convert'; import 'dart:io'; +import 'dart:typed_data'; 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:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; -import 'package:mapbox_gl/mapbox_gl.dart'; +import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart' as mapbox; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:geolocator/geolocator.dart'; // For getting the user's location +import 'package:geolocator/geolocator.dart' as geo; -import '../classes/alert.dart'; // Assuming this contains your error dialog code. +import '../classes/alert.dart'; import '../variable/globals.dart' as globals; import '../classes/MyDrawer.dart'; import '../classes/auth_service.dart'; @@ -16,10 +17,15 @@ 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'; // +import '../locale_provider.dart'; void main() async { - await dotenv.load(fileName: ".env"); // Load .env file + WidgetsFlutterBinding.ensureInitialized(); + await dotenv.load(fileName: ".env"); + + // Set the access token globally + mapbox.MapboxOptions.setAccessToken(dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? ''); + runApp(const MyApp()); } @@ -30,9 +36,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Directions Example', - theme: ThemeData( - primarySwatch: Colors.blue, - ), + theme: ThemeData(primarySwatch: Colors.blue), home: const MapboxPages(title: 'Event Location', place: "Flutter"), ); } @@ -52,36 +56,26 @@ class MapboxPages extends StatefulWidget { class _MapboxPagesState extends State with ShowAlertDialog { final AuthService _authService = AuthService(); - late MapboxMapController mapController; - late String mapboxAccessToken; - List routeCoordinates = []; - String selectedMode = 'driving'; + mapbox.MapboxMap? mapboxMap; + mapbox.PointAnnotationManager? pointAnnotationManager; + mapbox.PolylineAnnotationManager? polylineAnnotationManager; + double longitude = 0.0; double latitude = 0.0; bool isLoading = true; - late LatLng userPosition; + mapbox.Point? userPosition; bool isUserPositionInitialized = false; - Line? currentRouteLine; + + String selectedMode = 'driving'; + List> routeCoordinates = []; @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") ?? ""; @@ -96,119 +90,51 @@ class _MapboxPagesState extends State with ShowAlertDialog { latitude = events["latitude"]; longitude = events["longitude"]; - setState(() { - isLoading = false; - }); + setState(() => isLoading = false); } else { _handleErrorResponse(responseGet.statusCode); } } else { - showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error", - AppLocalizations.of(context)?.invalid_cache ?? "Invalid cache."); + showAlertDialog(context, "Error", "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); + final errorMessage = "Error $statusCode fetching event"; + showAlertDialog(context, "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; + 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(); } - 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 == geo.LocationPermission.deniedForever) return; + + geo.Position position = await geo.Geolocator.getCurrentPosition(); + setState(() { + userPosition = mapbox.Point( + coordinates: + mapbox.Position(position.latitude, position.longitude)); + isUserPositionInitialized = true; + }); - 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"); + showAlertDialog(context, "Error", "Failed to get location"); } } Future _fetchRoute( - LatLng origin, LatLng destination, String mode) async { + mapbox.Point origin, mapbox.Point 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', + '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); @@ -216,156 +142,63 @@ class _MapboxPagesState extends State with ShowAlertDialog { final data = jsonDecode(response.body); final geometry = data['routes'][0]['geometry']['coordinates']; setState(() { - routeCoordinates = geometry.map((coord) { - return LatLng(coord[1], coord[0]); - }).toList(); + routeCoordinates = (geometry as List) + .map>((coord) => [coord[0], coord[1]]) + .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 (mapboxMap == null || !isUserPositionInitialized) return; - if (mapController != null && - userPosition != null && - latitude != 0.0 && - longitude != 0.0) { - final destination = LatLng(latitude, longitude); + // Managers + pointAnnotationManager ??= + await mapboxMap!.annotations.createPointAnnotationManager(); + polylineAnnotationManager ??= + await mapboxMap!.annotations.createPolylineAnnotationManager(); - // Register the custom images - // Add event marker + // Clear old annotations + await pointAnnotationManager!.deleteAll(); + await polylineAnnotationManager!.deleteAll(); - final eventMarkerImage = await _loadMarkerImage('images/marker-red.png'); + final destination = + mapbox.Point(coordinates: mapbox.Position(longitude, latitude)); - // 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, + // 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, + )); + + // Add event marker + final eventIcon = await _loadMarkerImage('images/marker-red.png'); + await pointAnnotationManager!.create(mapbox.PointAnnotationOptions( + geometry: mapbox.Point( + coordinates: mapbox.Position( + destination.coordinates.lng, destination.coordinates.lat)), + image: eventIcon, + iconSize: 0.4, + )); + + // 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, )); - - // 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(); @@ -374,78 +207,23 @@ class _MapboxPagesState extends State with ShowAlertDialog { @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!; - }); - }, - ) - ], - ), + appBar: AppBar(title: Text(widget.place)), 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', + body: isLoading + ? const Center(child: CircularProgressIndicator()) + : mapbox.MapWidget( + onMapCreated: (controller) { + mapboxMap = controller; + }, + cameraOptions: mapbox.CameraOptions( + center: mapbox.Point( + coordinates: mapbox.Position(longitude, latitude)), + zoom: 14.0, + ), ), - ), - ], + floatingActionButton: FloatingActionButton( + onPressed: _drawRouteAndMarkers, + child: const Icon(Icons.directions), ), ); }