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 {
|
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 = "../.."
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
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 '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(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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>();
|
||||||
|
@@ -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 à l’utilisateur
|
||||||
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),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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:
|
||||||
|
Reference in New Issue
Block a user