Compare commits
15 Commits
6169483839
...
main
Author | SHA1 | Date | |
---|---|---|---|
5ee053272e | |||
1bb2612de3 | |||
86f81ff009 | |||
9989aebc32 | |||
35fc1cfa25 | |||
692d55858c | |||
59b16e5131 | |||
89c5e1aa6c | |||
f4fb846855 | |||
9a9287bd20 | |||
210da71d50 | |||
63ea3091e8 | |||
3310bd572e | |||
6d504139b0 | |||
70545052c2 |
@@ -13,6 +13,7 @@ android {
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
@@ -39,6 +40,10 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
|
||||
}
|
||||
|
||||
flutter {
|
||||
source = "../.."
|
||||
}
|
||||
|
@@ -1,8 +1,15 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
|
||||
<application
|
||||
android:label="covas_mobile_new"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
||||
android:value="ca-app-pub-4855855675386260~3438207239"/>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
@@ -11,6 +18,7 @@
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
|
BIN
covas_mobile_new/images/flutter.png
Normal file
BIN
covas_mobile_new/images/flutter.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
BIN
covas_mobile_new/images/marker-red.png
Normal file
BIN
covas_mobile_new/images/marker-red.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
BIN
covas_mobile_new/images/marker.png
Normal file
BIN
covas_mobile_new/images/marker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
covas_mobile_new/images/search.png
Normal file
BIN
covas_mobile_new/images/search.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
@@ -6,21 +6,28 @@ import 'pages/LoginDemo.dart';
|
||||
import 'locale_provider.dart'; // <-- à adapter selon ton arborescence
|
||||
import 'package:covas_mobile/gen_l10n/app_localizations.dart';
|
||||
import 'classes/notification_service.dart';
|
||||
import 'classes/auth_service.dart';
|
||||
import 'pages/ListItemMenu.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await MobileAds.instance.initialize();
|
||||
await NotificationService.initialize();
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
final loggedIn = await _authService.isLoggedIn();
|
||||
|
||||
runApp(
|
||||
ChangeNotifierProvider(
|
||||
create: (_) => LocaleProvider(),
|
||||
child: MyApp(),
|
||||
child: MyApp(isLoggedIn: loggedIn),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
final bool isLoggedIn;
|
||||
const MyApp({Key? key, required this.isLoggedIn}) : super(key: key);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localeProvider = Provider.of<LocaleProvider>(
|
||||
@@ -30,7 +37,7 @@ class MyApp extends StatelessWidget {
|
||||
locale: localeProvider.locale, // <-- utilise la locale courante
|
||||
supportedLocales: L10n.all,
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
home: LoginDemo(),
|
||||
home: isLoggedIn ? ListItemMenu() : LoginDemo(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -221,15 +221,13 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
|
||||
|
||||
final file = File(widget.imagePath);
|
||||
|
||||
gemini
|
||||
.textAndImage(
|
||||
text:
|
||||
"Peux-tu donner le nom, la date (si l'année n'est pas précisé, mettez l'année actuelle ou future) et le lieu de l'évènement sous format JSON (sans le caratère json au début de la chaine de caractère) avec les valeurs suivantes : name, place, description, tags (tableau sans espace), organizers (tableau), start_date et end_date (si le end_date est vide, alors donnez une valeur de six de plus par rapport à start_date) sous le format en YYYY-MM-DD HH:mm:ssZ",
|
||||
images: [file.readAsBytesSync()],
|
||||
modelName: "models/gemini-1.5-pro-latest")
|
||||
.then((value) => searchEvents(
|
||||
value?.content?.parts?.last.text ?? '', widget.imagePath))
|
||||
.catchError((e) => displayError);
|
||||
gemini.prompt(parts: [
|
||||
Part.text(
|
||||
"Peux-tu donner le nom, la date (si l'année n'est pas précisé, mettez l'année actuelle ou future) et le lieu de l'évènement sous format JSON (sans le caratère json au début de la chaine de caractère) avec les valeurs suivantes : name, place, description, tags (tableau sans espace), organizers (tableau), start_date et end_date (si le end_date est vide, alors donnez une valeur de six de plus par rapport à start_date) sous le format en YYYY-MM-DD HH:mm:ssZ"),
|
||||
Part.bytes(file.readAsBytesSync())
|
||||
], model: "models/gemini-1.5-pro-latest").then((value) {
|
||||
searchEvents(value?.output ?? '', widget.imagePath);
|
||||
}).catchError((e) => displayError);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@@ -380,8 +380,8 @@ class _EditEventState extends State<EditEvent>
|
||||
|
||||
imgUrl = widget.events!.imgUrl ?? "";
|
||||
inputDesc.text = widget.events!.description ?? "";
|
||||
initialTags = List<String>.from(widget.events!.tags as List);
|
||||
initialOrga = List<String>.from(widget.events!.organizers as List);
|
||||
initialTags = List<String>.from((widget.events?.tags as List?) ?? []);
|
||||
initialOrga = List<String>.from((widget.events?.organizers as List?) ?? []);
|
||||
}
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
@@ -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<MapboxPages> with ShowAlertDialog {
|
||||
final AuthService _authService = AuthService();
|
||||
|
||||
late MapboxMapController mapController;
|
||||
late String mapboxAccessToken;
|
||||
List<LatLng> 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<List<double>> 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<void> _getEventInfo() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var accessToken = prefs.getString("access_token") ?? "";
|
||||
@@ -96,276 +90,170 @@ class _MapboxPagesState extends State<MapboxPages> 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<void> _getUserLocation() async {
|
||||
await dotenv.load(fileName: ".env");
|
||||
|
||||
// Set the access token globally
|
||||
mapbox.MapboxOptions.setAccessToken(
|
||||
dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? '');
|
||||
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;
|
||||
|
||||
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) {
|
||||
geo.Position position = await geo.Geolocator.getCurrentPosition();
|
||||
setState(() {
|
||||
userPosition = LatLng(position!.latitude, position!.longitude);
|
||||
userPosition = mapbox.Point(
|
||||
coordinates:
|
||||
mapbox.Position(position.longitude, position.latitude));
|
||||
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<void> _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);
|
||||
|
||||
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.map<LatLng>((coord) {
|
||||
return LatLng(coord[1], coord[0]);
|
||||
}).toList();
|
||||
routeCoordinates = (geometry as List)
|
||||
.map<List<double>>((coord) => [coord[0], coord[1]])
|
||||
.toList();
|
||||
});
|
||||
} else {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.failed_fetch ??
|
||||
"Failed to fetch the route");
|
||||
debugPrint("⚠️ Aucune route trouvée entre ${origin} et $destination.");
|
||||
// Optionnel : afficher un snackbar/toast à l’utilisateur
|
||||
}
|
||||
} else {
|
||||
debugPrint("❌ Erreur API Mapbox: ${response.statusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the map is created
|
||||
void _onStyleLoaded() async {
|
||||
// Log the map controller and coordinates
|
||||
Future<void> _zoomToFitRoute(List<List<double>> coordinates) async {
|
||||
if (mapboxMap == null || coordinates.isEmpty) return;
|
||||
|
||||
// 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');
|
||||
double minLat = coordinates.first[1];
|
||||
double maxLat = coordinates.first[1];
|
||||
double minLng = coordinates.first[0];
|
||||
double maxLng = coordinates.first[0];
|
||||
|
||||
// Register the image with Mapbox
|
||||
await mapController.addImage('event-marker', userMarkerImage);
|
||||
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 symbolOptions = SymbolOptions(
|
||||
geometry: LatLng(latitude, longitude),
|
||||
iconImage: "event-marker", // Use the registered custom marker
|
||||
iconSize: 0.4, // Optional: Adjust size
|
||||
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),
|
||||
);
|
||||
|
||||
// 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 {
|
||||
// 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
|
||||
// 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);
|
||||
|
||||
await _fetchRoute(userPosition!, destination, selectedMode);
|
||||
if (routeCoordinates.isNotEmpty) {
|
||||
currentRouteLine = await mapController.addLine(
|
||||
LineOptions(
|
||||
geometry: routeCoordinates,
|
||||
lineColor: '#3b9ddd',
|
||||
lineWidth: 5.0,
|
||||
lineOpacity: 0.8,
|
||||
await polylineAnnotationManager!.create(mapbox.PolylineAnnotationOptions(
|
||||
geometry: mapbox.LineString(
|
||||
coordinates:
|
||||
routeCoordinates.map((c) => mapbox.Position(c[0], c[1])).toList(),
|
||||
),
|
||||
);
|
||||
|
||||
_zoomToFitRoute(routeCoordinates);
|
||||
}
|
||||
} else {
|
||||
showAlertDialog(
|
||||
context,
|
||||
AppLocalizations.of(context)?.error ?? "Error",
|
||||
AppLocalizations.of(context)?.invalid_coordinates ??
|
||||
"Invalid coordinates or user position.");
|
||||
lineColor: Colors.blue.value,
|
||||
lineWidth: 4.0,
|
||||
));
|
||||
await _zoomToFitRoute(routeCoordinates);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
final ByteData data = await rootBundle.load(assetPath);
|
||||
return data.buffer.asUint8List();
|
||||
@@ -374,79 +262,69 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.place),
|
||||
actions: [
|
||||
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<String>(
|
||||
value: selectedMode,
|
||||
items: [
|
||||
items: const [
|
||||
DropdownMenuItem(
|
||||
value: 'walking',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.directions_walk, color: Colors.blue),
|
||||
SizedBox(width: 8),
|
||||
Text(AppLocalizations.of(context)?.walking ?? 'Walking'),
|
||||
],
|
||||
),
|
||||
),
|
||||
value: 'driving', child: Text("🚗 Voiture")),
|
||||
DropdownMenuItem(
|
||||
value: 'cycling',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.directions_bike, color: Colors.green),
|
||||
SizedBox(width: 8),
|
||||
Text(AppLocalizations.of(context)?.cycling ?? 'Cycling'),
|
||||
],
|
||||
),
|
||||
),
|
||||
value: 'walking', child: Text("🚶 Marche")),
|
||||
DropdownMenuItem(
|
||||
value: 'driving',
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.directions_car, color: Colors.red),
|
||||
SizedBox(width: 8),
|
||||
Text(AppLocalizations.of(context)?.driving ?? 'Driving'),
|
||||
value: 'cycling', child: Text("🚴 Vélo")),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
onChanged: (mode) {
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
selectedMode = mode!;
|
||||
selectedMode = value;
|
||||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
drawer: MyDrawer(),
|
||||
body: Stack(
|
||||
children: [
|
||||
isLoading
|
||||
? Center(child: CircularProgressIndicator())
|
||||
: MapboxMap(
|
||||
accessToken: mapboxAccessToken,
|
||||
onMapCreated: (controller) {
|
||||
mapController = controller;
|
||||
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,
|
||||
));
|
||||
},
|
||||
onStyleLoadedCallback: _onStyleLoaded,
|
||||
initialCameraPosition: CameraPosition(
|
||||
target: LatLng(latitude, longitude),
|
||||
cameraOptions: mapbox.CameraOptions(
|
||||
center: mapbox.Point(
|
||||
coordinates: mapbox.Position(longitude, latitude)),
|
||||
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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -37,7 +37,6 @@ dependencies:
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
flutter_local_notifications: ^19.4.1
|
||||
timezone: ^0.10.1
|
||||
cupertino_icons: ^1.0.2
|
||||
http: ^1.2.1
|
||||
@@ -59,6 +58,7 @@ dependencies:
|
||||
encrypt_shared_preferences: ^0.9.10
|
||||
provider: ^6.1.2 # ou la dernière version
|
||||
mapbox_maps_flutter: ^2.10.0
|
||||
flutter_local_notifications: ^19.4.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Reference in New Issue
Block a user