15 Commits

11 changed files with 237 additions and 341 deletions

View File

@@ -13,6 +13,7 @@ android {
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11
isCoreLibraryDesugaringEnabled = true
} }
kotlinOptions { kotlinOptions {
@@ -39,6 +40,10 @@ android {
} }
} }
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
}
flutter { flutter {
source = "../.." source = "../.."
} }

View File

@@ -1,8 +1,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <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 <application
android:label="covas_mobile_new" android:label="covas_mobile_new"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-4855855675386260~3438207239"/>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
@@ -11,6 +18,7 @@
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:enableOnBackInvokedCallback="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as <!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user

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

@@ -6,21 +6,28 @@ import 'pages/LoginDemo.dart';
import 'locale_provider.dart'; // <-- à adapter selon ton arborescence import 'locale_provider.dart'; // <-- à adapter selon ton arborescence
import 'package:covas_mobile/gen_l10n/app_localizations.dart'; import 'package:covas_mobile/gen_l10n/app_localizations.dart';
import 'classes/notification_service.dart'; import 'classes/notification_service.dart';
import 'classes/auth_service.dart';
import 'pages/ListItemMenu.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize(); await MobileAds.instance.initialize();
await NotificationService.initialize(); await NotificationService.initialize();
final AuthService _authService = AuthService();
final loggedIn = await _authService.isLoggedIn();
runApp( runApp(
ChangeNotifierProvider( ChangeNotifierProvider(
create: (_) => LocaleProvider(), create: (_) => LocaleProvider(),
child: MyApp(), child: MyApp(isLoggedIn: loggedIn),
), ),
); );
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
final bool isLoggedIn;
const MyApp({Key? key, required this.isLoggedIn}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final localeProvider = Provider.of<LocaleProvider>( final localeProvider = Provider.of<LocaleProvider>(
@@ -30,7 +37,7 @@ class MyApp extends StatelessWidget {
locale: localeProvider.locale, // <-- utilise la locale courante locale: localeProvider.locale, // <-- utilise la locale courante
supportedLocales: L10n.all, supportedLocales: L10n.all,
localizationsDelegates: AppLocalizations.localizationsDelegates, localizationsDelegates: AppLocalizations.localizationsDelegates,
home: LoginDemo(), home: isLoggedIn ? ListItemMenu() : LoginDemo(),
); );
} }
} }

View File

@@ -221,15 +221,13 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
final file = File(widget.imagePath); final file = File(widget.imagePath);
gemini gemini.prompt(parts: [
.textAndImage( Part.text(
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"),
"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())
images: [file.readAsBytesSync()], ], model: "models/gemini-1.5-pro-latest").then((value) {
modelName: "models/gemini-1.5-pro-latest") searchEvents(value?.output ?? '', widget.imagePath);
.then((value) => searchEvents( }).catchError((e) => displayError);
value?.content?.parts?.last.text ?? '', widget.imagePath))
.catchError((e) => displayError);
} }
@override @override

View File

@@ -380,8 +380,8 @@ class _EditEventState extends State<EditEvent>
imgUrl = widget.events!.imgUrl ?? ""; imgUrl = widget.events!.imgUrl ?? "";
inputDesc.text = widget.events!.description ?? ""; inputDesc.text = widget.events!.description ?? "";
initialTags = List<String>.from(widget.events!.tags as List); initialTags = List<String>.from((widget.events?.tags as List?) ?? []);
initialOrga = List<String>.from(widget.events!.organizers as List); initialOrga = List<String>.from((widget.events?.organizers as List?) ?? []);
} }
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();

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,276 +90,170 @@ 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 {
await dotenv.load(fileName: ".env");
// Set the access token globally
mapbox.MapboxOptions.setAccessToken(
dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? '');
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();
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) { geo.Position position = await geo.Geolocator.getCurrentPosition();
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(() { setState(() {
userPosition = LatLng(position!.latitude, position!.longitude); userPosition = mapbox.Point(
coordinates:
mapbox.Position(position.longitude, position.latitude));
isUserPositionInitialized = true; 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);
if (response.statusCode == 200) { if (response.statusCode == 200) {
final data = jsonDecode(response.body); 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']; 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 { } else {
showAlertDialog( debugPrint("⚠️ Aucune route trouvée entre ${origin} et $destination.");
context, // Optionnel : afficher un snackbar/toast à lutilisateur
AppLocalizations.of(context)?.error ?? "Error", }
AppLocalizations.of(context)?.failed_fetch ?? } else {
"Failed to fetch the route"); debugPrint("❌ Erreur API Mapbox: ${response.statusCode}");
} }
} }
// Called when the map is created Future<void> _zoomToFitRoute(List<List<double>> coordinates) async {
void _onStyleLoaded() async { if (mapboxMap == null || coordinates.isEmpty) return;
// Log the map controller and coordinates
// Check if the mapController is really initialized double minLat = coordinates.first[1];
if (mapController != null) { double maxLat = coordinates.first[1];
try { double minLng = coordinates.first[0];
// Ensure the coordinates are valid double maxLng = coordinates.first[0];
if (latitude != 0.0 && longitude != 0.0) {
// Load marker image as Uint8List
final userMarkerImage = await _loadMarkerImage('images/marker.png');
// Register the image with Mapbox for (var coord in coordinates) {
await mapController.addImage('event-marker', userMarkerImage); 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( final bounds = mapbox.CoordinateBounds(
geometry: LatLng(latitude, longitude), southwest: mapbox.Point(coordinates: mapbox.Position(minLng, minLat)),
iconImage: "event-marker", // Use the registered custom marker northeast: mapbox.Point(coordinates: mapbox.Position(maxLng, maxLat)),
iconSize: 0.4, // Optional: Adjust size 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 { 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(
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, iconSize: 0.2,
)); ));
// Fetch and draw route // Fetch and draw route
await _fetchRoute(userPosition, destination, selectedMode); await _fetchRoute(userPosition!, destination, selectedMode);
if (routeCoordinates.isNotEmpty) { if (routeCoordinates.isNotEmpty) {
currentRouteLine = await mapController.addLine( await polylineAnnotationManager!.create(mapbox.PolylineAnnotationOptions(
LineOptions( geometry: mapbox.LineString(
geometry: routeCoordinates, coordinates:
lineColor: '#3b9ddd', routeCoordinates.map((c) => mapbox.Position(c[0], c[1])).toList(),
lineWidth: 5.0,
lineOpacity: 0.8,
), ),
); lineColor: Colors.blue.value,
lineWidth: 4.0,
_zoomToFitRoute(routeCoordinates); ));
} await _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,79 +262,69 @@ 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), drawer: MyDrawer(),
actions: [ body: isLoading
? const Center(child: CircularProgressIndicator())
: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("Mode : "),
DropdownButton<String>( DropdownButton<String>(
value: selectedMode, value: selectedMode,
items: [ items: const [
DropdownMenuItem( DropdownMenuItem(
value: 'walking', value: 'driving', child: Text("🚗 Voiture")),
child: Row(
children: [
Icon(Icons.directions_walk, color: Colors.blue),
SizedBox(width: 8),
Text(AppLocalizations.of(context)?.walking ?? 'Walking'),
],
),
),
DropdownMenuItem( DropdownMenuItem(
value: 'cycling', value: 'walking', child: Text("🚶 Marche")),
child: Row(
children: [
Icon(Icons.directions_bike, color: Colors.green),
SizedBox(width: 8),
Text(AppLocalizations.of(context)?.cycling ?? 'Cycling'),
],
),
),
DropdownMenuItem( DropdownMenuItem(
value: 'driving', value: 'cycling', child: Text("🚴 Vélo")),
child: Row(
children: [
Icon(Icons.directions_car, color: Colors.red),
SizedBox(width: 8),
Text(AppLocalizations.of(context)?.driving ?? 'Driving'),
], ],
), onChanged: (value) {
), if (value != null) {
],
onChanged: (mode) {
setState(() { setState(() {
selectedMode = mode!; selectedMode = value;
}); });
}
}, },
) ),
], ],
), ),
drawer: MyDrawer(), Expanded(
body: Stack( child: mapbox.MapWidget(
children: [ onMapCreated: (controller) async {
isLoading mapboxMap = controller;
? Center(child: CircularProgressIndicator())
: MapboxMap( // Crée un manager si nécessaire
accessToken: mapboxAccessToken, pointAnnotationManager ??= await mapboxMap!.annotations
onMapCreated: (controller) { .createPointAnnotationManager();
mapController = controller;
// 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, cameraOptions: mapbox.CameraOptions(
initialCameraPosition: CameraPosition( center: mapbox.Point(
target: LatLng(latitude, longitude), coordinates: mapbox.Position(longitude, latitude)),
zoom: 14.0, 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),
),
); );
} }
} }

View File

@@ -37,7 +37,6 @@ dependencies:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
flutter_local_notifications: ^19.4.1
timezone: ^0.10.1 timezone: ^0.10.1
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
http: ^1.2.1 http: ^1.2.1
@@ -59,6 +58,7 @@ dependencies:
encrypt_shared_preferences: ^0.9.10 encrypt_shared_preferences: ^0.9.10
provider: ^6.1.2 # ou la dernière version provider: ^6.1.2 # ou la dernière version
mapbox_maps_flutter: ^2.10.0 mapbox_maps_flutter: ^2.10.0
flutter_local_notifications: ^19.4.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: