fix mapbox pages

This commit is contained in:
2025-09-01 22:14:08 +02:00
parent 6d504139b0
commit 3310bd572e
5 changed files with 104 additions and 326 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,14 +1,15 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; // For environment variables import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter/services.dart'; // For loading assets import 'package:flutter/services.dart';
import 'package:http/http.dart' as http; 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: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 '../variable/globals.dart' as globals;
import '../classes/MyDrawer.dart'; import '../classes/MyDrawer.dart';
import '../classes/auth_service.dart'; import '../classes/auth_service.dart';
@@ -16,10 +17,15 @@ import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:covas_mobile/gen_l10n/app_localizations.dart'; import 'package:covas_mobile/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../locale_provider.dart'; // import '../locale_provider.dart';
void main() async { 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()); runApp(const MyApp());
} }
@@ -30,9 +36,7 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: 'Flutter Directions Example', title: 'Flutter Directions Example',
theme: ThemeData( theme: ThemeData(primarySwatch: Colors.blue),
primarySwatch: Colors.blue,
),
home: const MapboxPages(title: 'Event Location', place: "Flutter"), home: const MapboxPages(title: 'Event Location', place: "Flutter"),
); );
} }
@@ -52,36 +56,26 @@ class MapboxPages extends StatefulWidget {
class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog { class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
final AuthService _authService = AuthService(); final AuthService _authService = AuthService();
late MapboxMapController mapController; mapbox.MapboxMap? mapboxMap;
late String mapboxAccessToken; mapbox.PointAnnotationManager? pointAnnotationManager;
List<LatLng> routeCoordinates = []; mapbox.PolylineAnnotationManager? polylineAnnotationManager;
String selectedMode = 'driving';
double longitude = 0.0; double longitude = 0.0;
double latitude = 0.0; double latitude = 0.0;
bool isLoading = true; bool isLoading = true;
late LatLng userPosition; mapbox.Point? userPosition;
bool isUserPositionInitialized = false; bool isUserPositionInitialized = false;
Line? currentRouteLine;
String selectedMode = 'driving';
List<List<double>> routeCoordinates = [];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_authService.checkTokenStatus(context); _authService.checkTokenStatus(context);
_getUserLocation(); _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<void> _getEventInfo() async { Future<void> _getEventInfo() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? ""; var accessToken = prefs.getString("access_token") ?? "";
@@ -96,119 +90,51 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
latitude = events["latitude"]; latitude = events["latitude"];
longitude = events["longitude"]; longitude = events["longitude"];
setState(() { setState(() => isLoading = false);
isLoading = false;
});
} else { } else {
_handleErrorResponse(responseGet.statusCode); _handleErrorResponse(responseGet.statusCode);
} }
} else { } else {
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error", showAlertDialog(context, "Error", "Invalid cache.");
AppLocalizations.of(context)?.invalid_cache ?? "Invalid cache.");
} }
} }
void _handleErrorResponse(int statusCode) { void _handleErrorResponse(int statusCode) {
final messages = { final errorMessage = "Error $statusCode fetching event";
400: AppLocalizations.of(context)?.request_error ?? showAlertDialog(context, "Error", errorMessage);
"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<void> _getUserLocation() async { Future<void> _getUserLocation() async {
try { try {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); bool serviceEnabled = await geo.Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) { if (!serviceEnabled) return;
showAlertDialog(
context, geo.LocationPermission permission =
AppLocalizations.of(context)?.error ?? "Error", await geo.Geolocator.checkPermission();
AppLocalizations.of(context)?.geo_disabled ?? if (permission == geo.LocationPermission.denied) {
"Location services are disabled."); permission = await geo.Geolocator.requestPermission();
return;
} }
LocationPermission permission = await Geolocator.checkPermission(); if (permission == geo.LocationPermission.deniedForever) return;
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission(); geo.Position position = await geo.Geolocator.getCurrentPosition();
if (permission == LocationPermission.denied) { setState(() {
showAlertDialog( userPosition = mapbox.Point(
context, coordinates:
AppLocalizations.of(context)?.error ?? "Error", mapbox.Position(position.latitude, position.longitude));
AppLocalizations.of(context)?.permission_denied ?? isUserPositionInitialized = true;
"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(); _getEventInfo();
} catch (e) { } catch (e) {
showAlertDialog( showAlertDialog(context, "Error", "Failed to get location");
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.failed_location ??
"Failed to get user location");
} }
} }
Future<void> _fetchRoute( Future<void> _fetchRoute(
LatLng origin, LatLng destination, String mode) async { mapbox.Point origin, mapbox.Point destination, String mode) async {
final url = Uri.parse( 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); final response = await http.get(url);
@@ -216,156 +142,63 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
final data = jsonDecode(response.body); final data = jsonDecode(response.body);
final geometry = data['routes'][0]['geometry']['coordinates']; final geometry = data['routes'][0]['geometry']['coordinates'];
setState(() { setState(() {
routeCoordinates = geometry.map<LatLng>((coord) { routeCoordinates = (geometry as List)
return LatLng(coord[1], coord[0]); .map<List<double>>((coord) => [coord[0], coord[1]])
}).toList(); .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<void> _drawRouteAndMarkers() async { Future<void> _drawRouteAndMarkers() async {
// Remove previous route line if it exists if (mapboxMap == null || !isUserPositionInitialized) return;
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 && // Managers
userPosition != null && pointAnnotationManager ??=
latitude != 0.0 && await mapboxMap!.annotations.createPointAnnotationManager();
longitude != 0.0) { polylineAnnotationManager ??=
final destination = LatLng(latitude, longitude); await mapboxMap!.annotations.createPolylineAnnotationManager();
// Register the custom images // Clear old annotations
// Add event marker 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 // Add user marker
await mapController.addImage('user-marker', eventMarkerImage); final userIcon = await _loadMarkerImage('images/marker.png');
await mapController.addSymbol(SymbolOptions( await pointAnnotationManager!.create(mapbox.PointAnnotationOptions(
geometry: userPosition, geometry: mapbox.Point(
iconImage: 'user-marker', // Custom icon for event coordinates: mapbox.Position(
iconSize: 0.2, 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<LatLng> 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<Uint8List> _loadMarkerImage(String assetPath) async { Future<Uint8List> _loadMarkerImage(String assetPath) async {
final ByteData data = await rootBundle.load(assetPath); final ByteData data = await rootBundle.load(assetPath);
return data.buffer.asUint8List(); return data.buffer.asUint8List();
@@ -374,78 +207,23 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(title: Text(widget.place)),
title: Text(widget.place),
actions: [
DropdownButton<String>(
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(), drawer: MyDrawer(),
body: Stack( body: isLoading
children: [ ? const Center(child: CircularProgressIndicator())
isLoading : mapbox.MapWidget(
? Center(child: CircularProgressIndicator()) onMapCreated: (controller) {
: MapboxMap( mapboxMap = controller;
accessToken: mapboxAccessToken, },
onMapCreated: (controller) { cameraOptions: mapbox.CameraOptions(
mapController = controller; center: mapbox.Point(
}, coordinates: mapbox.Position(longitude, latitude)),
onStyleLoadedCallback: _onStyleLoaded, zoom: 14.0,
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',
), ),
), floatingActionButton: FloatingActionButton(
], onPressed: _drawRouteAndMarkers,
child: const Icon(Icons.directions),
), ),
); );
} }