62 Commits

Author SHA1 Message Date
76db2f8254 add cache for language 2025-08-06 22:34:01 +02:00
0a62011b3a translate date on list by tag and by organizer 2025-08-06 22:17:53 +02:00
cbc75bbc7b translate date 2025-07-30 23:54:15 +02:00
7a418d82a7 Add german translation 2025-07-29 21:21:21 +02:00
e310197aa7 translate updateevent 100% 2025-07-28 23:21:37 +02:00
5d02f2b1fb translate updateevent 50% 2025-07-28 22:56:39 +02:00
119dfcbdfe translate mapbox page 100% 2025-07-27 20:51:55 +02:00
9e50b6d6f6 translate mapbox page 50% 2025-07-27 20:43:35 +02:00
dcc2ec25de translate mapbox page 10% 2025-07-27 20:04:55 +02:00
2c2eedb7ce translate loginpage 100% 2025-07-27 15:16:38 +02:00
089aa58f4a ListItemOrganizer and Tags 2025-07-27 12:13:52 +02:00
b4b0199fc2 ItemMenu 100% 2025-07-26 19:23:59 +02:00
b48483c9f0 translate forgotpassword 100% again 2025-07-26 19:00:28 +02:00
22d0581da3 translate forgotpassword 100% 2025-07-25 23:44:58 +02:00
337fab4a08 translate editsettings 100% 2025-07-25 23:20:51 +02:00
a99986813e translate editprofile 100% 2025-07-25 23:04:28 +02:00
199000035e translate editprofile 75% 2025-07-24 21:04:27 +02:00
f143036ca8 translate editevent 100% 2025-07-24 20:42:25 +02:00
271c3ba118 translate editevent 75% 2025-07-24 20:25:52 +02:00
f8b5c24efd translate editevent 55% 2025-07-23 23:28:16 +02:00
1df36987d9 translate editevent 50% 2025-07-23 23:18:52 +02:00
b4dc29aff6 translate displaypicturescreen 100% 2025-07-21 22:50:56 +02:00
79563e829c Add displaypicture 2025-07-18 19:38:10 +02:00
413807f039 Add camera dart 2025-07-18 19:06:38 +02:00
26372368b2 full translate for AddProfile pages 100% 2025-07-17 20:53:07 +02:00
f81a8c264c full translate for AddProfile pages 28% 2025-07-16 23:32:35 +02:00
c5985f2954 full translate for AddProfile pages 26% 2025-07-16 23:05:49 +02:00
1f8d18343c full translate for AddProfile pages 25% 2025-07-16 22:58:01 +02:00
c3b8b0df14 full translate for AddProfile pages 20% 2025-07-16 22:46:41 +02:00
ec8ce404ab full translate for AddProfile pages 16% 2025-07-16 22:08:08 +02:00
1208982b15 full translate for AddProfile pages 15% 2025-07-14 23:20:22 +02:00
479ab76fb7 full translate for AddProfile pages 10% 2025-07-14 22:53:34 +02:00
1646a0b6e3 full translate for ItemListMenu pages 50% 2025-07-09 23:33:04 +02:00
e21b03d13c full translate for ItemListMenu pages 2025-07-09 23:05:50 +02:00
45cdb253e4 internationlization wip 2025-07-06 20:59:57 +02:00
75b443758a changement de langue ok sur drawer 2025-07-02 22:35:53 +02:00
e2195e6500 change language ok 2025-07-02 22:03:51 +02:00
4f41aff572 change button language 2025-06-30 23:25:56 +02:00
1c21f59420 Merge pull request 'feature/localeDate' (#45) from feature/localeDate into main
Reviewed-on: #45
2025-06-25 19:16:50 +00:00
7441af6f13 remove if body isnotempty 2025-06-25 21:14:45 +02:00
4f4b0b609c add date locale in list by tags 2025-06-23 23:25:34 +02:00
2e6814c33d add date locale in list by organizers 2025-06-23 23:17:05 +02:00
909b321158 test date format french ok for first page 2025-06-23 23:09:37 +02:00
6641c7938c Merge pull request 'add fetchcount' (#44) from hotfix/fix-counter into main
Reviewed-on: #44
2025-06-23 20:02:46 +00:00
8904032265 add fetchcount 2025-06-18 23:36:24 +02:00
9d35a59ba6 Merge pull request 'hotfix/duplicate-event' (#43) from hotfix/duplicate-event into main
Reviewed-on: #43
2025-06-16 21:05:14 +00:00
303447ff1e check duplicate event 2025-06-16 22:51:01 +02:00
32532246eb add duplicate event check 2025-06-11 23:05:00 +02:00
452faf05d8 Merge pull request 'feature/pagination' (#42) from feature/pagination into main
Reviewed-on: #42
2025-06-02 21:22:13 +00:00
3a64f1ae36 add pagination for other pages 2025-06-02 23:03:49 +02:00
afae3293c4 add current date 2025-06-02 22:22:33 +02:00
600bf8d2f4 add pagination with limit 2025-05-30 23:39:50 +02:00
ec7a286074 pagination wip 2025-05-27 23:36:19 +02:00
6c1c650fa1 add last data 2025-05-25 23:39:37 +02:00
7468c5f24c change fetchCOunt 2025-05-23 23:47:17 +02:00
1bcc8372c6 pagination work for main page 2025-05-23 00:01:48 +02:00
510e366216 add pagination 2025-03-24 22:47:49 +01:00
52580c6568 fetchCount to 0 2025-03-24 22:10:49 +01:00
4f7c8f60d0 add increment and decrement to pagination 2025-03-19 23:40:38 +01:00
ce2a061bf0 Merge pull request 'persist encrypt shared' (#41) from feature/add-encrypted-sharepreferences into main
Reviewed-on: #41
2025-03-12 21:58:27 +00:00
04f4b7ce8e persist encrypt shared 2025-03-12 22:48:35 +01:00
78ea5f0c10 Merge pull request 'add remember me' (#40) from feature/persist-connect into main
Reviewed-on: #40
2025-03-06 22:38:26 +00:00
25 changed files with 1591 additions and 348 deletions

4
covas_mobile/l10n.yaml Normal file
View File

@@ -0,0 +1,4 @@
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-class: AppLocalizations

View File

@@ -10,6 +10,12 @@ import 'alert.dart';
import '../variable/globals.dart' as globals;
import '../pages/LoginDemo.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; // Import dotenv
import 'package:encrypt_shared_preferences/provider.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class MyDrawer extends StatelessWidget with ShowAlertDialog {
Future<void> logout(BuildContext context) async {
@@ -28,7 +34,20 @@ class MyDrawer extends StatelessWidget with ShowAlertDialog {
print("Status code logout ${response.statusCode}");
if (response.statusCode == 200) {
await prefs.remove("access_token"); // Correctly remove the token
await prefs.remove("access_token");
await dotenv.load(fileName: ".env"); // Load .env file
final keyEncrypt = dotenv.env['KEY_ENCRYPT'] ?? '';
if (keyEncrypt.isNotEmpty) {
await EncryptedSharedPreferences.initialize(keyEncrypt);
var sharedPref = EncryptedSharedPreferences.getInstance();
String username = sharedPref.getString("username") ?? "";
String password = sharedPref.getString("password") ?? "";
if ((username.isEmpty) || (password.isEmpty)) {
sharedPref.remove("username");
sharedPref.remove("password");
}
}
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (_) => LoginDemo()),
@@ -72,6 +91,8 @@ class MyDrawer extends StatelessWidget with ShowAlertDialog {
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
final localeProvider = Provider.of<LocaleProvider>(context);
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
@@ -92,7 +113,7 @@ class MyDrawer extends StatelessWidget with ShowAlertDialog {
// Drawer Items
ListTile(
leading: Icon(Icons.home),
title: Text('Home'),
title: Text(loc?.home ?? "Home"),
onTap: () {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (_) => ListItemMenu()));
@@ -102,7 +123,7 @@ class MyDrawer extends StatelessWidget with ShowAlertDialog {
),
ListTile(
leading: Icon(Icons.settings),
title: Text('Settings'),
title: Text(loc?.settings ?? 'Settings'),
onTap: () {
Navigator.pushReplacement(
context,
@@ -112,7 +133,7 @@ class MyDrawer extends StatelessWidget with ShowAlertDialog {
),
ListTile(
leading: Icon(Icons.account_circle),
title: Text('Update profile'),
title: Text(loc?.update_profile ?? 'Update profile'),
onTap: () {
Navigator.pushReplacement(
context,
@@ -121,16 +142,61 @@ class MyDrawer extends StatelessWidget with ShowAlertDialog {
},
),
ListTile(
leading: Icon(Icons.info),
title: Text('About'),
leading: Icon(Icons.language),
title: Text(loc?.language ?? 'Language'),
onTap: () {
showAlertDialog(
context, 'About', "Version 0.0.1"); // Close the drawer
showDialog(
context: context,
builder: (_) => AlertDialog(
title: Text(loc?.select_language ?? 'Select Language'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: Icon(Icons.flag),
title: Text(loc?.french ?? 'Français'),
onTap: () {
Provider.of<LocaleProvider>(context, listen: false)
.setLocale(const Locale('fr'));
Navigator.of(context).pop();
},
),
ListTile(
leading: Icon(Icons.flag_outlined),
title: Text(loc?.english ?? 'English'),
onTap: () {
Provider.of<LocaleProvider>(context, listen: false)
.setLocale(const Locale('en'));
Navigator.of(context).pop();
},
),
ListTile(
leading: Icon(Icons.flag_outlined),
title: Text(loc?.german ?? 'German'),
onTap: () {
Provider.of<LocaleProvider>(context, listen: false)
.setLocale(const Locale('de'));
Navigator.of(context).pop();
},
),
],
),
),
);
},
),
ListTile(
leading: Icon(Icons.info),
title: Text(loc?.about ?? 'About'),
onTap: () {
showAlertDialog(context, loc?.about ?? 'About',
"Version 0.0.1"); // Close the drawer
},
),
ListTile(
leading: Icon(Icons.logout),
title: Text('Log out'),
title: Text(loc?.log_out ?? 'Log out'),
onTap: () async {
logout(context);
// Close the drawer

View File

@@ -4,7 +4,9 @@ import 'package:http/http.dart' as http;
import '../variable/globals.dart' as globals;
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:encrypt_shared_preferences/provider.dart';
import '../pages/LoginDemo.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; // Import dotenv
class AuthService {
// Login with username and password
@@ -19,15 +21,22 @@ class AuthService {
'accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
},
body: {
"username": username,
"password": password,
"remember_me": rememberMe.toString()
},
body: {"username": username, "password": password},
);
if (response.statusCode == 200 || response.statusCode == 201) {
final prefs = await SharedPreferences.getInstance();
if (rememberMe) {
await dotenv.load(fileName: ".env"); // Load .env file
final keyEncrypt = dotenv.env['KEY_ENCRYPT'] ?? '';
if (keyEncrypt.isNotEmpty) {
await EncryptedSharedPreferences.initialize(keyEncrypt);
var sharedPref = EncryptedSharedPreferences.getInstance();
sharedPref.setString("username", username);
sharedPref.setString("password", password);
}
}
final cookies = response.headers["set-cookie"]?.split(";") ?? [];
for (final cookie in cookies) {
@@ -81,8 +90,25 @@ class AuthService {
return true;
} else {
print("Token is invalid. Status code: ${response.statusCode}");
await prefs.remove("access_token"); // Clear invalid token
return false;
await dotenv.load(fileName: ".env"); // Load .env file
final keyEncrypt = dotenv.env['KEY_ENCRYPT'] ?? '';
if (keyEncrypt.isNotEmpty) {
await EncryptedSharedPreferences.initialize(keyEncrypt);
var sharedPref = EncryptedSharedPreferences.getInstance();
String username = sharedPref.getString("username") ?? "";
String password = sharedPref.getString("password") ?? "";
if ((username.isEmpty) || (password.isEmpty)) {
sharedPref.remove("username");
sharedPref.remove("password");
await prefs.remove("access_token"); // Clear invalid token
return false;
} else {
return login(username, password);
}
} else {
return false;
}
}
} catch (e) {
print("Error while checking token: $e");

View File

@@ -0,0 +1,134 @@
{
"@@locale": "de",
"menu_list": "Veranstaltungsmenü",
"language": "Sprache",
"home": "Startseite",
"settings": "Einstellungen",
"update_profile": "Profil aktualisieren",
"about": "Über",
"log_out": "Abmelden",
"french": "Französisch",
"english": "Englisch",
"german": "Deutsch",
"select_language": "Sprache auswählen",
"search_item": "Nach Element suchen",
"search_tag": "Nach Schlagwörtern suchen",
"search_geographical": "Nach geografischer Zone suchen",
"show_date_field": "Datumsfelder anzeigen",
"hide_date_field": "Datumsfelder ausblenden",
"no_data": "Keine Daten verfügbar",
"search": "Suchen",
"no_events": "Keine Veranstaltungen für diesen Ort verfügbar.",
"start_date": "Anfangsdatum",
"end_date": "Enddatum",
"failed_suggestions": "Vorschläge konnten nicht geladen werden",
"error": "Fehler",
"password_different": "Ein anderes Passwort eingeben",
"create": "Erstellung",
"user_create": "Benutzer wurde erstellt",
"user_update": "Benutzer wurde aktualisiert",
"request_error": "Fehlerhafte Anfrage",
"incorrect_password": "Falsches Passwort",
"unknown_user": "Unbekannter Benutzer",
"disabled_user": "Benutzer deaktiviert",
"invalid_token": "Ungültiger Token",
"internal_error_server": "Interner Serverfehler",
"unknown_error_auth": "Unbekannter Authentifizierungsfehler",
"required_input": "Pflichtfeld",
"create_profile": "Profil erstellen",
"edit_pseudo": "Benutzernamen bearbeiten",
"password": "Passwort",
"enter_password": "Passwort eingeben",
"password_confirmed": "Passwort bestätigt",
"last_name": "Nachname",
"first_name": "Vorname",
"email": "E-Mail",
"edit_last_name": "Nachnamen bearbeiten",
"edit_first_name": "Vornamen bearbeiten",
"edit_email": "E-Mail-Adresse bearbeiten",
"birth_date": "Geburtsdatum",
"edit_birth": "Geburtsdatum bearbeiten",
"create_profile_button": "Profil erstellen",
"take_picture": "Foto aufnehmen",
"error_ia": "Google KI konnte das Bild nicht analysieren. Bitte ein anderes versuchen.",
"no_data_geo": "Keine geografischen Daten",
"response_status_update": "Statuscode-Antwort aktualisieren",
"error_token": "Token-Fehler",
"error_format": "Vom KI geliefertes Datenformat ist fehlerhaft",
"display_picture": "Bild anzeigen",
"analyze_image": "Bildanalyse läuft",
"loading_progress": "Ladefortschritt",
"error_event": "Veranstaltungsfehler",
"no_future_event": "Keine zukünftigen Veranstaltungen",
"error_user": "Benutzerfehler",
"empty_input": "Eingabefeld leer",
"info_event": "Veranstaltungsinfo",
"event_already": "Veranstaltung existiert bereits",
"picture_error": "Bildfehler",
"no_picture_published": "Kein Bild veröffentlicht",
"event_update": "Veranstaltung aktualisiert",
"location": "Ort",
"add_event": "Veranstaltung hinzufügen oder aktualisieren",
"edit_image": "Bilder bearbeiten",
"name": "Name",
"edit_event_name": "Veranstaltungsname bearbeiten",
"start_time": "Startzeit",
"end_time": "Endzeit",
"select_date": "Zum Auswählen eines Datums klicken",
"select_time": "Zum Auswählen einer Uhrzeit klicken",
"tag": "Schlagwörter",
"already_tag": "Dieses Schlagwort ist bereits vorhanden",
"enter_tag": "Ein Schlagwort eingeben",
"organizer": "Veranstalter",
"already_organiser": "Veranstalter bereits vorhanden",
"enter_organizer": "Veranstalter eingeben",
"description": "Beschreibung",
"describe_event": "Veranstaltung beschreiben",
"add": "Hinzufügen",
"different_password_error": "Passwörter stimmen nicht überein",
"update": "Aktualisieren",
"updated": "Aktualisiert",
"settings_updated": "Einstellungen aktualisiert",
"define_kilometer": "Kilometer definieren",
"email_sent": "E-Mail wurde gesendet",
"forgot_password": "Passwort vergessen",
"enter_email": "E-Mail eingeben",
"send_email": "E-Mail senden",
"invalid_cache": "Ungültiger Cache",
"item_date": "Datum : ",
"item_maps": "Karte : ",
"item_organizer": "Veranstalter : ",
"item_description": "Beschreibung : ",
"item_tags": "Schlagwörter : ",
"failed_auth": "Authentifizierung fehlgeschlagen",
"login_page": "Anmeldeseite",
"pseudo": "Benutzername",
"enter_existing_pseudo": "Vorhandenen Benutzernamen eingeben",
"remembr_me": "Angemeldet bleiben",
"new_user": "Neuer Benutzer? Konto erstellen",
"sign_in": "Anmelden",
"map_token": "Mapbox-Zugangstoken ist nicht verfügbar",
"geo_disabled": "Standortdienste sind deaktiviert.",
"permission_denied": "Standortberechtigungen wurden verweigert.",
"enable_permission": "Standortberechtigungen dauerhaft verweigert. Bitte in den Einstellungen aktivieren.",
"no_last_position": "Keine letzte bekannte Position verfügbar.",
"failed_location": "Standort konnte nicht ermittelt werden",
"failed_fetch": "Route konnte nicht abgerufen werden",
"invalid_coordinates_symbol": "Ungültige Koordinaten, Symbol kann nicht hinzugefügt werden.",
"error_symbol": "Fehler beim Hinzufügen des Symbols.",
"position_not_init": "Benutzerposition noch nicht initialisiert. Erneut versuchen.",
"invalid_coordinates": "Ungültige Koordinaten.",
"walking": "Zu Fuß",
"cycling": "Mit dem Fahrrad",
"driving": "Mit dem Auto",
"get_direction": "Wegbeschreibung und Markierungen anzeigen",
"missing_token": "Zugangstoken fehlt",
"geocoding_error": "Fehler bei der Geokodierung",
"no_found_place": "Kein Ort gefunden",
"upload_error": "Fehler beim Hochladen des Bildes",
"event_added": "Veranstaltung hinzugefügt",
"unknown_error": "Unbekannter Fehler",
"app_error": "Anwendungsfehler",
"at": "um",
"to_date": "bis"
}

View File

@@ -0,0 +1,136 @@
{
"@@locale": "en",
"menu_list": "Event list menu",
"language": "Language",
"home": "Home",
"settings": "Settings",
"update_profile": "Update profile",
"about": "About",
"log_out": "Log out",
"french": "French",
"english": "English",
"german": "German",
"select_language": "Select language",
"search_item": "Search by item",
"search_tag": "Search by tags",
"search_geographical": "Search by geographical zone",
"show_date_field": "Show Date Fields",
"hide_date_field": "Hide Date Fields",
"no_data": "No data available",
"search": "Search",
"no_events": "No events available for this location.",
"start_date": "Start date",
"end_date": "End date",
"failed_suggestions": "Failed to load suggestions",
"error":"Error",
"password_different":"Must write a different password",
"create": "Creation",
"user_create": "Your user created",
"user_update": "Your user updated",
"request_error": "Poorly constructed query",
"incorrect_password": "Incorrect password",
"unknown_user": "Unknown user",
"disabled_user": "User disabled",
"invalid_token": "Invalid token",
"internal_error_server": "Internal error server",
"unknown_error_auth": "Unknown error authentification",
"required_input": "Required input",
"create_profile": "Create profile",
"edit_pseudo": "Edit pseudo",
"password":"Password",
"enter_password": "Enter the passord",
"password_confirmed": "Password confirmed",
"last_name": "Last name",
"first_name": "First name",
"email": "Mail",
"edit_last_name": "Edit name",
"edit_first_name": "Edit first name",
"edit_email": "Edit email address",
"birth_date": "Birth date",
"edit_birth": "Edit birth date",
"create_profile_button": "Create profile",
"take_picture": "Take a picture",
"error_ia": "Google AI failed to analyze picture. Retry with another one",
"no_data_geo": "No geographical data",
"response_status_update": "response status code update",
"error_token": "Token error",
"error_format": "Data format error given by AI",
"display_picture": "Display the Picture",
"analyze_image": "Image Analyze in progress",
"loading_progress": "Loading progress",
"error_event": "Event error",
"no_future_event": "No future event",
"error_user": "Error user",
"empty_input": "Empty input",
"info_event": "Event info",
"event_already": "Event already exists",
"picture_error": "Picture error",
"no_picture_published": "No picture published",
"event_update": "Event updated",
"location": "Location",
"add_event": "Add or Update a event",
"edit_image": "Edit pictures",
"name": "Name",
"edit_event_name": "Edit event name",
"start_time": "Start time",
"end_time": "End time",
"select_date": "Click to select a date",
"select_time": "Click to select a time",
"tag": "Tags",
"already_tag": "You have already this tags",
"enter_tag": "Enter a tag",
"organizer": "Organizer",
"already_organiser": "You have already a organizer",
"enter_organizer": "Enter a organizer",
"description": "Description",
"describe_event": "Describe event",
"add": "Add",
"update_profile": "Update profile",
"different_password_error": "Different password",
"update": "Update",
"updated": "Updated",
"settings_updated": "Settings updated",
"define_kilometer": "Define Kilometer",
"settings": "Settings",
"email_sent": "Email has been sent",
"forgot_password": "Forgot password",
"enter_email": "Enter the email",
"send_email": "Send email",
"invalid_cache": "Invalid cache",
"item_date": "Date : ",
"item_maps": "Maps : ",
"item_organizer": "Organizer : ",
"item_description": "Description : ",
"item_tags": "Tags : ",
"failed_auth": "Authentification failed",
"login_page": "Login page",
"pseudo": "Pseudo",
"enter_existing_pseudo": "Enter a existing pseudo",
"remembr_me": "Remember me",
"new_user": "New User? Create Account",
"sign_in": "Sign in",
"map_token": "Mapbox Access Token is not available",
"geo_disabled": "Location services are disabled.",
"permission_denied":"Location permissions are denied.",
"enable_permission": "Location permissions are permanently denied. Enable them in settings.",
"no_last_position": "No last known position available.",
"failed_location": "Failed to get user location",
"failed_fetch": "Failed to fetch the route",
"invalid_coordinates_symbol": "Invalid coordinates, cannot add symbol.",
"error_symbol": "Error when adding symbol.",
"position_not_init": "User position is not yet initialized. Try again.",
"invalid_coordinates": "Invalid coordinates.",
"walking": "Walking",
"cycling": "Cycling",
"driving": "Driving",
"get_direction": "Get Directions and Markers",
"missing_token": "Missing access token",
"geocoding_error": "Error when geocoding",
"no_found_place": "No found place",
"upload_error": "Error when image uploading",
"event_added": "Event added",
"unknown_error": "Unknown error",
"app_error": "Application error",
"at": "at",
"to_date": "to"
}

View File

@@ -0,0 +1,137 @@
{
"@@locale": "fr",
"menu_list": "Liste d'évènement",
"language": "Langue",
"home": "Accueil",
"settings": "Paramètres",
"update_profile": "Modifier profil",
"about": "À propos",
"log_out": "Se déconnecter",
"french": "Français",
"english": "Anglais",
"german": "Allemand",
"select_language": "Selectionne la langue",
"search_item": "Recherche par item",
"search_tag": "Recherche par tags",
"search_geographical": "Recherche par zone géographique",
"show_date_field": "Afficher champ date",
"hide_date_field": "Cacher Date Fields",
"no_data": "Aucune donnée disponible",
"search": "Recherche",
"no_events": "Pas d'évènements dans cette localisation",
"start_date": "Date de début",
"end_date": "Date de fin",
"failed_suggestions": "Echec de chargement des suggestions",
"error":"Erreur",
"password_different":"Tu dois écrire un mot de passe different",
"create": "Création",
"user_create": "Votre utilisateur a été créé",
"user_update": "Votre utilisateur a été modifié",
"request_error": "Requête mal construite",
"incorrect_password": "Mot de passe incorrect",
"unknown_user": "Utilisateur inconnu",
"disabled_user": "Utilisateur désactivé",
"invalid_token": "Token invalide",
"internal_error_server": "Erreur interne de serveur",
"unknown_error_auth": "Problème d'authentification inconnu",
"required_input": "Champ requis",
"create_profile": "Creation profil",
"edit_pseudo": "Modifier le pseudo",
"password":"Mot de passe",
"enter_password": "Entrez le password",
"password_confirmed": "Confirmez le mot de passe",
"last_name": "Nom",
"first_name": "Prénom",
"email": "Email",
"edit_last_name": "Modifier le nom",
"edit_first_name": "Modifier le prénom",
"edit_email": "Modifier l'email",
"birth_date": "Date de naissance",
"edit_birth": "Modifier la date de naissance",
"create_profile_button": "Créer le profil",
"take_picture": "Take a picture",
"error_ia": "L'IA de Google n'a pas su analyser l'image. Recommencer avec une autre",
"no_data_geo": "Aucune donnée géographique",
"response_status_update": "Code du statut de réponse de la modification",
"error_token": "Erreur de token",
"error_format": "Erreur de format de donnée fourni par l'IA",
"display_picture": "Display the Picture",
"analyze_image": "Analyse de l'image en cours",
"loading_progress": "Chargement en cours",
"error_event": "Erreur de l'évènement",
"no_future_event": "Évènement non futur",
"error_user": "Erreur de l'utilisateur",
"empty_input": "Champ vide",
"info_event": "Event info",
"event_already": "Event already exists",
"picture_error": "Erreur image",
"no_picture_published": "Image non publiée",
"event_update": "Évènement modifié",
"location": "Lieu",
"add_event": "Ajouter ou modifier un évènement",
"edit_image": "Changer la photo",
"name": "Nom",
"edit_event_name": "Changer le nom de l'évènement",
"start_time": "Heure de début",
"end_time": "Heure de fin",
"select_date": "Cliquer pour selectionner une date",
"select_time": "Cliquer pour selectionner une heure",
"tag": "Tags",
"already_tag": "Tu as déjà entré ce tag",
"enter_tag": "Entrer un tag",
"organizer": "Organisateur",
"already_organiser": "Tu as déjà rentré cet organisateur",
"enter_organizer": "Entrer un organisateur",
"description": "Description",
"describe_event": "Décrire l'évènement",
"add": "Ajouter",
"update_profile": "Modifier le profil",
"different_password_error": "Mot de passe différent",
"update": "Mettre à jour",
"updated": "Mis à jour",
"settings_updated": "Paramètre mis à jour",
"define_kilometer": "Definir un kilomètre",
"settings": "Paramètres",
"email_sent": "Email a été envoyé",
"forgot_password": "Mot de passe oublié",
"enter_email": "Entrez l'email",
"send_email": "Send email",
"invalid_cache": "Cache invalide",
"item_date": "Date : ",
"item_maps": "Carte : ",
"item_organizer": "Organisateurs : ",
"item_description": "Description : ",
"item_tags": "Tags : ",
"failed_auth": "Échec de l'authenticaton",
"login_page": "Page d'authentification",
"pseudo": "Pseudo",
"enter_existing_pseudo": "Entrez un pseudo existant",
"remembr_me": "Se souvenir de moi",
"new_user": "Nouvel utilisateur ? Créer un compte",
"sign_in": "Se connecter",
"map_token": "Token d'accès de Mapbox n'est pas disponible",
"geo_disabled": "Les services de localisation sont désactivés.",
"permission_denied":"Les permissions de localisation sont refusées.",
"enable_permission": "Les permissions de localisation sont toujours désactivés. Il faut les désactiver",
"no_last_position": "Aucune position n'est pas disponible.",
"failed_location": "Échec de récupération des données geographique",
"failed_fetch": "Échec de récupération des routes",
"invalid_coordinates_symbol": "Coordonnées invalides. On ne peut pas ajouter le symbole",
"error_symbol": "Erreur lors de l'ajout du symbole",
"position_not_init": "Coordonnées non initialisées. Essaye encore.",
"invalid_coordinates": "Coordonnées invalides",
"walking": "Marche",
"cycling": "Vélo",
"driving": "Voiture",
"get_direction": "Get Directions and Markers",
"missing_token": "Token d'accès manquant",
"geocoding_error": "Erreur lors du geocodage",
"no_found_place": "Lieu introuvable",
"upload_error": "Erreur lors de l'upload d'image",
"event_added": "Évènement ajouté",
"unknown_error": "Erreur inconnue",
"app_error": "Erreur d'application",
"at": "à",
"to_date": "jusqu'à"
}

View File

@@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class LocaleProvider with ChangeNotifier {
static const _localeKey = 'locale_code';
Locale _locale = const Locale('en');
Locale get locale => _locale;
LocaleProvider() {
_loadLocale();
}
Future<void> _loadLocale() async {
final prefs = await SharedPreferences.getInstance();
final code = prefs.getString(_localeKey);
if (code != null && L10n.all.contains(Locale(code))) {
_locale = Locale(code);
notifyListeners();
}
}
void setLocale(Locale locale) async {
if (!L10n.all.contains(locale)) return;
_locale = locale;
notifyListeners();
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_localeKey, locale.languageCode);
}
}
class L10n {
static final all = [
const Locale('en'),
const Locale('fr'),
const Locale('de')
];
}

View File

@@ -1,19 +1,33 @@
import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:provider/provider.dart';
import 'pages/LoginDemo.dart';
import 'locale_provider.dart'; // <-- à adapter selon ton arborescence
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
runApp(MyApp());
runApp(
ChangeNotifierProvider(
create: (_) => LocaleProvider(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final localeProvider = Provider.of<LocaleProvider>(
context); // écoute les changements de langue
return MaterialApp(
debugShowCheckedModeBanner: false,
locale: localeProvider.locale, // <-- utilise la locale courante
supportedLocales: L10n.all,
localizationsDelegates: AppLocalizations.localizationsDelegates,
home: LoginDemo(),
);
}

View File

@@ -13,6 +13,10 @@ import '../variable/globals.dart' as globals;
import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; // Créé plus loin
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -95,7 +99,11 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
if ((password.isNotEmpty) && (confirmedPassword.isNotEmpty)) {
if (password != confirmedPassword) {
showAlertDialog(context, "Erreur", "Mot de passe different");
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.password_different ??
"Must write a different password");
return;
}
}
@@ -117,24 +125,32 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
}));
print(responsePost.statusCode);
if (responsePost.statusCode == 200) {
showAlertDialog(context, "Creation", "Votre utilisateur a été créé");
showAlertDialog(
context,
AppLocalizations.of(context)?.create ?? "Creation",
AppLocalizations.of(context)?.user_create ?? "Your user created");
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (_) => LoginDemo()));
return;
}
final errorMessages = {
400: "Requête mal construite",
406: "Mot de passe incorrect",
404: "Utilisateur inconnu",
403: "Utilisateur désactivé",
410: "Token invalide",
500: "Problème interne du serveur",
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 text = errorMessages[responsePost.statusCode] ??
"Problème d'authentification inconnu";
showAlertDialog(context, "Erreur serveur", text);
AppLocalizations.of(context)?.unknown_error_auth ??
"Unknown error auth";
showAlertDialog(
context, AppLocalizations.of(context)?.error ?? "Error", text);
}
@override
@@ -150,7 +166,9 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
final _formKey = GlobalKey<FormState>();
String? _validateField(String? value) {
return value!.isEmpty ? 'Champ requis' : null;
return value!.isEmpty
? AppLocalizations.of(context)?.required_input ?? 'Required input'
: null;
}
@override
@@ -158,7 +176,8 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Create profile"),
title: Text(
AppLocalizations.of(context)?.create_profile ?? "Create profile"),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
@@ -183,7 +202,8 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Pseudo',
hintText: 'Modifier le pseudo'),
hintText: AppLocalizations.of(context)?.edit_pseudo ??
'Edit pseudo'),
),
),
Padding(
@@ -196,8 +216,11 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Mot de passe',
hintText: 'Entrez le mot de passe'),
labelText: AppLocalizations.of(context)?.password ??
'Password',
hintText:
AppLocalizations.of(context)?.enter_password ??
'Enter the password'),
),
),
Padding(
@@ -209,9 +232,14 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
validator: (value) => _validateField(value),
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Confirmez le mot de passe',
hintText: 'Confirmez le mot de passe'),
border: OutlineInputBorder(),
labelText:
AppLocalizations.of(context)?.password_confirmed ??
'Password confirmed',
hintText:
AppLocalizations.of(context)?.password_confirmed ??
'Password confirmed',
),
),
),
Padding(
@@ -223,8 +251,11 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Nom',
hintText: 'Modifier le nom'),
labelText: AppLocalizations.of(context)?.last_name ??
'Last name',
hintText:
AppLocalizations.of(context)?.edit_last_name ??
'Edit last name'),
),
),
Padding(
@@ -236,8 +267,11 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Prénom',
hintText: 'Modifier le prénom'),
labelText: AppLocalizations.of(context)?.first_name ??
'First name',
hintText:
AppLocalizations.of(context)?.edit_first_name ??
'Edit first name'),
),
),
Padding(
@@ -249,8 +283,10 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Email',
hintText: 'Modifier l\'adresse mail'),
labelText:
AppLocalizations.of(context)?.email ?? 'Email',
hintText: AppLocalizations.of(context)?.edit_email ??
'Edit email'),
),
),
Padding(
@@ -263,8 +299,10 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Date de naissance',
hintText: 'Cliquez ici pour selectionner une date'),
labelText: AppLocalizations.of(context)?.birth_date ??
'Birth date',
hintText: AppLocalizations.of(context)?.edit_birth ??
'Edit birth date'),
onTap: () => onTapFunctionDatePicker(context: context)),
),
SizedBox(
@@ -283,7 +321,8 @@ class _AddProfileState extends State<AddProfile> with ShowAlertDialog {
}
},
child: Text(
'Créer le profil',
AppLocalizations.of(context)?.create_profile_button ??
"Create profile",
style: TextStyle(color: Colors.white, fontSize: 25),
),
),

View File

@@ -7,6 +7,10 @@ import 'DisplayPictureScreen.dart';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; // Créé plus loin
Future<void> main() async {
// Ensure that plugin services are initialized so that `availableCameras()`
@@ -92,7 +96,9 @@ class CameraState extends State<Camera> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Take a picture')),
appBar: AppBar(
title: Text(AppLocalizations.of(context)?.take_picture ??
"Take a picture")),
// You must wait until the controller is initialized before displaying the
// camera preview. Use a FutureBuilder to display a loading spinner until the
// controller has finished initializing.

View File

@@ -8,6 +8,10 @@ import 'EditEvent.dart';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; // Créé
Future<void> main() async {
// Ensure that plugin services are initialized so that `availableCameras()`
@@ -94,7 +98,9 @@ class CameraEditState extends State<CameraEdit> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Take a picture')),
appBar: AppBar(
title: Text(AppLocalizations.of(context)?.take_picture ??
'Take a picture')),
// You must wait until the controller is initialized before displaying the
// camera preview. Use a FutureBuilder to display a loading spinner until the
// controller has finished initializing.

View File

@@ -17,6 +17,10 @@ import '../classes/MyDrawer.dart';
import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; // Créé
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -98,8 +102,46 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
Future<void> displayError(String e) async {
print("problem gemini : ${e}");
showAlertDialog(context, 'Error IA',
"L'IA de Google n'a pas su analyser l'image. Recommecer avec une autre");
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? 'Error',
AppLocalizations.of(context)?.error_ia ??
'Google AI failed to analyze picture. Retry with another one');
}
void _showErrorDialog(BuildContext context, String title, String message) {
showAlertDialog(context, title, message);
}
Future<Map<String, dynamic>?> _fetchGeolocation(String place) async {
final apiKey = dotenv.env['PLACE_API_KEY'] ?? '';
final response = await http.get(Uri.parse(
'https://maps.googleapis.com/maps/api/place/textsearch/json?query=${place}}&key=$apiKey'));
if (response.statusCode == 200) {
final data = json.decode(response.body);
if (data['results'].isNotEmpty) {
return data['results'][0]['geometry']['location'];
}
}
return null;
}
Future<bool> _isDuplicateEvent(String accessToken,
Map<String, dynamic> jsonData, Map<String, dynamic> location) async {
final url = Uri.parse(
"${globals.api}/events/search?item=${jsonData["name"]}&date_event=${jsonData["start_date"]}"
"&min_lat=${location['lat']}&max_lat=${location['lat']}"
"&min_lon=${location['lng']}&max_lon=${location['lng']}");
final response = await http.get(url,
headers: {HttpHeaders.cookieHeader: 'access_token=$accessToken'});
if (response.statusCode == 200) {
final events = jsonDecode(utf8.decode(response.bodyBytes));
return events.isNotEmpty;
}
return false;
}
Future<void> searchEvents(String json, String imagePath) async {
@@ -116,13 +158,25 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
var accessToken = prefs.getString("access_token") ?? "";
if (accessToken.isNotEmpty) {
var urlGet = Uri.parse(
"${globals.api}/events/search?item=${name}&date_event=${date}");
final location = await _fetchGeolocation(place);
if (location == null) {
_showErrorDialog(
context,
AppLocalizations.of(context)?.error ?? 'Error',
AppLocalizations.of(context)?.no_data_geo ??
'No geographical data');
return;
}
var responseGet = await http.get(urlGet,
headers: {HttpHeaders.cookieHeader: 'access_token=${accessToken}'});
if (responseGet.statusCode == 200) {
var events = jsonDecode(utf8.decode(responseGet.bodyBytes));
final url = Uri.parse(
"${globals.api}/events/search?item=${name}&date_event=${date}"
"&min_lat=${location['lat']}&max_lat=${location['lat']}"
"&min_lon=${location['lng']}&max_lon=${location['lng']}");
final response = await http.get(url,
headers: {HttpHeaders.cookieHeader: 'access_token=$accessToken'});
if (response.statusCode == 200) {
var events = jsonDecode(utf8.decode(response.bodyBytes));
print("reponse http : ${events.length}");
if (events.length == 0) {
Navigator.push(
@@ -137,15 +191,23 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
builder: (_) => ItemMenu(title: events[0]["id"])));
}
} else {
showAlertDialog(context, 'Erreur de reponse',
"response status code update : ${responseGet.statusCode}");
String error = AppLocalizations.of(context)?.response_status_update ??
'Response status update : ${response.statusCode}';
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? 'Error',
"${error} : ${response.statusCode}");
}
} else {
showAlertDialog(context, "Erreur de reponse", "Erreur de token");
showAlertDialog(context, AppLocalizations.of(context)?.error ?? 'Error',
AppLocalizations.of(context)?.error_token ?? "Token error");
}
} catch (e) {
showAlertDialog(
context, "Erreur IA", "Erreur de format de donnée fourni par l'IA");
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.error_format ??
"Data format error given by AI");
}
//showDescImageAddDialog(context, message);
@@ -162,7 +224,7 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
gemini
.textAndImage(
text:
"Peux-tu donner le nom, la date avec l'année actuelle ou d'une année future proche 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",
images: [file.readAsBytesSync()],
modelName: "models/gemini-1.5-pro-latest")
.then((value) => searchEvents(
@@ -173,7 +235,9 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Display the Picture')),
appBar: AppBar(
title: Text(AppLocalizations.of(context)?.display_picture ??
"Display The Picture")),
// The image is stored as a file on the device. Use the `Image.file`
// constructor with the given path to display the image.
drawer: MyDrawer(),
@@ -189,12 +253,15 @@ class DisplayPictureScreenState extends State<DisplayPictureScreen>
width: _bannerAd!.size.width.toDouble(),
child: AdWidget(ad: _bannerAd!)),
Text(
'Analyse de l\'image en cours',
AppLocalizations.of(context)?.analyze_image ??
'Image analyze in progress',
style: Theme.of(context).textTheme.titleLarge,
),
CircularProgressIndicator(
value: controller.value,
semanticsLabel: 'Loading progress',
semanticsLabel:
AppLocalizations.of(context)?.loading_progress ??
'Loading progress',
),
])));
}

View File

@@ -22,6 +22,10 @@ import '../variable/globals.dart' as globals;
import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; // Créé
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -166,13 +170,19 @@ class _EditEventState extends State<EditEvent>
Future<void> _updateEvent(BuildContext context) async {
if (!_isEventInFuture()) {
_showErrorDialog(context, "Erreur evenement", "Evenement non futur");
_showErrorDialog(
context,
AppLocalizations.of(context)?.error_event ?? "Event error",
AppLocalizations.of(context)?.no_future_event ?? "No future event");
return;
}
final accessToken = await _getAccessToken();
if (accessToken.isEmpty) {
_showErrorDialog(context, "Erreur utilisateur", "Champ vide");
_showErrorDialog(
context,
AppLocalizations.of(context)?.error_user ?? "User error",
AppLocalizations.of(context)?.empty_input ?? "Empty input");
return;
}
@@ -181,27 +191,41 @@ class _EditEventState extends State<EditEvent>
final geolocation = await _fetchGeolocation();
if (geolocation == null) {
_showErrorDialog(
context, "Erreur serveur", "Aucune donnée geographique");
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.no_data_geo ??
"No geographical data");
return;
}
if (await _isDuplicateEvent(accessToken, geolocation)) {
_showErrorDialog(context, "Info evenement", "Evenement deja existant");
_showErrorDialog(
context,
AppLocalizations.of(context)?.info_event ?? "Event info",
AppLocalizations.of(context)?.event_already ??
"Event already exists");
return;
}
if (widget.imgPath.isNotEmpty) {
imgUrl = await _uploadImage(widget.imgPath);
if (imgUrl.isEmpty) {
_showErrorDialog(context, "Erreur image", "Image non postée");
_showErrorDialog(
context,
AppLocalizations.of(context)?.picture_error ?? "Error picture",
AppLocalizations.of(context)?.no_picture_published ??
"No picture published");
return;
}
}
await _updateEventData(accessToken, geolocation);
showEventDialog(context, "Evenement ${inputName.text} modifie");
String message =
AppLocalizations.of(context)?.event_update ?? "Event updated";
showEventDialog(context, "${message} : ${inputName.text}");
} catch (e) {
_showErrorDialog(context, "Erreur serveur", "$e");
_showErrorDialog(
context, AppLocalizations.of(context)?.error ?? "Error", "$e");
}
}
@@ -297,15 +321,22 @@ class _EditEventState extends State<EditEvent>
void _handleErrorResponse(BuildContext context, int statusCode) {
final messages = {
400: "Requête mal construite",
406: "Mot de passe incorrect",
404: "Utilisateur inconnu",
403: "Utilisateur désactivé",
410: "Token invalide",
500: "Problème interne du serveur"
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"
};
_showErrorDialog(context, "Erreur serveur",
messages[statusCode] ?? "Problème d'authentification inconnu");
_showErrorDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
messages[statusCode] ??
AppLocalizations.of(context)?.unknown_error_auth ??
"Unknown error auth");
}
void _showErrorDialog(BuildContext context, String title, String message) {
@@ -349,7 +380,9 @@ class _EditEventState extends State<EditEvent>
final _formKey = GlobalKey<FormState>();
String? _validateField(String? value) {
return value!.isEmpty ? 'Champ requis' : null;
return value!.isEmpty
? AppLocalizations.of(context)?.required_input ?? "Required input"
: null;
}
Future<void> searchSuggestions(String input) async {
@@ -360,11 +393,9 @@ class _EditEventState extends State<EditEvent>
'https://api.mapbox.com/geocoding/v5/mapbox.places/${input}.json?access_token=${mapboxAccessToken}&types=poi,address,place';
var encoded = Uri.encodeFull(url);
final response = await http.get(Uri.parse(encoded));
print("response code suggesttion : ${response.statusCode}");
if (response.statusCode == 200) {
final data = json.decode(response.body);
print("data suggestion : ${data}");
setState(() {
suggestions = (data['features'] as List)
.map((feature) => {
@@ -389,7 +420,7 @@ class _EditEventState extends State<EditEvent>
TextField(
controller: inputGeo,
decoration: InputDecoration(
labelText: 'Lieu',
labelText: AppLocalizations.of(context)?.location ?? "Location",
border: OutlineInputBorder(),
suffixIcon: IconButton(
icon: const Icon(Icons.clear),
@@ -459,7 +490,8 @@ class _EditEventState extends State<EditEvent>
backgroundColor: Colors.white,
drawer: MyDrawer(),
appBar: AppBar(
title: Text("Add or Update a event"),
title: Text(AppLocalizations.of(context)?.add_event ??
"Add or Update a event"),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
@@ -516,7 +548,8 @@ class _EditEventState extends State<EditEvent>
child: ElevatedButton.icon(
onPressed: popCamera,
icon: Icon(Icons.edit, size: 16), // Edit icon
label: Text("Edit Image"), // Button text
label: Text(AppLocalizations.of(context)?.edit_image ??
"Edit pictures"), // Button text
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Button color
foregroundColor: Colors.white, // Text color
@@ -533,8 +566,10 @@ class _EditEventState extends State<EditEvent>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Nom',
hintText: 'Modifier le nom de l\'évènement'),
labelText: AppLocalizations.of(context)?.name ?? "Name",
hintText:
AppLocalizations.of(context)?.edit_event_name ??
"Edit event name"),
),
),
_buildGeographicalZoneSearchField(),
@@ -548,8 +583,10 @@ class _EditEventState extends State<EditEvent>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Date de debut',
hintText: 'Cliquez ici pour selectionner une date'),
labelText: AppLocalizations.of(context)?.start_date ??
"Start date",
hintText: AppLocalizations.of(context)?.select_date ??
"Click to select a date"),
onTap: () => onTapFunctionDatePicker(
context: context, position: "start")),
),
@@ -563,8 +600,10 @@ class _EditEventState extends State<EditEvent>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Heure de debut',
hintText: 'Cliquez ici pour selectionner une heure'),
labelText: AppLocalizations.of(context)?.start_time ??
"Start time",
hintText: AppLocalizations.of(context)?.select_time ??
"Click to select a time"),
onTap: () => onTapFunctionTimePicker(
context: context, position: "start")),
),
@@ -578,8 +617,10 @@ class _EditEventState extends State<EditEvent>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Date de fin',
hintText: 'Cliquez ici pour selectionner une date'),
labelText: AppLocalizations.of(context)?.end_date ??
"End date",
hintText: AppLocalizations.of(context)?.select_time ??
"Click to select a date"),
onTap: () => onTapFunctionDatePicker(
context: context, position: "end")),
),
@@ -593,8 +634,10 @@ class _EditEventState extends State<EditEvent>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Heure de fin',
hintText: 'Cliquez ici pour selectionner une heure'),
labelText: AppLocalizations.of(context)?.end_time ??
"End time",
hintText: AppLocalizations.of(context)?.select_time ??
"Click to select a time"),
onTap: () => onTapFunctionTimePicker(
context: context, position: "end")),
),
@@ -604,7 +647,8 @@ class _EditEventState extends State<EditEvent>
textSeparators: const [' ', ','],
validator: (String tag) {
if (_stringTagController.getTags!.contains(tag)) {
return 'Tu as deja rentre ce tag';
return AppLocalizations.of(context)?.already_tag ??
"You have already entered this tag";
}
return null;
},
@@ -619,10 +663,12 @@ class _EditEventState extends State<EditEvent>
onSubmitted: inputFieldValues.onTagSubmitted,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Tags',
labelText:
AppLocalizations.of(context)?.tag ?? 'Tags',
hintText: inputFieldValues.tags.isNotEmpty
? ''
: "Enter tag...",
: AppLocalizations.of(context)?.enter_tag ??
"Enter tag...",
errorText: inputFieldValues.error,
prefixIcon: inputFieldValues.tags.isNotEmpty
? SingleChildScrollView(
@@ -699,7 +745,9 @@ class _EditEventState extends State<EditEvent>
textSeparators: const [','],
validator: (String tag) {
if (_stringOrgaController.getTags!.contains(tag)) {
return 'Cet organisateur est déjà rentré';
return AppLocalizations.of(context)
?.already_organiser ??
"You have already entered this organizer";
}
return null;
},
@@ -714,10 +762,14 @@ class _EditEventState extends State<EditEvent>
onSubmitted: inputFieldValues.onTagSubmitted,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Organisateurs',
labelText:
AppLocalizations.of(context)?.organizer ??
"Organizer",
hintText: inputFieldValues.tags.isNotEmpty
? ''
: "Enter un organisateur...",
: AppLocalizations.of(context)
?.enter_organizer ??
"Enter a organizer",
errorText: inputFieldValues.error,
prefixIcon: inputFieldValues.tags.isNotEmpty
? SingleChildScrollView(
@@ -798,8 +850,11 @@ class _EditEventState extends State<EditEvent>
maxLines: 10,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Description',
hintText: 'Décrire l\'evènement'),
labelText: AppLocalizations.of(context)?.description ??
'Description',
hintText:
AppLocalizations.of(context)?.describe_event ??
'Describe event'),
),
),
SizedBox(
@@ -818,7 +873,7 @@ class _EditEventState extends State<EditEvent>
}
},
child: Text(
'Ajouter',
AppLocalizations.of(context)?.add ?? 'Add',
style: TextStyle(color: Colors.white, fontSize: 25),
),
),

View File

@@ -18,6 +18,10 @@ import '../variable/globals.dart' as globals;
import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; // Créé
void main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -104,7 +108,11 @@ class _EditProfileState extends State<EditProfile>
if ((password.isNotEmpty) && (confirmedPassword.isNotEmpty)) {
if (password != confirmedPassword) {
showAlertDialog(context, "Erreur", "Mot de passe different");
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.different_password_error ??
"Different password");
return;
}
}
@@ -131,24 +139,29 @@ class _EditProfileState extends State<EditProfile>
}));
print(responsePut.statusCode);
if (responsePut.statusCode == 200) {
showEventDialog(context, "Votre utilisateur a été modifié");
showEventDialog(context,
AppLocalizations.of(context)?.user_update ?? "Your user updated");
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (_) => EditProfile()));
return;
}
final errorMessages = {
400: "Requête mal construite",
406: "Mot de passe incorrect",
404: "Utilisateur inconnu",
403: "Utilisateur désactivé",
410: "Token invalide",
500: "Problème interne du serveur",
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 text = errorMessages[responsePut.statusCode] ??
"Problème d'authentification inconnu";
showAlertDialog(context, "Erreur serveur", text);
final text = messages[responsePut.statusCode] ??
AppLocalizations.of(context)?.unknown_error_auth ??
"Unknown error auth";
showAlertDialog(
context, AppLocalizations.of(context)?.error ?? "Error", text);
} else {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (_) => LoginDemo()));
@@ -180,18 +193,22 @@ class _EditProfileState extends State<EditProfile>
return;
}
final errorMessages = {
400: "Requête mal construite",
406: "Mot de passe incorrect",
404: "Utilisateur inconnu",
403: "Utilisateur désactivé",
410: "Token invalide",
500: "Problème interne du serveur",
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 text = errorMessages[responseGet.statusCode] ??
"Problème d'authentification inconnu";
showAlertDialog(context, "Erreur serveur", text);
final text = messages[responseGet.statusCode] ??
AppLocalizations.of(context)?.unknown_error_auth ??
"Unknown error auth";
showAlertDialog(
context, AppLocalizations.of(context)?.error ?? "Error", text);
} else {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (_) => LoginDemo()));
@@ -213,7 +230,9 @@ class _EditProfileState extends State<EditProfile>
final _formKey = GlobalKey<FormState>();
String? _validateField(String? value) {
return value!.isEmpty ? 'Champ requis' : null;
return value!.isEmpty
? AppLocalizations.of(context)?.required_input ?? "Required input"
: null;
}
@override
@@ -221,7 +240,8 @@ class _EditProfileState extends State<EditProfile>
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Update profile"),
title: Text(
AppLocalizations.of(context)?.update_profile ?? "Update profile"),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
@@ -246,8 +266,9 @@ class _EditProfileState extends State<EditProfile>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Pseudo',
hintText: 'Modifier le pseudo'),
labelText: AppLocalizations.of(context)?.name,
hintText: AppLocalizations.of(context)?.edit_pseudo ??
"Edit pseudo"),
),
),
Padding(
@@ -259,8 +280,11 @@ class _EditProfileState extends State<EditProfile>
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Mot de passe',
hintText: 'Entrez le mot de passe'),
labelText: AppLocalizations.of(context)?.password ??
"Password",
hintText:
AppLocalizations.of(context)?.enter_password ??
"Enter a password"),
),
),
Padding(
@@ -272,8 +296,12 @@ class _EditProfileState extends State<EditProfile>
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Confirmez le mot de passe',
hintText: 'Confirmez le mot de passe'),
labelText:
AppLocalizations.of(context)?.password_confirmed ??
"Must confirm password",
hintText:
AppLocalizations.of(context)?.password_confirmed ??
"Must confirm password"),
),
),
Padding(
@@ -285,8 +313,11 @@ class _EditProfileState extends State<EditProfile>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Nom',
hintText: 'Modifier le nom'),
labelText: AppLocalizations.of(context)?.last_name ??
"Last name",
hintText:
AppLocalizations.of(context)?.edit_last_name ??
"Edit last name"),
),
),
Padding(
@@ -298,8 +329,11 @@ class _EditProfileState extends State<EditProfile>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Prénom',
hintText: 'Modifier le prénom'),
labelText: AppLocalizations.of(context)?.first_name ??
"First name",
hintText:
AppLocalizations.of(context)?.edit_first_name ??
"Edit first name"),
),
),
Padding(
@@ -311,8 +345,10 @@ class _EditProfileState extends State<EditProfile>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Email',
hintText: 'Modifier l\'adresse mail'),
labelText:
AppLocalizations.of(context)?.email ?? "Email",
hintText: AppLocalizations.of(context)?.edit_email ??
"Edit email"),
),
),
Padding(
@@ -325,8 +361,9 @@ class _EditProfileState extends State<EditProfile>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Date de naissance',
hintText: 'Cliquez ici pour selectionner une date'),
labelText: AppLocalizations.of(context)?.birth_date,
hintText: AppLocalizations.of(context)?.edit_birth ??
"Click to select a birth date"),
onTap: () => onTapFunctionDatePicker(context: context)),
),
SizedBox(
@@ -345,7 +382,8 @@ class _EditProfileState extends State<EditProfile>
}
},
child: Text(
'Modifier le profil',
AppLocalizations.of(context)?.update_profile ??
"Update profile ",
style: TextStyle(color: Colors.white, fontSize: 25),
),
),

View File

@@ -14,6 +14,11 @@ import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; // Créé
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
@@ -56,7 +61,10 @@ class _EditProfileState extends State<EditSettings>
SharedPreferences prefs = await SharedPreferences.getInstance();
if (kilometer != null) {
prefs.setDouble("kilometer", kilometer?.toDouble() ?? 50);
showAlertDialog(context, "Update", "Mise à jour des paramètres");
showAlertDialog(
context,
AppLocalizations.of(context)?.updated ?? "Updated",
AppLocalizations.of(context)?.settings_updated ?? "Settings updated");
}
}
@@ -78,7 +86,7 @@ class _EditProfileState extends State<EditSettings>
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Settings"),
title: Text(AppLocalizations.of(context)?.settings ?? "Settings"),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
@@ -103,7 +111,9 @@ class _EditProfileState extends State<EditSettings>
child: DropdownButtonFormField<int>(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Define kilometer',
labelText:
AppLocalizations.of(context)?.define_kilometer ??
'Define kilometer',
),
value:
kilometer, // Set the initial selected value here, or leave as `null` if unselected.
@@ -147,7 +157,7 @@ class _EditProfileState extends State<EditSettings>
child: TextButton(
onPressed: () {},
child: Text(
'Mettre à jour',
AppLocalizations.of(context)?.update ?? "Update",
style: TextStyle(color: Colors.white, fontSize: 25),
),
),

View File

@@ -11,6 +11,11 @@ import '../classes/alert.dart';
import '../variable/globals.dart' as globals;
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; //
void main() {
runApp(MyApp());
}
@@ -74,22 +79,32 @@ class _PasswordForgotState extends State<PasswordForgot> with ShowAlertDialog {
}));
print(responsePost.statusCode);
if (responsePost.statusCode == 200) {
showAlertDialog(context, "Creation", "Un email a été envoyé à ${email}");
String message =
AppLocalizations.of(context)?.email_sent ?? "Email has been sent";
showAlertDialog(
context,
AppLocalizations.of(context)?.create ?? "Creation",
"${message} : ${email}");
return;
}
final errorMessages = {
400: "Requête mal construite",
406: "Mot de passe incorrect",
404: "Utilisateur inconnu",
403: "Utilisateur désactivé",
410: "Token invalide",
500: "Problème interne du serveur",
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 text = errorMessages[responsePost.statusCode] ??
"Problème d'authentification inconnu";
showAlertDialog(context, "Erreur serveur", text);
final text = messages[responsePost.statusCode] ??
AppLocalizations.of(context)?.unknown_error_auth ??
"Unknown error auth";
showAlertDialog(
context, AppLocalizations.of(context)?.error ?? "Error", text);
}
@override
@@ -107,7 +122,8 @@ class _PasswordForgotState extends State<PasswordForgot> with ShowAlertDialog {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Mot de passe oublie"),
title: Text(AppLocalizations.of(context)?.forgot_password ??
"Forgot password"),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
@@ -125,8 +141,10 @@ class _PasswordForgotState extends State<PasswordForgot> with ShowAlertDialog {
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Email',
hintText: 'Modifier l\'adresse mail'),
labelText:
AppLocalizations.of(context)?.email ?? 'Email',
hintText: AppLocalizations.of(context)?.enter_email ??
'Enter the email'),
),
),
SizedBox(
@@ -145,7 +163,7 @@ class _PasswordForgotState extends State<PasswordForgot> with ShowAlertDialog {
}
},
child: Text(
'Envoyer le mail',
AppLocalizations.of(context)?.send_email ?? 'Send email',
style: TextStyle(color: Colors.white, fontSize: 25),
),
),

View File

@@ -25,6 +25,11 @@ import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; //
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
@@ -109,7 +114,8 @@ class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
final accessToken = prefs.getString("access_token") ?? "";
if (accessToken.isEmpty) {
showAlertDialog(context, "Erreur serveur", "Cache invalide");
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.invalid_cache ?? "Invalid cache");
return;
}
@@ -119,27 +125,35 @@ class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
headers: {HttpHeaders.cookieHeader: 'access_token=$accessToken'},
);
stderr.writeln('Response Get status: ${responseGet.statusCode}');
if (responseGet.statusCode == 200) {
final responseBody = utf8.decode(responseGet.bodyBytes);
stderr.writeln('Username : $responseBody');
final event = Events.fromJson(jsonDecode(responseBody));
final locale = Provider.of<LocaleProvider>(context, listen: false)
.locale
?.toString() ??
'en_US';
final startDate =
DateTime.parse(event.startDate ?? DateTime.now().toString());
//final date = DateFormat.yMd().format(startDate);
//final time = DateFormat.Hm().format(startDate);
final endDate =
DateTime.parse(event.endDate ?? DateTime.now().toString());
String separator = AppLocalizations.of(context)?.at ?? "at";
final formattedStartDate =
"${DateFormat.yMd().format(startDate)} ${DateFormat.Hm().format(startDate)}";
DateFormat("EEEE d MMMM y '${separator}' HH:mm", locale)
.format(startDate);
final formattedEndDate =
"${DateFormat.yMd().format(endDate)} ${DateFormat.Hm().format(endDate)}";
DateFormat("EEEE d MMMM y '${separator}' HH:mm", locale)
.format(endDate);
String link = AppLocalizations.of(context)?.to_date ?? "to";
setState(() {
eventName = event.name ?? "";
eventStartDate = "$formattedStartDate à $formattedEndDate";
eventStartDate = "$formattedStartDate ${link} $formattedEndDate";
organizers = List<String>.from(event.organizers ?? []);
place = event.place ?? "";
imgUrl = event.imgUrl ?? "";
@@ -147,18 +161,23 @@ class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
tags = List<String>.from(event.tags ?? []);
});
} else {
final errorMessages = {
400: "Requête mal construite",
406: "Mot de passe incorrect",
404: "Utilisateur inconnu",
403: "Vous n'avez pas l'autorisation de faire cette action",
410: "Token invalide",
500: "Problème interne du serveur",
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 = errorMessages[responseGet.statusCode] ??
"Problème d'authentification inconnu";
showAlertDialog(context, "Erreur serveur", errorMessage);
final errorMessage = messages[responseGet.statusCode] ??
AppLocalizations.of(context)?.unknown_error_auth ??
"Unknown error auth";
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error",
errorMessage);
}
}
@@ -223,7 +242,7 @@ class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
Row(children: [
Icon(Icons.event),
Text(
"Date : ",
AppLocalizations.of(context)?.item_date ?? "Date : ",
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
)
]),
@@ -237,7 +256,7 @@ class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
Row(children: [
Icon(Icons.explore),
Text(
"Carte : ",
AppLocalizations.of(context)?.item_maps ?? "Maps : ",
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
)
]),
@@ -260,7 +279,7 @@ class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
Row(children: [
Icon(Icons.group),
Text(
"Organisateurs : ",
AppLocalizations.of(context)?.item_organizer ?? "Organizers : ",
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
)
]),
@@ -313,7 +332,9 @@ class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
]),
Row(children: [
Icon(Icons.description),
Text("Description : ",
Text(
AppLocalizations.of(context)?.item_description ??
"Description : ",
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold))
]),
Row(children: [
@@ -325,7 +346,7 @@ class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
]),
Row(children: [
Icon(Icons.category),
Text("Tags : ",
Text(AppLocalizations.of(context)?.item_tags ?? "Tags : ",
style: TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold))
]),
Row(
@@ -394,7 +415,7 @@ class _ItemMenuState extends State<ItemMenu> with ShowAlertDialog {
);
},
backgroundColor: Colors.blue,
tooltip: 'Recherche',
tooltip: AppLocalizations.of(context)?.search ?? 'Search',
child: const Icon(Icons.edit, color: Colors.white),
),
);

View File

@@ -12,10 +12,20 @@ import 'package:intl/date_symbol_data_local.dart';
import '../variable/globals.dart' as globals;
import '../classes/MyDrawer.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; //
// app starting point
void main() {
initializeDateFormatting("fr_FR", null).then((_) => (const MyApp()));
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeDateFormatting("fr_FR", null);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
@@ -24,6 +34,14 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('fr', 'FR'),
],
home: const ListItemOrganizers(organizer: "default"),
debugShowCheckedModeBanner: false,
);
@@ -42,17 +60,54 @@ class ListItemOrganizers extends StatefulWidget {
// homepage state
class _MyHomePageState extends State<ListItemOrganizers> {
int _fetchCount = 0;
bool _isLoading = false;
late ScrollController _scrollController;
final AuthService _authService = AuthService();
void _incrementFetchCount() {
setState(() {
_fetchCount++;
});
}
void _scrollListener() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_incrementFetchCount();
_fetchData();
}
}
Future<void> _fetchData() async {
if (_isLoading) return;
setState(() {
_isLoading = true;
});
await Future.delayed(Duration(seconds: 2));
getPosts(widget.organizer, count: _fetchCount);
setState(() {
_isLoading = false;
});
}
// variable to call and store future list of posts
// function to fetch data from api and return future list of posts
static Future<List<Events>> getPosts(organizer) async {
static Future<List<Events>> getPosts(organizer, {count = 0}) async {
await initializeDateFormatting("fr_FR", null);
SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? "";
final List<Events> body = [];
if (accessToken.isNotEmpty) {
var url = Uri.parse("${globals.api}/events?organizers=${organizer}");
DateTime currentDatetime = DateTime.now();
num limit = 20 * (count + 1);
var url = Uri.parse(
"${globals.api}/events?organizers=${organizer}&limit=${limit}&current_datetime=${currentDatetime.toString()}");
final response = await http.get(url, headers: {
"Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=${accessToken}"
@@ -67,6 +122,14 @@ class _MyHomePageState extends State<ListItemOrganizers> {
void initState() {
super.initState();
_authService.checkTokenStatus(context);
_scrollController = ScrollController();
_scrollController.addListener(_scrollListener);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
// build function
@@ -88,7 +151,8 @@ class _MyHomePageState extends State<ListItemOrganizers> {
return buildPosts(posts);
} else {
// if no data, show simple Text
return const Text("No data available");
return Text(
AppLocalizations.of(context)?.no_data ?? "No data available");
}
},
),
@@ -98,27 +162,38 @@ class _MyHomePageState extends State<ListItemOrganizers> {
// function to display fetched data on screen
Widget buildPosts(List<Events> posts) {
String organizer =
AppLocalizations.of(context)?.item_organizer ?? "Organizer : ";
// ListView Builder to show data in a list
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text("Organisateur : ${widget.organizer}",
title: Text("${organizer}${widget.organizer}",
overflow: TextOverflow.ellipsis),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
body: ListView.separated(
itemCount: posts.length,
controller: _scrollController,
itemCount: posts.isNotEmpty
? posts.length + (_isLoading ? 1 : 0) // Add 1 only if loading
: 0,
itemBuilder: (context, index) {
final post = posts[index];
final startDate = DateTime.parse(post.startDate!);
final date = DateFormat.yMd().format(startDate);
final time = DateFormat.Hm().format(startDate);
final locale = Provider.of<LocaleProvider>(context, listen: false)
.locale
?.toString() ??
'en_US';
final dateLongue =
DateFormat('EEEE d MMMM y', locale).format(startDate);
return ListTile(
title: Text('${post.name!}'),
subtitle: Text('${post.place!}\n${date} ${time}'),
subtitle: Text('${post.place!}\n${dateLongue}'),
onTap: () {
Navigator.push(
context,

View File

@@ -13,10 +13,20 @@ import '../variable/globals.dart' as globals;
import '../classes/MyDrawer.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; //
// app starting point
void main() {
initializeDateFormatting("fr_FR", null).then((_) => (const MyApp()));
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeDateFormatting("fr_FR", null);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
@@ -25,6 +35,14 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('fr', 'FR'),
],
home: const ListItemTags(tags: "default"),
debugShowCheckedModeBanner: false,
);
@@ -44,15 +62,55 @@ class ListItemTags extends StatefulWidget {
class _MyHomePageState extends State<ListItemTags> {
// variable to call and store future list of posts
int _fetchCount = 0;
bool _isLoading = false;
late ScrollController _scrollController;
final AuthService _authService = AuthService();
void _incrementFetchCount() {
setState(() {
_fetchCount++;
});
}
void _scrollListener() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_incrementFetchCount();
_fetchData();
}
}
Future<void> _fetchData() async {
print("Counter : ${_fetchCount}");
if (_isLoading) return;
setState(() {
_isLoading = true;
});
await Future.delayed(Duration(seconds: 2));
getPosts(widget.tags, count: _fetchCount);
setState(() {
_isLoading = false;
});
}
// function to fetch data from api and return future list of posts
static Future<List<Events>> getPosts(tags) async {
static Future<List<Events>> getPosts(tags, {count = 0}) async {
await initializeDateFormatting("fr_FR", null);
SharedPreferences prefs = await SharedPreferences.getInstance();
var accessToken = prefs.getString("access_token") ?? "";
final List<Events> body = [];
if (accessToken.isNotEmpty) {
var url = Uri.parse("${globals.api}/events?tags=${tags}");
DateTime currentDatetime = DateTime.now();
num limit = 20 * (count + 1);
var url = Uri.parse(
"${globals.api}/events?tags=${tags}&limit=${limit}&current_datetime=${currentDatetime.toString()}");
final response = await http.get(url, headers: {
"Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=${accessToken}"
@@ -67,6 +125,15 @@ class _MyHomePageState extends State<ListItemTags> {
void initState() {
super.initState();
_authService.checkTokenStatus(context);
_authService.checkTokenStatus(context);
_scrollController = ScrollController();
_scrollController.addListener(_scrollListener);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
// build function
@@ -88,7 +155,8 @@ class _MyHomePageState extends State<ListItemTags> {
return buildPosts(posts);
} else {
// if no data, show simple Text
return const Text("No data available");
return Text(
AppLocalizations.of(context)?.no_data ?? "No data available");
}
},
),
@@ -99,25 +167,33 @@ class _MyHomePageState extends State<ListItemTags> {
// function to display fetched data on screen
Widget buildPosts(List<Events> posts) {
// ListView Builder to show data in a list
String tag = AppLocalizations.of(context)?.item_tags ?? "Tags : ";
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text("Tags : ${widget.tags}", overflow: TextOverflow.ellipsis),
title: Text("${tag}${widget.tags}", overflow: TextOverflow.ellipsis),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
body: ListView.separated(
itemCount: posts.length,
controller: _scrollController,
itemCount: posts.isNotEmpty
? posts.length + (_isLoading ? 1 : 0) // Add 1 only if loading
: 0,
itemBuilder: (context, index) {
final post = posts[index];
final startDate = DateTime.parse(post.startDate!);
final date = DateFormat.yMd().format(startDate);
final time = DateFormat.Hm().format(startDate);
final locale = Provider.of<LocaleProvider>(context, listen: false)
.locale
?.toString() ??
'en_US';
final dateLongue =
DateFormat('EEEE d MMMM y', locale).format(startDate);
return ListTile(
title: Text('${post.name!}'),
subtitle: Text('${post.place!}\n${date} ${time}'),
subtitle: Text('${post.place!}\n${dateLongue}'),
onTap: () {
Navigator.push(
context,

View File

@@ -19,11 +19,21 @@ import 'package:camera/camera.dart';
import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; // Créé plus loin
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
initializeDateFormatting("fr_FR", null).then((_) => runApp(const MyApp()));
await initializeDateFormatting("fr_FR", null);
runApp(ChangeNotifierProvider(
create: (_) => LocaleProvider(),
child: const MyApp(),
));
}
class MyApp extends StatelessWidget {
@@ -31,8 +41,17 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final localeProvider = Provider.of<LocaleProvider>(context);
return MaterialApp(
home: const ListItemMenu(),
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [const Locale('fr', 'FR'), const Locale('en')],
locale: localeProvider.locale,
home: Builder(builder: (context) => ListItemMenu()),
debugShowCheckedModeBanner: false,
);
}
@@ -48,6 +67,9 @@ class ListItemMenu extends StatefulWidget {
class _MyHomePageState extends State<ListItemMenu> {
BannerAd? _bannerAd;
final AuthService _authService = AuthService();
late ScrollController _scrollController;
int _fetchCount = 0;
bool _isLoading = false;
Future<List<Events>> postsFuture = getPosts();
List<Events> filteredPosts = [];
@@ -71,6 +93,7 @@ class _MyHomePageState extends State<ListItemMenu> {
bool showInputTag = true;
// Fetching events from API
static Future<List<Events>> getPosts() async {
await initializeDateFormatting("fr_FR");
PermissionStatus status = await Permission.location.status;
final List<Events> body = [];
var url = Uri.parse("${globals.api}/events");
@@ -144,6 +167,37 @@ class _MyHomePageState extends State<ListItemMenu> {
return "${year}-${month}-${day}";
}
void _incrementFetchCount() {
setState(() {
_fetchCount++;
});
_fetchData();
}
void _scrollListener() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
_incrementFetchCount();
}
// Scroll to top
}
Future<void> _fetchData() async {
print("Counter : ${_fetchCount}");
if (_isLoading) return;
setState(() {
_isLoading = true;
});
await Future.delayed(Duration(seconds: 2));
fetchPostsByLocation();
setState(() {
_isLoading = false;
});
}
@override
void initState() {
super.initState();
@@ -153,10 +207,19 @@ class _MyHomePageState extends State<ListItemMenu> {
_bannerAd = ad;
});
});
_scrollController = ScrollController();
_scrollController.addListener(_scrollListener);
// Initialize data fetch when the page loads
_getCurrentLocation();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
// Get the device's current location
Future<void> _getCurrentLocation() async {
PermissionStatus status = await Permission.location.status;
@@ -240,7 +303,6 @@ class _MyHomePageState extends State<ListItemMenu> {
}
} catch (e) {
fetchPostsByLocation();
print("Error getting city and country: $e");
}
}
@@ -291,7 +353,8 @@ class _MyHomePageState extends State<ListItemMenu> {
}
});
} else {
throw Exception('Failed to load suggestions');
throw Exception(AppLocalizations.of(context)?.failed_suggestions ??
'Failed to load suggestions');
}
}
@@ -346,8 +409,10 @@ class _MyHomePageState extends State<ListItemMenu> {
} else {
queryParameters = dateParameter;
}
int limit = 20 * (_fetchCount + 1);
return Uri.parse("${globals.api}/$endpoint?$queryParameters");
return Uri.parse(
"${globals.api}/$endpoint?$queryParameters&limit=${limit}");
}
Future<void> searchSuggestionsByItem(String input) async {
@@ -358,7 +423,7 @@ class _MyHomePageState extends State<ListItemMenu> {
var url = await getUrlForEvents();
final response = await http.get(url, headers: {
"Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=$accessToken"
HttpHeaders.cookieHeader: "acce0ss_token=$accessToken"
});
if (response.statusCode == 200) {
@@ -387,16 +452,13 @@ class _MyHomePageState extends State<ListItemMenu> {
"Content-Type": "application/json",
HttpHeaders.cookieHeader: "access_token=$accessToken"
});
print("status code tags : ${response.statusCode}");
if (response.statusCode == 200) {
final data = json.decode(utf8.decode(response.bodyBytes));
print("tags ${data}");
setState(() {
suggestionsTags = (data as List)
.map((feature) => {'name': feature['name']})
.toList();
print("suggesttion tag : ${suggestionsTags}");
if (suggestionsTags.isNotEmpty) {
showInputGeo = false;
showInputSearch = false;
@@ -422,16 +484,17 @@ class _MyHomePageState extends State<ListItemMenu> {
if (response.statusCode == 200) {
final List<dynamic> body = json.decode(utf8.decode(response.bodyBytes));
print("results fetch : ${body}");
print("fetch count : ${_fetchCount}");
// Update state after getting the response
setState(() {
if (body.isNotEmpty) {
// If we have results, map them to Events
filteredPosts = body
.map((e) => Events.fromJson(e as Map<String, dynamic>))
.toList();
} else {
// If no results, clear filteredPosts
filteredPosts.clear();
int counter = filteredPosts.length;
// If we have results, map them to Events
filteredPosts = body
.map((e) => Events.fromJson(e as Map<String, dynamic>))
.toList();
if (counter == filteredPosts.length) {
_fetchCount--;
}
});
} else {
@@ -461,10 +524,10 @@ class _MyHomePageState extends State<ListItemMenu> {
Padding _buildDateField(String position) {
TextEditingController datePicker = startDatepicker;
String hintText = "Date de début";
String hintText = AppLocalizations.of(context)?.start_date ?? "Start date";
if (position == "end") {
datePicker = endDatepicker;
hintText = "Date de fin";
hintText = AppLocalizations.of(context)?.end_date ?? "End date";
}
return Padding(
padding: const EdgeInsets.all(8.0),
@@ -549,9 +612,11 @@ class _MyHomePageState extends State<ListItemMenu> {
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
final localeProvider = Provider.of<LocaleProvider>(context);
return Scaffold(
appBar: AppBar(
title: const Text("Item list menu"),
title: Text(loc?.menu_list ?? "Item list menu"),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
@@ -568,8 +633,9 @@ class _MyHomePageState extends State<ListItemMenu> {
if (showInputSearch)
_buildSearchField(
controller: inputItem,
labelText: 'Search by item',
labelText: loc?.search_item ?? "Search by item",
onChanged: (value) {
_fetchCount = 0;
if (value.isNotEmpty) {
setState(() {
itemName = value;
@@ -587,6 +653,7 @@ class _MyHomePageState extends State<ListItemMenu> {
}
},
onClear: () {
_fetchCount = 0;
setState(() {
inputItem.clear();
itemName = '';
@@ -598,6 +665,7 @@ class _MyHomePageState extends State<ListItemMenu> {
},
suggestions: suggestionsItem,
onSuggestionTap: (suggestion) async {
_fetchCount = 0;
setState(() {
itemName = suggestion['name'];
inputItem.text = itemName;
@@ -611,8 +679,9 @@ class _MyHomePageState extends State<ListItemMenu> {
if ((showDateFields) && (showInputTag))
_buildSearchField(
controller: inputTags,
labelText: 'Search by tags',
labelText: loc?.search_tag ?? "Search by tags",
onChanged: (value) {
_fetchCount = 0;
if (value.isNotEmpty) {
setState(() {
itemTags = value;
@@ -630,6 +699,7 @@ class _MyHomePageState extends State<ListItemMenu> {
}
},
onClear: () {
_fetchCount = 0;
setState(() {
inputTags.clear();
});
@@ -637,6 +707,8 @@ class _MyHomePageState extends State<ListItemMenu> {
},
suggestions: suggestionsTags,
onSuggestionTap: (suggestion) async {
_fetchCount = 0;
setState(() {
itemTags = suggestion['name'];
inputTags.text = itemTags;
@@ -658,8 +730,11 @@ class _MyHomePageState extends State<ListItemMenu> {
if ((showDateFields) && (showInputGeo))
_buildSearchField(
controller: inputGeo,
labelText: 'Search by geographical zone',
labelText:
loc?.search_geographical ?? 'Search by geographical zone',
onChanged: (value) async {
_fetchCount = 0;
if (value.isNotEmpty) {
setState(() {
geographicalZone = value;
@@ -681,6 +756,7 @@ class _MyHomePageState extends State<ListItemMenu> {
}
},
onClear: () async {
_fetchCount = 0;
final prefs = await SharedPreferences.getInstance();
prefs.remove("city_lat");
prefs.remove("city_long");
@@ -696,6 +772,7 @@ class _MyHomePageState extends State<ListItemMenu> {
},
suggestions: suggestionsGeo,
onSuggestionTap: (suggestion) async {
_fetchCount = 0;
final latitude = suggestion['geometry']['coordinates'][1];
final longitude = suggestion['geometry']['coordinates'][0];
setState(() {
@@ -725,21 +802,26 @@ class _MyHomePageState extends State<ListItemMenu> {
: Icons.keyboard_arrow_down,
color: Colors.blue,
),
tooltip: showDateFields ? 'Show Date Fields' : 'Hide Date Fields',
tooltip: showDateFields
? loc?.show_date_field ?? 'Show Date Fields'
: loc?.hide_date_field ?? 'Hide Date Fields',
),
Expanded(
child: FutureBuilder<List<Events>>(
future: postsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasData) {
final posts = snapshot.data!;
final displayedPosts =
filteredPosts.isEmpty ? posts : filteredPosts;
return buildPosts(displayedPosts);
} else {
return const Text("No data available");
return Center(
child: Text(AppLocalizations.of(context)?.no_data ??
"No data available"),
);
}
},
),
@@ -749,7 +831,7 @@ class _MyHomePageState extends State<ListItemMenu> {
floatingActionButton: FloatingActionButton(
onPressed: popCamera,
backgroundColor: Colors.blue,
tooltip: 'Recherche',
tooltip: loc?.search ?? 'Recherche',
child: const Icon(Icons.photo_camera, color: Colors.white),
),
);
@@ -757,28 +839,41 @@ class _MyHomePageState extends State<ListItemMenu> {
// Function to display fetched data on screen
Widget buildPosts(List<Events> posts) {
print("posts : ${posts}");
print("filteredposts : ${filteredPosts}");
final displayedPosts = filteredPosts;
print("results ${displayedPosts}");
// If filteredPosts is empty, show a message saying no data is available
if (displayedPosts.isEmpty) {
return const Center(
child: Text('No events available for this location.',
return Center(
child: Text(
AppLocalizations.of(context)?.no_events ??
'No events available for this location.',
style: TextStyle(fontSize: 18, color: Colors.grey)),
);
}
return ListView.separated(
itemCount: displayedPosts.length,
controller: _scrollController,
itemCount: displayedPosts.isNotEmpty
? displayedPosts.length +
(_isLoading ? 1 : 0) // Add 1 only if loading
: 0,
itemBuilder: (context, index) {
if (index >= displayedPosts.length) {
return _isLoading
? Center(child: CircularProgressIndicator())
: SizedBox.shrink();
}
final post = displayedPosts[index];
final startDate = DateTime.parse(post.startDate!);
final date = DateFormat.yMd().format(startDate);
final time = DateFormat.Hm().format(startDate);
//final date = DateFormat.yMd().format(startDate);
//final time = DateFormat.Hm().format(startDate);
final locale =
Provider.of<LocaleProvider>(context).locale?.toString() ??
'en_US';
final dateLongue =
DateFormat('EEEE d MMMM y', locale).format(startDate);
return ListTile(
title: Text('${post.name!}'),
subtitle: Text('${post.place!}\n${date} ${time}'),
subtitle: Text('${post.place!}\n${dateLongue}'),
onTap: () {
Navigator.push(
context,

View File

@@ -8,6 +8,11 @@ import '../pages/ForgotPassword.dart';
import '../classes/alert.dart';
import '../classes/ad_helper.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; //
class LoginDemo extends StatefulWidget {
@override
_LoginDemoState createState() => _LoginDemoState();
@@ -25,7 +30,8 @@ class _LoginDemoState extends State<LoginDemo> with ShowAlertDialog {
final password = inputPassword.text;
if (pseudo.isEmpty || password.isEmpty) {
showAlertDialog(context, "Erreur", "Champ vide");
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.empty_input ?? "Empty input");
return;
}
@@ -36,7 +42,8 @@ class _LoginDemoState extends State<LoginDemo> with ShowAlertDialog {
Navigator.push(
context, MaterialPageRoute(builder: (_) => ListItemMenu()));
} else {
showAlertDialog(context, "Erreur", "Échec de l'authentification");
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.failed_auth ?? "Authentication failed");
}
}
@@ -73,7 +80,7 @@ class _LoginDemoState extends State<LoginDemo> with ShowAlertDialog {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Login Page"),
title: Text(AppLocalizations.of(context)?.login_page ?? "Login Page"),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
@@ -93,8 +100,10 @@ class _LoginDemoState extends State<LoginDemo> with ShowAlertDialog {
controller: inputPseudo,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Pseudo',
hintText: 'Enter pseudo existant',
labelText: AppLocalizations.of(context)?.pseudo ?? 'Pseudo',
hintText:
AppLocalizations.of(context)?.enter_existing_pseudo ??
'Enter a existing pseudo',
),
),
),
@@ -105,13 +114,16 @@ class _LoginDemoState extends State<LoginDemo> with ShowAlertDialog {
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
hintText: 'Enter secure password',
labelText:
AppLocalizations.of(context)?.password ?? "Password",
hintText: AppLocalizations.of(context)?.enter_password ??
"Enter the password",
),
),
),
CheckboxListTile(
title: Text("Se souvenir de moi"),
title: Text(
AppLocalizations.of(context)?.remembr_me ?? "Remember me"),
value: _rememberMe,
onChanged: (newValue) {
setState(() {
@@ -124,7 +136,9 @@ class _LoginDemoState extends State<LoginDemo> with ShowAlertDialog {
Navigator.push(context,
MaterialPageRoute(builder: (_) => PasswordForgot()));
},
child: Text('Forgot Password',
child: Text(
AppLocalizations.of(context)?.forgot_password ??
'Forgot Password',
style: TextStyle(color: Colors.blue, fontSize: 15)),
),
Container(
@@ -134,13 +148,14 @@ class _LoginDemoState extends State<LoginDemo> with ShowAlertDialog {
color: Colors.blue, borderRadius: BorderRadius.circular(20)),
child: TextButton(
onPressed: () => _login(context),
child: Text('Login',
child: Text(AppLocalizations.of(context)?.sign_in ?? 'Sign in',
style: TextStyle(color: Colors.white, fontSize: 25)),
),
),
SizedBox(height: 130),
InkWell(
child: Text('New User? Create Account'),
child: Text(AppLocalizations.of(context)?.new_user ??
'New User? Create Account'),
onTap: () {
Navigator.push(
context, MaterialPageRoute(builder: (_) => AddProfile()));

View File

@@ -13,6 +13,11 @@ import '../variable/globals.dart' as globals;
import '../classes/MyDrawer.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; //
void main() async {
await dotenv.load(fileName: ".env"); // Load .env file
runApp(const MyApp());
@@ -70,7 +75,10 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? '';
if (mapboxAccessToken.isEmpty) {
showAlertDialog(
context, "Erreur Mapbox", "Mapbox Access Token is not available.");
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.map_token ??
"Map Access Token is not available.");
}
}
@@ -95,35 +103,29 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
_handleErrorResponse(responseGet.statusCode);
}
} else {
showAlertDialog(context, "Erreur serveur", "Invalid cache.");
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.invalid_cache ?? "Invalid cache.");
}
}
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.";
}
showAlertDialog(context, "Erreur serveur", text);
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);
}
Future<void> _getUserLocation() async {
@@ -131,7 +133,10 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
showAlertDialog(
context, "Erreur de service", "Location services are disabled.");
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.geo_disabled ??
"Location services are disabled.");
return;
}
@@ -139,15 +144,21 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
showAlertDialog(context, "Erreur de permission",
"Location permissions are denied.");
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.permission_denied ??
"Location permissions are denied.");
return;
}
}
if (permission == LocationPermission.deniedForever) {
showAlertDialog(context, "Erreur de permission",
"Location permissions are permanently denied. Enable them in settings.");
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(
@@ -158,17 +169,23 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
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.');
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.no_last_position ??
"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.');
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.no_last_position ??
"No last known position available");
}
}
if (position != null) {
@@ -180,7 +197,11 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
_initToken();
_getEventInfo();
} catch (e) {
showAlertDialog(context, "Erreur geo", "Failed to get user location: $e");
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.failed_location ??
"Failed to get user location");
}
}
@@ -200,16 +221,17 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
}).toList();
});
} else {
showAlertDialog(context, "Erreur serveur",
"Failed to fetch the route: ${response.statusCode}");
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.failed_fetch ??
"Failed to fetch the route");
}
}
// Called when the map is created
void _onStyleLoaded() async {
// Log the map controller and coordinates
print("Mapbox controller initialized: $mapController");
print("lat - long : $latitude - $longitude");
// Check if the mapController is really initialized
if (mapController != null) {
@@ -229,20 +251,30 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
);
// Debugging symbol options
print("Adding symbol with options: $symbolOptions");
// Add symbol to map
mapController!.addSymbol(symbolOptions);
} else {
print("Error: Invalid coordinates, cannot add symbol.");
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
print("Error when adding symbol: $e");
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.error_symbol ??
"Error when adding symbol.");
}
} else {
print(
"Error: MapboxMapController is null at the time of symbol addition");
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.error_symbol ??
"Error when adding symbol.");
}
}
@@ -253,8 +285,11 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
currentRouteLine = null;
}
if (!isUserPositionInitialized) {
showAlertDialog(context, "Erreur de position",
"User position is not yet initialized. Try again.");
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.position_not_init ??
"User position is not yet initialized. Try again.");
return;
}
@@ -293,8 +328,11 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
_zoomToFitRoute(routeCoordinates);
}
} else {
showAlertDialog(context, "Erreur de coordonée",
"Invalid coordinates or user position.");
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.invalid_coordinates ??
"Invalid coordinates or user position.");
}
}
@@ -348,7 +386,7 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
children: [
Icon(Icons.directions_walk, color: Colors.blue),
SizedBox(width: 8),
Text('Walking'),
Text(AppLocalizations.of(context)?.walking ?? 'Walking'),
],
),
),
@@ -358,7 +396,7 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
children: [
Icon(Icons.directions_bike, color: Colors.green),
SizedBox(width: 8),
Text('Cycling'),
Text(AppLocalizations.of(context)?.cycling ?? 'Cycling'),
],
),
),
@@ -368,7 +406,7 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
children: [
Icon(Icons.directions_car, color: Colors.red),
SizedBox(width: 8),
Text('Driving'),
Text(AppLocalizations.of(context)?.driving ?? 'Driving'),
],
),
),
@@ -403,7 +441,8 @@ class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
child: FloatingActionButton(
onPressed: _drawRouteAndMarkers,
child: Icon(Icons.directions),
tooltip: 'Get Directions and Markers',
tooltip: AppLocalizations.of(context)?.get_direction ??
'Get Directions and Markers',
),
),
],

View File

@@ -20,6 +20,11 @@ import '../classes/ad_helper.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import '../classes/auth_service.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import '../locale_provider.dart'; //
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await MobileAds.instance.initialize();
@@ -165,7 +170,8 @@ class _UpdateeventImageState extends State<UpdateeventImage>
var endDate = "${endDateFormat}T${endTimepicker.text.replaceAll('-', ':')}";
if (!startDateCompare.isAfter(dateNow)) {
showAlertDialog(context, "Erreur evenement", "Evenement non futur");
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.no_future_event ?? "No future event");
return;
}
@@ -173,7 +179,11 @@ class _UpdateeventImageState extends State<UpdateeventImage>
var accessToken = prefs.getString("access_token") ?? "";
if (accessToken.isEmpty) {
showAlertDialog(context, "Erreur token", "Token d'accès manquant");
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.missing_token ??
"Missing access token");
return;
}
@@ -188,14 +198,18 @@ class _UpdateeventImageState extends State<UpdateeventImage>
final searchboxResponse = await http.get(searchboxUrl);
if (searchboxResponse.statusCode != 200) {
showAlertDialog(context, "Erreur map",
"Erreur lors de la géocodage avec Searchbox");
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.geocoding_error ??
"Error when geocoding");
return;
}
final searchboxData = json.decode(searchboxResponse.body);
if (searchboxData['results'].isEmpty) {
showAlertDialog(context, "Erreur", "Lieu introuvable");
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.no_found_place ?? "No found place");
return;
}
@@ -240,7 +254,10 @@ class _UpdateeventImageState extends State<UpdateeventImage>
if (imgbbResponse.statusCode != 200) {
showAlertDialog(
context, "Erreur serveur", "Erreur lors de l'upload d'image");
context,
AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.upload_error ??
"Error when image uploading");
return;
}
@@ -271,27 +288,37 @@ class _UpdateeventImageState extends State<UpdateeventImage>
);
if (eventResponse.statusCode == 200 || eventResponse.statusCode == 201) {
showEventDialog(context, "Événement $name ajouté");
String event_message =
AppLocalizations.of(context)?.event_added ?? "Event added";
showEventDialog(context, "$event_message : $name");
} else {
handleHttpError(eventResponse.statusCode, context);
}
} catch (e) {
showAlertDialog(context, "Erreur", "Erreur: ${e.toString()}");
showAlertDialog(context, AppLocalizations.of(context)?.error ?? "Error",
AppLocalizations.of(context)?.app_error ?? "Error application");
}
}
// Utility function to handle HTTP errors
void handleHttpError(int statusCode, BuildContext context) {
final errorMessages = {
400: "Requête mal construite",
403: "Utilisateur désactivé",
404: "Utilisateur inconnu",
406: "Mot de passe incorrect",
410: "Token invalide",
500: "Problème interne du serveur",
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"
};
showAlertDialog(context, "Erreur serveur",
errorMessages[statusCode] ?? "Erreur inconnue");
showAlertDialog(
context,
AppLocalizations.of(context)?.error ?? "Error",
messages[statusCode] ??
AppLocalizations.of(context)?.unknown_error ??
"Unknown error");
}
void start() async {
@@ -330,7 +357,9 @@ class _UpdateeventImageState extends State<UpdateeventImage>
final _formKey = GlobalKey<FormState>();
String? _validateField(String? value) {
return value!.isEmpty ? 'Champ requis' : null;
return value!.isEmpty
? AppLocalizations.of(context)?.required_input ?? 'Required input'
: null;
}
Future<void> searchSuggestions(String input) async {
@@ -344,11 +373,9 @@ class _UpdateeventImageState extends State<UpdateeventImage>
// Perform the request
final response = await http.get(searchboxUrl);
print("response code suggestion: ${response.statusCode}");
if (response.statusCode == 200) {
final data = json.decode(response.body);
print("data suggestion: ${data}");
setState(() {
// Map the results to extract name and full_address
@@ -374,7 +401,7 @@ class _UpdateeventImageState extends State<UpdateeventImage>
TextField(
controller: inputGeo,
decoration: InputDecoration(
labelText: 'Lieu',
labelText: AppLocalizations.of(context)?.location ?? 'Location',
border: OutlineInputBorder(),
suffixIcon: IconButton(
icon: const Icon(Icons.clear),
@@ -432,7 +459,8 @@ class _UpdateeventImageState extends State<UpdateeventImage>
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: Text("Add or Update a event"),
title: Text(AppLocalizations.of(context)?.add_event ??
"Add or Update a event"),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
@@ -467,8 +495,10 @@ class _UpdateeventImageState extends State<UpdateeventImage>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Nom',
hintText: 'Modifier le nom de l\'évènement'),
labelText: AppLocalizations.of(context)?.name ?? "Name",
hintText:
AppLocalizations.of(context)?.edit_event_name ??
"Edit event name"),
),
),
_buildGeographicalZoneSearchField(),
@@ -482,8 +512,10 @@ class _UpdateeventImageState extends State<UpdateeventImage>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Date de debut',
hintText: 'Cliquez ici pour selectionner une date'),
labelText: AppLocalizations.of(context)?.start_date ??
"Start date",
hintText: AppLocalizations.of(context)?.select_date ??
"Click to select a date"),
onTap: () => onTapFunctionDatePicker(
context: context, position: "start")),
),
@@ -497,8 +529,10 @@ class _UpdateeventImageState extends State<UpdateeventImage>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Heure de debut',
hintText: 'Cliquez ici pour selectionner une heure'),
labelText: AppLocalizations.of(context)?.start_time ??
"Start time",
hintText: AppLocalizations.of(context)?.select_time ??
"Click to select a time"),
onTap: () => onTapFunctionTimePicker(
context: context, position: "start")),
),
@@ -512,8 +546,10 @@ class _UpdateeventImageState extends State<UpdateeventImage>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Date de fin',
hintText: 'Cliquez ici pour selectionner une date'),
labelText: AppLocalizations.of(context)?.end_date ??
"End date",
hintText: AppLocalizations.of(context)?.select_date ??
"Click to select a date"),
onTap: () => onTapFunctionDatePicker(
context: context, position: "end")),
),
@@ -527,8 +563,10 @@ class _UpdateeventImageState extends State<UpdateeventImage>
validator: (value) => _validateField(value),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Heure de fin',
hintText: 'Cliquez ici pour selectionner une heure'),
labelText: AppLocalizations.of(context)?.end_time ??
"End time",
hintText: AppLocalizations.of(context)?.select_time ??
"Click to select a time"),
onTap: () => onTapFunctionTimePicker(
context: context, position: "end")),
),
@@ -538,7 +576,8 @@ class _UpdateeventImageState extends State<UpdateeventImage>
textSeparators: const [' ', ','],
validator: (String tag) {
if (_stringTagController.getTags!.contains(tag)) {
return 'Tu as deja rentre ce tag';
return AppLocalizations.of(context)?.already_tag ??
'You have already entered this tag';
}
return null;
},
@@ -553,10 +592,12 @@ class _UpdateeventImageState extends State<UpdateeventImage>
onSubmitted: inputFieldValues.onTagSubmitted,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Tags',
labelText:
AppLocalizations.of(context)?.tag ?? 'Tags',
hintText: inputFieldValues.tags.isNotEmpty
? ''
: "Enter tag...",
: AppLocalizations.of(context)?.enter_tag ??
"Enter tag...",
errorText: inputFieldValues.error,
prefixIcon: inputFieldValues.tags.isNotEmpty
? SingleChildScrollView(
@@ -633,7 +674,9 @@ class _UpdateeventImageState extends State<UpdateeventImage>
textSeparators: const [','],
validator: (String tag) {
if (_stringOrgaController.getTags!.contains(tag)) {
return 'Cet organisateur est déjà rentré';
return AppLocalizations.of(context)
?.already_organiser ??
'You have already entered this organizer';
}
return null;
},
@@ -648,10 +691,14 @@ class _UpdateeventImageState extends State<UpdateeventImage>
onSubmitted: inputFieldValues.onTagSubmitted,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Organisateurs',
labelText:
AppLocalizations.of(context)?.organizer ??
'Organizers',
hintText: inputFieldValues.tags.isNotEmpty
? ''
: "Enter un organisateur...",
: AppLocalizations.of(context)
?.enter_organizer ??
"Enter un organisateur...",
errorText: inputFieldValues.error,
prefixIcon: inputFieldValues.tags.isNotEmpty
? SingleChildScrollView(
@@ -732,8 +779,11 @@ class _UpdateeventImageState extends State<UpdateeventImage>
maxLines: 10,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Description',
hintText: 'Décrire l\'evènement'),
labelText: AppLocalizations.of(context)?.description ??
'Description',
hintText:
AppLocalizations.of(context)?.describe_event ??
'Describe the event'),
),
),
SizedBox(
@@ -752,7 +802,7 @@ class _UpdateeventImageState extends State<UpdateeventImage>
}
},
child: Text(
'Ajouter',
AppLocalizations.of(context)?.add_event ?? 'Add',
style: TextStyle(color: Colors.white, fontSize: 25),
),
),

View File

@@ -9,6 +9,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.6.1"
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.7.0"
asn1lib:
dependency: transitive
description:
name: asn1lib
sha256: "4bae5ae63e6d6dd17c4aac8086f3dec26c0236f6a0f03416c6c19d830c367cf5"
url: "https://pub.dev"
source: hosted
version: "1.5.8"
async:
dependency: transitive
description:
@@ -89,6 +105,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.18.0"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
cross_file:
dependency: transitive
description:
@@ -137,6 +161,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
encrypt:
dependency: transitive
description:
name: encrypt
sha256: "62d9aa4670cc2a8798bab89b39fc71b6dfbacf615de6cf5001fb39f7e4a996a2"
url: "https://pub.dev"
source: hosted
version: "5.0.3"
encrypt_shared_preferences:
dependency: "direct main"
description:
name: encrypt_shared_preferences
sha256: "35cd218e5e9d12fe4a63a545f46f2144d861909e4c4f2c4606fc75ffb53d9a46"
url: "https://pub.dev"
source: hosted
version: "0.8.8"
fake_async:
dependency: transitive
description:
@@ -230,6 +270,11 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.0"
flutter_localizations:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@@ -520,6 +565,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.6"
nested:
dependency: transitive
description:
name: nested
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
path:
dependency: "direct main"
description:
@@ -648,6 +701,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe"
url: "https://pub.dev"
source: hosted
version: "3.9.1"
provider:
dependency: "direct main"
description:
name: provider
sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84"
url: "https://pub.dev"
source: hosted
version: "6.1.5"
shared_preferences:
dependency: "direct main"
description:

View File

@@ -29,6 +29,9 @@ environment:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
@@ -51,6 +54,8 @@ dependencies:
url_launcher: ^6.3.1
mapbox_gl: ^0.16.0
google_mobile_ads: ^5.3.1
encrypt_shared_preferences: ^0.8.8
provider: ^6.1.2 # ou la dernière version
dev_dependencies:
flutter_test:
@@ -68,6 +73,7 @@ dev_dependencies:
# The following section is specific to Flutter packages.
flutter:
generate: true
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in