409 lines
13 KiB
Dart
Raw Permalink Normal View History

2024-11-11 14:48:23 +01:00
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
2024-11-16 23:24:00 +01:00
import 'package:flutter_dotenv/flutter_dotenv.dart'; // For environment variables
import 'package:flutter/services.dart'; // For loading assets
2024-11-11 14:48:23 +01:00
import 'package:http/http.dart' as http;
import 'package:mapbox_gl/mapbox_gl.dart';
2024-11-16 23:24:00 +01:00
import 'package:shared_preferences/shared_preferences.dart';
import 'package:geolocator/geolocator.dart'; // For getting the user's location
2024-11-11 14:48:23 +01:00
import '../classes/alert.dart'; // Assuming this contains your error dialog code.
import '../variable/globals.dart' as globals;
2025-01-10 21:06:41 +01:00
import '../classes/MyDrawer.dart';
2024-11-11 14:48:23 +01:00
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(
2024-11-16 23:24:00 +01:00
title: 'Flutter Directions Example',
2024-11-11 14:48:23 +01:00
theme: ThemeData(
primarySwatch: Colors.blue,
),
2024-11-16 23:24:00 +01:00
home: const MapboxPages(title: 'Event Location', place: "Flutter"),
2024-11-11 14:48:23 +01:00
);
}
}
2024-11-16 23:24:00 +01:00
class MapboxPages extends StatefulWidget {
const MapboxPages({Key? key, required this.title, required this.place})
2024-11-15 23:22:17 +01:00
: super(key: key);
2024-11-11 14:48:23 +01:00
final String title;
2024-11-15 23:22:17 +01:00
final String place;
2024-11-11 14:48:23 +01:00
@override
2024-11-16 23:24:00 +01:00
State<MapboxPages> createState() => _MapboxPagesState();
2024-11-11 14:48:23 +01:00
}
2024-12-30 22:34:17 +01:00
class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
2024-11-16 23:24:00 +01:00
late MapboxMapController mapController;
2024-11-11 14:48:23 +01:00
late String mapboxAccessToken;
2024-11-16 23:24:00 +01:00
List<LatLng> routeCoordinates = [];
String selectedMode = 'driving';
2024-11-11 14:48:23 +01:00
double longitude = 0.0;
double latitude = 0.0;
bool isLoading = true;
2024-11-17 10:46:17 +01:00
late LatLng userPosition;
bool isUserPositionInitialized = false;
2024-11-17 11:31:07 +01:00
Line? currentRouteLine;
2024-11-11 14:48:23 +01:00
@override
void initState() {
super.initState();
2024-11-17 10:58:29 +01:00
_getUserLocation();
2024-11-11 14:48:23 +01:00
}
2024-11-15 23:22:17 +01:00
void _initToken() {
2024-11-11 14:48:23 +01:00
mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? '';
if (mapboxAccessToken.isEmpty) {
2024-12-30 22:34:17 +01:00
showAlertDialog(
context, "Erreur Mapbox", "Mapbox Access Token is not available.");
2024-11-11 14:48:23 +01:00
}
}
2024-11-15 23:22:17 +01:00
Future<void> _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"];
2024-11-17 10:58:29 +01:00
2024-11-15 23:22:17 +01:00
setState(() {
isLoading = false;
});
2024-11-11 14:48:23 +01:00
} else {
2024-11-16 23:24:00 +01:00
_handleErrorResponse(responseGet.statusCode);
2024-11-11 14:48:23 +01:00
}
2024-11-15 23:22:17 +01:00
} else {
2024-12-30 22:34:17 +01:00
showAlertDialog(context, "Erreur serveur", "Invalid cache.");
2024-11-11 14:48:23 +01:00
}
}
2024-11-16 23:24:00 +01:00
void _handleErrorResponse(int statusCode) {
String text;
switch (statusCode) {
case 400:
text = "Bad Request.";
break;
case 406:
text = "Incorrect Password.";
break;
case 404:
text = "User Not Found.";
break;
case 403:
text = "Action not permitted.";
break;
case 410:
text = "Invalid Token.";
break;
case 500:
text = "Internal Server Error.";
break;
default:
text = "Unknown error.";
}
2024-12-30 22:34:17 +01:00
showAlertDialog(context, "Erreur serveur", text);
2024-11-13 23:47:40 +01:00
}
2024-11-16 23:24:00 +01:00
Future<void> _getUserLocation() async {
try {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
2024-12-30 22:34:17 +01:00
showAlertDialog(
context, "Erreur de service", "Location services are disabled.");
2024-11-16 23:24:00 +01:00
return;
}
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
2024-12-30 22:34:17 +01:00
showAlertDialog(context, "Erreur de permission",
"Location permissions are denied.");
2024-11-16 23:24:00 +01:00
return;
}
}
if (permission == LocationPermission.deniedForever) {
2024-12-30 22:34:17 +01:00
showAlertDialog(context, "Erreur de permission",
2024-11-16 23:24:00 +01:00
"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
print('Location services are disabled.');
position = await Geolocator.getLastKnownPosition();
if (position == null) {
print('No last known position available.');
}
} catch (e) {
// Handle other errors
print('Failed to get location: $e');
position = await Geolocator.getLastKnownPosition();
if (position == null) {
print('No last known position available.');
}
}
if (position != null) {
setState(() {
userPosition = LatLng(position!.latitude, position!.longitude);
isUserPositionInitialized = true;
});
}
2024-11-17 10:58:29 +01:00
_initToken();
_getEventInfo();
2024-11-16 23:24:00 +01:00
} catch (e) {
2024-12-30 22:34:17 +01:00
showAlertDialog(context, "Erreur geo", "Failed to get user location: $e");
2024-11-16 23:24:00 +01:00
}
}
Future<void> _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<LatLng>((coord) {
return LatLng(coord[1], coord[0]);
}).toList();
});
} else {
2024-12-30 22:34:17 +01:00
showAlertDialog(context, "Erreur serveur",
"Failed to fetch the route: ${response.statusCode}");
2024-11-16 23:24:00 +01:00
}
2024-11-15 22:48:05 +01:00
}
2024-11-11 14:48:23 +01:00
2024-11-15 22:48:05 +01:00
// Called when the map is created
void _onStyleLoaded() async {
2024-11-11 14:48:23 +01:00
// Log the map controller and coordinates
print("Mapbox controller initialized: $mapController");
print("lat - long : $latitude - $longitude");
// Check if the mapController is really initialized
if (mapController != null) {
try {
2024-11-13 23:47:40 +01:00
// Ensure the coordinates are valid
if (latitude != 0.0 && longitude != 0.0) {
// Load marker image as Uint8List
2024-11-16 23:24:00 +01:00
final userMarkerImage = await _loadMarkerImage('images/marker.png');
2024-11-13 23:47:40 +01:00
// Register the image with Mapbox
2024-11-17 10:46:17 +01:00
await mapController.addImage('event-marker', userMarkerImage);
2024-11-13 23:47:40 +01:00
final symbolOptions = SymbolOptions(
geometry: LatLng(latitude, longitude),
2024-11-17 10:46:17 +01:00
iconImage: "event-marker", // Use the registered custom marker
2024-11-16 23:24:00 +01:00
iconSize: 0.4, // Optional: Adjust size
2024-11-13 23:47:40 +01:00
);
// Debugging symbol options
print("Adding symbol with options: $symbolOptions");
// Add symbol to map
mapController!.addSymbol(symbolOptions);
} else {
print("Error: Invalid coordinates, cannot add symbol.");
}
2024-11-11 14:48:23 +01:00
} catch (e) {
// Handle any exception that occurs when adding the symbol
print("Error when adding symbol: $e");
}
} else {
print(
"Error: MapboxMapController is null at the time of symbol addition");
}
}
2024-11-16 23:24:00 +01:00
Future<void> _drawRouteAndMarkers() async {
2024-11-17 11:31:07 +01:00
// Remove previous route line if it exists
if (currentRouteLine != null) {
await mapController.removeLine(currentRouteLine!);
currentRouteLine = null;
}
2024-11-17 10:46:17 +01:00
if (!isUserPositionInitialized) {
2024-12-30 22:34:17 +01:00
showAlertDialog(context, "Erreur de position",
"User position is not yet initialized. Try again.");
2024-11-17 10:46:17 +01:00
return;
}
2024-11-16 23:24:00 +01:00
if (mapController != null &&
userPosition != null &&
latitude != 0.0 &&
longitude != 0.0) {
final destination = LatLng(latitude, longitude);
// Register the custom images
// Add event marker
2024-11-17 15:53:26 +01:00
final eventMarkerImage = await _loadMarkerImage('images/marker-red.png');
2024-11-16 23:24:00 +01:00
// Register the image with Mapbox
2024-11-17 15:53:26 +01:00
await mapController.addImage('user-marker', eventMarkerImage);
2024-11-16 23:24:00 +01:00
await mapController.addSymbol(SymbolOptions(
2024-11-17 10:46:17 +01:00
geometry: userPosition,
iconImage: 'user-marker', // Custom icon for event
2024-11-17 15:53:26 +01:00
iconSize: 0.2,
2024-11-16 23:24:00 +01:00
));
// Fetch and draw route
2024-11-17 11:31:07 +01:00
await _fetchRoute(userPosition, destination, selectedMode);
2024-11-16 23:24:00 +01:00
if (routeCoordinates.isNotEmpty) {
2024-11-17 11:31:07 +01:00
currentRouteLine = await mapController.addLine(
2024-11-16 23:24:00 +01:00
LineOptions(
geometry: routeCoordinates,
lineColor: '#3b9ddd',
lineWidth: 5.0,
lineOpacity: 0.8,
),
);
2024-11-17 11:31:07 +01:00
2024-11-17 11:10:43 +01:00
_zoomToFitRoute(routeCoordinates);
2024-11-16 23:24:00 +01:00
}
} else {
2024-12-30 22:34:17 +01:00
showAlertDialog(context, "Erreur de coordonée",
"Invalid coordinates or user position.");
2024-11-16 23:24:00 +01:00
}
}
2024-11-17 11:10:43 +01:00
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
),
);
}
2024-11-16 23:24:00 +01:00
// Load image from assets
Future<Uint8List> _loadMarkerImage(String assetPath) async {
final ByteData data = await rootBundle.load(assetPath);
return data.buffer.asUint8List();
}
2024-11-11 14:48:23 +01:00
@override
Widget build(BuildContext context) {
return Scaffold(
2024-11-16 23:24:00 +01:00
appBar: AppBar(
title: Text(widget.place),
actions: [
DropdownButton<String>(
value: selectedMode,
2024-11-17 11:38:59 +01:00
items: [
DropdownMenuItem(
value: 'walking',
child: Row(
children: [
Icon(Icons.directions_walk, color: Colors.blue),
SizedBox(width: 8),
Text('Walking'),
],
),
),
DropdownMenuItem(
value: 'cycling',
child: Row(
children: [
Icon(Icons.directions_bike, color: Colors.green),
SizedBox(width: 8),
Text('Cycling'),
],
),
),
DropdownMenuItem(
value: 'driving',
child: Row(
children: [
Icon(Icons.directions_car, color: Colors.red),
SizedBox(width: 8),
Text('Driving'),
],
),
),
],
2024-11-16 23:24:00 +01:00
onChanged: (mode) {
setState(() {
selectedMode = mode!;
});
},
)
],
),
2025-01-10 21:06:41 +01:00
drawer: MyDrawer(),
2024-11-16 23:24:00 +01:00
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: 'Get Directions and Markers',
2024-11-11 14:48:23 +01:00
),
2024-11-16 23:24:00 +01:00
),
],
),
2024-11-11 14:48:23 +01:00
);
}
}