Add launch configuration, update API keys, and refactor UI components for better structure and performance

This commit is contained in:
Dayron
2025-10-06 14:17:30 +02:00
parent 29141ba8b2
commit 797f77cf69
16 changed files with 371 additions and 351 deletions

31
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,31 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Flutter",
"type": "dart",
"request": "launch",
"program": "lib/main.dart"
},
{
"name": "travel_mate",
"request": "launch",
"type": "dart"
},
{
"name": "travel_mate (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "travel_mate (release mode)",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
}

View File

@@ -35,7 +35,7 @@
android:value="2" /> android:value="2" />
<meta-data <meta-data
android:name="com.google.android.geo.API_KEY" android:name="com.google.android.geo.API_KEY"
android:value="AIzaSyAAQd5UqtPc0fsbHnuezm22r9b5dgzv21Y"/> android:value="AIzaSyCAtz1_d5K0ANwxAA_T84iq7Ac_gsUs_oM"/>
</application> </application>
<!-- Required to query activities that can process text, see: <!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and https://developer.android.com/training/package-visibility and

View File

@@ -8,7 +8,7 @@ import GoogleMaps
_ application: UIApplication, _ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool { ) -> Bool {
GMSServices.provideAPIKey("AIzaSyDoflpihvRWaQx18SKMu8_fgirF0muW4yU") GMSServices.provideAPIKey("AIzaSyBsgaG8PuDTFodn4xjGSjVDOxzw3gJB35A")
GeneratedPluginRegistrant.register(with: self) GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions) return super.application(application, didFinishLaunchingWithOptions: launchOptions)
} }

View File

@@ -5,9 +5,23 @@ class CountContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return const Center(
appBar: AppBar(title: const Text('Count Page')), child: Column(
body: const Center(child: Text('This is the Count Page')), mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.account_balance_wallet, size: 64, color: Colors.green),
SizedBox(height: 16),
Text(
'Comptes et Budget',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
'Gérez vos dépenses de voyage',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
],
),
); );
} }
} }

View File

@@ -5,9 +5,23 @@ class GroupContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return const Center(
appBar: AppBar(title: const Text('Chat Group Page')), child: Column(
body: const Center(child: Text('This is the Chat Group Page')), mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.group, size: 64, color: Colors.blue),
SizedBox(height: 16),
Text(
'Chat de Groupe',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
'Communiquez avec vos compagnons de voyage',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
],
),
); );
} }
} }

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../providers/user_provider.dart';
class CreateTripContent extends StatefulWidget { class CreateTripContent extends StatefulWidget {
const CreateTripContent({super.key}); const CreateTripContent({super.key});
@@ -350,11 +349,13 @@ class _CreateTripContentState extends State<CreateTripContent> {
Future<void> _selectEndDate(BuildContext context) async { Future<void> _selectEndDate(BuildContext context) async {
if (_startDate == null) { if (_startDate == null) {
ScaffoldMessenger.of(context).showSnackBar( if (mounted) {
SnackBar( ScaffoldMessenger.of(context).showSnackBar(
content: Text('Veuillez d\'abord sélectionner la date de début'), SnackBar(
), content: Text('Veuillez d\'abord sélectionner la date de début'),
); ),
);
}
return; return;
} }
@@ -364,7 +365,7 @@ class _CreateTripContentState extends State<CreateTripContent> {
firstDate: _startDate!, firstDate: _startDate!,
lastDate: DateTime.now().add(Duration(days: 365 * 2)), lastDate: DateTime.now().add(Duration(days: 365 * 2)),
); );
if (picked != null) { if (picked != null && mounted) {
setState(() { setState(() {
_endDate = picked; _endDate = picked;
}); });
@@ -378,17 +379,21 @@ class _CreateTripContentState extends State<CreateTripContent> {
// Validation email simple // Validation email simple
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegex.hasMatch(email)) { if (!emailRegex.hasMatch(email)) {
ScaffoldMessenger.of( if (mounted) {
context, ScaffoldMessenger.of(context).showSnackBar(
).showSnackBar(SnackBar(content: Text('Email invalide'))); SnackBar(content: Text('Email invalide'))
);
}
return; return;
} }
// Vérifier si l'email existe déjà // Vérifier si l'email existe déjà
if (_participants.contains(email)) { if (_participants.contains(email)) {
ScaffoldMessenger.of( if (mounted) {
context, ScaffoldMessenger.of(context).showSnackBar(
).showSnackBar(SnackBar(content: Text('Ce participant est déjà ajouté'))); SnackBar(content: Text('Ce participant est déjà ajouté'))
);
}
return; return;
} }
@@ -410,9 +415,11 @@ class _CreateTripContentState extends State<CreateTripContent> {
} }
if (_startDate == null || _endDate == null) { if (_startDate == null || _endDate == null) {
ScaffoldMessenger.of(context).showSnackBar( if (mounted) {
SnackBar(content: Text('Veuillez sélectionner les dates')), ScaffoldMessenger.of(context).showSnackBar(
); SnackBar(content: Text('Veuillez sélectionner les dates')),
);
}
return; return;
} }
@@ -424,25 +431,31 @@ class _CreateTripContentState extends State<CreateTripContent> {
// TODO: Implémenter la sauvegarde du voyage // TODO: Implémenter la sauvegarde du voyage
await Future.delayed(Duration(seconds: 2)); // Simulation await Future.delayed(Duration(seconds: 2)); // Simulation
ScaffoldMessenger.of(context).showSnackBar( if (mounted) {
SnackBar( ScaffoldMessenger.of(context).showSnackBar(
content: Text('Voyage créé avec succès !'), SnackBar(
backgroundColor: Colors.green, content: Text('Voyage créé avec succès !'),
), backgroundColor: Colors.green,
); ),
);
Navigator.pop(context); Navigator.pop(context);
}
} catch (e) { } catch (e) {
ScaffoldMessenger.of(context).showSnackBar( if (mounted) {
SnackBar( ScaffoldMessenger.of(context).showSnackBar(
content: Text('Erreur lors de la création: $e'), SnackBar(
backgroundColor: Colors.red, content: Text('Erreur lors de la création: $e'),
), backgroundColor: Colors.red,
); ),
);
}
} finally { } finally {
setState(() { if (mounted) {
_isLoading = false; setState(() {
}); _isLoading = false;
});
}
} }
} }
} }

View File

@@ -68,7 +68,7 @@ class HomeContent extends StatelessWidget {
), ),
// Espacement en bas pour éviter que le FAB cache le contenu // Espacement en bas pour éviter que le FAB cache le contenu
SizedBox(height: 80), const SizedBox(height: 80),
], ],
), ),
); );
@@ -82,7 +82,7 @@ class HomeContent extends StatelessWidget {
}, },
backgroundColor: Theme.of(context).colorScheme.primary, backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Colors.white, foregroundColor: Colors.white,
child: Icon(Icons.add), child: const Icon(Icons.add),
), ),
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
); );
@@ -100,7 +100,7 @@ class HomeContent extends StatelessWidget {
}) { }) {
return Card( return Card(
elevation: 4, elevation: 4,
margin: EdgeInsets.only(bottom: 16), margin: const EdgeInsets.only(bottom: 16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: InkWell( child: InkWell(
onTap: () { onTap: () {
@@ -115,7 +115,7 @@ class HomeContent extends StatelessWidget {
Container( Container(
height: 150, height: 150,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.vertical(top: Radius.circular(12)), borderRadius: const BorderRadius.vertical(top: Radius.circular(12)),
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
@@ -131,7 +131,7 @@ class HomeContent extends StatelessWidget {
Container( Container(
width: double.infinity, width: double.infinity,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.vertical( borderRadius: const BorderRadius.vertical(
top: Radius.circular(12), top: Radius.circular(12),
), ),
color: color.withValues(alpha: 0.3), color: color.withValues(alpha: 0.3),
@@ -148,24 +148,24 @@ class HomeContent extends StatelessWidget {
children: [ children: [
Text( Text(
title, title,
style: TextStyle( style: const TextStyle(
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Colors.white, color: Colors.white,
), ),
), ),
SizedBox(height: 4), const SizedBox(height: 4),
Row( Row(
children: [ children: [
Icon( const Icon(
Icons.location_on, Icons.location_on,
color: Colors.white, color: Colors.white,
size: 16, size: 16,
), ),
SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
location, location,
style: TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
color: Colors.white, color: Colors.white,
), ),

View File

@@ -1,8 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
class MapContent extends StatefulWidget { class MapContent extends StatefulWidget {
const MapContent({super.key}); const MapContent({super.key});
@@ -12,130 +9,13 @@ class MapContent extends StatefulWidget {
} }
class _MapContentState extends State<MapContent> { class _MapContentState extends State<MapContent> {
Location locationController = Location(); final LatLng _initialPosition = LatLng(48.8566, 2.3522); // Paris
final Completer<GoogleMapController> _mapController =
Completer<GoogleMapController>();
LatLng? currentPosition;
@override
void initState() {
super.initState();
//getLocationUpdate();
currentPosition = LatLng(48.8566, 2.3522); // Position par défaut (Paris)
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return GoogleMap(initialCameraPosition: CameraPosition(
body: SafeArea( target: _initialPosition,
child: Padding( zoom: 12,
padding: EdgeInsets.all(8.0), ));
child: Column(
children: [
TextField(
decoration: InputDecoration(
hintText: 'Rechercher un lieu...',
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
),
SizedBox(height: 8.0),
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
),
onPressed: () {
// TODO : Logique de recherche à implémenter
},
child: Text('Chercher'),
),
),
SizedBox(height: 8.0),
Expanded(
child: currentPosition == null
? const Center(
child: CircularProgressIndicator(),
)
: GoogleMap(
onMapCreated: (GoogleMapController controller) =>
_mapController.complete(controller),
initialCameraPosition: CameraPosition(
target: currentPosition!,
zoom: 14.4746,
),
myLocationEnabled: true,
myLocationButtonEnabled: true,
mapType: MapType.normal,
zoomControlsEnabled: true,
markers: {
Marker(
markerId: MarkerId('currentLocation'),
position: currentPosition!,
icon: BitmapDescriptor.defaultMarkerWithHue(
BitmapDescriptor.hueAzure,
),
),
},
),
),
],
),
),
),
);
} }
}
Future<void> _cameraToPosition(LatLng pos) async {
final GoogleMapController controller = await _mapController.future;
CameraPosition newCameraPosition = CameraPosition(target: pos, zoom: 13);
await controller.animateCamera(
CameraUpdate.newCameraPosition(newCameraPosition),
);
}
Future<void> getLocationUpdate() async {
bool serviceEnabled;
PermissionStatus permissionGranted;
serviceEnabled = await locationController.serviceEnabled();
if (serviceEnabled) {
serviceEnabled = await locationController.requestService();
} else {
return;
}
permissionGranted = await locationController.hasPermission();
if (permissionGranted == PermissionStatus.denied) {
permissionGranted = await locationController.requestPermission();
if (permissionGranted != PermissionStatus.granted) {
return;
}
}
locationController.onLocationChanged.listen((LocationData currentLocation) {
if (currentLocation.latitude != null &&
currentLocation.longitude != null) {
setState(() {
currentPosition = LatLng(
currentLocation.latitude!,
currentLocation.longitude!,
);
_cameraToPosition(currentPosition!);
print(currentPosition);
});
}
});
}
}

View File

@@ -8,18 +8,18 @@ class SettingsContent extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView( return ListView(
padding: EdgeInsets.all(16), padding: const EdgeInsets.all(16),
children: [ children: [
Text( const Text(
'Paramètres', 'Paramètres',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
), ),
SizedBox(height: 20), const SizedBox(height: 20),
// Section Profil intégrée // Section Profil intégrée
ProfileContent(), const ProfileContent(),
SizedBox(height: 20), const SizedBox(height: 20),
// Autres paramètres // Autres paramètres
Text( Text(
@@ -30,39 +30,39 @@ class SettingsContent extends StatelessWidget {
color: Colors.grey[600], color: Colors.grey[600],
), ),
), ),
SizedBox(height: 8), const SizedBox(height: 8),
ListTile( ListTile(
leading: Icon(Icons.palette), leading: const Icon(Icons.palette),
title: Text('Thème'), title: const Text('Thème'),
subtitle: Text('Clair, sombre ou système'), subtitle: const Text('Clair, sombre ou système'),
trailing: Icon(Icons.arrow_forward_ios), trailing: const Icon(Icons.arrow_forward_ios),
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute(builder: (context) => SettingsThemeContent()), MaterialPageRoute(builder: (context) => const SettingsThemeContent()),
); );
}, },
), ),
ListTile( ListTile(
leading: Icon(Icons.notifications), leading: const Icon(Icons.notifications),
title: Text('Notifications'), title: const Text('Notifications'),
trailing: Icon(Icons.arrow_forward_ios), trailing: const Icon(Icons.arrow_forward_ios),
onTap: () {}, onTap: () {},
), ),
ListTile( ListTile(
leading: Icon(Icons.language), leading: const Icon(Icons.language),
title: Text('Langue'), title: const Text('Langue'),
trailing: Icon(Icons.arrow_forward_ios), trailing: const Icon(Icons.arrow_forward_ios),
onTap: () {}, onTap: () {},
), ),
ListTile( ListTile(
leading: Icon(Icons.privacy_tip), leading: const Icon(Icons.privacy_tip),
title: Text('Confidentialité'), title: const Text('Confidentialité'),
trailing: Icon(Icons.arrow_forward_ios), trailing: const Icon(Icons.arrow_forward_ios),
onTap: () {}, onTap: () {},
), ),
], ],

View File

@@ -9,68 +9,93 @@ class SettingsThemeContent extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text('Thème'), title: const Text('Thème'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
), ),
body: Consumer<ThemeProvider>( body: Consumer<ThemeProvider>(
builder: (context, themeProvider, child) { builder: (context, themeProvider, child) {
return ListView( return ListView(
padding: EdgeInsets.all(16), padding: const EdgeInsets.all(16),
children: [ children: [
Text( Text(
'Choisir le thème', 'Choisir le thème',
style: Theme.of(context).textTheme.headlineSmall, style: Theme.of(context).textTheme.headlineSmall,
), ),
SizedBox(height: 20), const SizedBox(height: 20),
// Option Système // Option Système
RadioListTile<ThemeMode>( Card(
title: Text('Système'), child: ListTile(
subtitle: Text('Suit les paramètres de votre appareil'), leading: Icon(
value: ThemeMode.system, themeProvider.themeMode == ThemeMode.system
groupValue: themeProvider.themeMode, ? Icons.radio_button_checked
onChanged: (ThemeMode? value) { : Icons.radio_button_unchecked,
if (value != null) { color: themeProvider.themeMode == ThemeMode.system
themeProvider.setThemeMode(value); ? Theme.of(context).colorScheme.primary
} : null,
}, ),
secondary: Icon(Icons.brightness_auto), title: const Text('Système'),
subtitle: const Text('Suit les paramètres de votre appareil'),
trailing: const Icon(Icons.brightness_auto),
selected: themeProvider.themeMode == ThemeMode.system,
onTap: () {
themeProvider.setThemeMode(ThemeMode.system);
},
),
), ),
const SizedBox(height: 8),
// Option Clair // Option Clair
RadioListTile<ThemeMode>( Card(
title: Text('Clair'), child: ListTile(
subtitle: Text('Thème clair en permanence'), leading: Icon(
value: ThemeMode.light, themeProvider.themeMode == ThemeMode.light
groupValue: themeProvider.themeMode, ? Icons.radio_button_checked
onChanged: (ThemeMode? value) { : Icons.radio_button_unchecked,
if (value != null) { color: themeProvider.themeMode == ThemeMode.light
themeProvider.setThemeMode(value); ? Theme.of(context).colorScheme.primary
} : null,
}, ),
secondary: Icon(Icons.light_mode), title: const Text('Clair'),
subtitle: const Text('Thème clair en permanence'),
trailing: const Icon(Icons.light_mode),
selected: themeProvider.themeMode == ThemeMode.light,
onTap: () {
themeProvider.setThemeMode(ThemeMode.light);
},
),
), ),
const SizedBox(height: 8),
// Option Sombre // Option Sombre
RadioListTile<ThemeMode>( Card(
title: Text('Sombre'), child: ListTile(
subtitle: Text('Thème sombre en permanence'), leading: Icon(
value: ThemeMode.dark, themeProvider.themeMode == ThemeMode.dark
groupValue: themeProvider.themeMode, ? Icons.radio_button_checked
onChanged: (ThemeMode? value) { : Icons.radio_button_unchecked,
if (value != null) { color: themeProvider.themeMode == ThemeMode.dark
themeProvider.setThemeMode(value); ? Theme.of(context).colorScheme.primary
} : null,
}, ),
secondary: Icon(Icons.dark_mode), title: const Text('Sombre'),
subtitle: const Text('Thème sombre en permanence'),
trailing: const Icon(Icons.dark_mode),
selected: themeProvider.themeMode == ThemeMode.dark,
onTap: () {
themeProvider.setThemeMode(ThemeMode.dark);
},
),
), ),
SizedBox(height: 30), const SizedBox(height: 30),
// Aperçu du thème actuel // Aperçu du thème actuel
Card( Card(
child: Padding( child: Padding(
padding: EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -78,7 +103,7 @@ class SettingsThemeContent extends StatelessWidget {
'Aperçu', 'Aperçu',
style: Theme.of(context).textTheme.titleMedium, style: Theme.of(context).textTheme.titleMedium,
), ),
SizedBox(height: 10), const SizedBox(height: 10),
Row( Row(
children: [ children: [
Icon( Icon(
@@ -87,7 +112,7 @@ class SettingsThemeContent extends StatelessWidget {
: Icons.light_mode, : Icons.light_mode,
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
), ),
SizedBox(width: 10), const SizedBox(width: 10),
Text( Text(
themeProvider.isDarkMode themeProvider.isDarkMode
? 'Mode sombre actif' ? 'Mode sombre actif'

View File

@@ -14,29 +14,63 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> { class _HomePageState extends State<HomePage> {
int _currentIndex = 0; int _currentIndex = 0;
// Cache pour les pages créées
final Map<int, Widget> _pageCache = {};
final List<String> titles = [
'Mes voyages', // 0
'Paramètres', // 1
'Carte', // 2
'Chat de groupe', // 3
'Comptes', // 4
];
Widget _buildPage(int index) {
// Vérifier si la page est déjà en cache
if (_pageCache.containsKey(index)) {
return _pageCache[index]!;
}
// Créer la page seulement quand elle est demandée
Widget page;
switch (index) {
case 0:
page = const HomeContent();
break;
case 1:
page = const SettingsContent();
break;
case 2:
page = const MapContent();
break;
case 3:
page = const GroupContent();
break;
case 4:
page = const CountContent();
break;
default:
page = const HomeContent();
}
// Mettre en cache la page créée
_pageCache[index] = page;
return page;
}
void _onNavigationTap(int index) {
setState(() {
_currentIndex = index;
});
Navigator.pop(context); // Fermer le drawer
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// Définir les pages directement dans le build pour éviter les erreurs
final List<Widget> pages = [
HomeContent(), // 0
SettingsContent(), // 1
MapContent(), // 2
GroupContent(), // 3
CountContent(), // 4
];
final List<String> titles = [
'Mes voyages', // 0
'Paramètres', // 1
'Carte', // 2
'Chat de groupe', // 3
'Comptes', // 4
];
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(titles[_currentIndex]), // Debug title: Text(titles[_currentIndex]),
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
foregroundColor: Colors.white, foregroundColor: Colors.white,
), ),
@@ -48,70 +82,40 @@ class _HomePageState extends State<HomePage> {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.inversePrimary, color: Theme.of(context).colorScheme.inversePrimary,
), ),
child: Text( child: const Text(
"Travel Mate", "Travel Mate",
style: TextStyle(color: Colors.white, fontSize: 24), style: TextStyle(color: Colors.white, fontSize: 24),
), ),
), ),
ListTile( _buildDrawerItem(
leading: Icon(Icons.home), icon: Icons.home,
title: Text("Mes voyages"), title: "Mes voyages",
selected: _currentIndex == 0, index: 0,
onTap: () {
setState(() {
_currentIndex = 0;
});
Navigator.pop(context);
},
), ),
ListTile( _buildDrawerItem(
leading: Icon(Icons.settings), icon: Icons.settings,
title: Text("Paramètres"), title: "Paramètres",
selected: _currentIndex == 1, index: 1,
onTap: () {
setState(() {
_currentIndex = 1;
});
Navigator.pop(context);
},
), ),
ListTile( _buildDrawerItem(
leading: Icon(Icons.map), icon: Icons.map,
title: Text("Carte"), title: "Carte",
selected: _currentIndex == 2, index: 2,
onTap: () {
setState(() {
_currentIndex = 2;
});
Navigator.pop(context);
},
), ),
ListTile( _buildDrawerItem(
leading: Icon(Icons.group), icon: Icons.group,
title: Text("Chat de groupe"), title: "Chat de groupe",
selected: _currentIndex == 3, index: 3,
onTap: () {
setState(() {
_currentIndex = 3;
});
Navigator.pop(context);
},
), ),
ListTile( _buildDrawerItem(
leading: Icon(Icons.account_balance_wallet), icon: Icons.account_balance_wallet,
title: Text("Comptes"), title: "Comptes",
selected: _currentIndex == 4, index: 4,
onTap: () {
setState(() {
_currentIndex = 4;
});
Navigator.pop(context);
},
), ),
Divider(), const Divider(),
ListTile( ListTile(
leading: Icon(Icons.logout, color: Colors.red), leading: const Icon(Icons.logout, color: Colors.red),
title: Text("Déconnexion", style: TextStyle(color: Colors.red)), title: const Text("Déconnexion", style: TextStyle(color: Colors.red)),
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
Navigator.pushNamedAndRemoveUntil( Navigator.pushNamedAndRemoveUntil(
@@ -124,7 +128,38 @@ class _HomePageState extends State<HomePage> {
], ],
), ),
), ),
body: pages[_currentIndex], body: IndexedStack(
index: _currentIndex,
children: [
// Créer les pages seulement si elles sont sélectionnées
for (int i = 0; i < titles.length; i++)
if (_currentIndex == i || _pageCache.containsKey(i))
_buildPage(i)
else
Container(), // Placeholder vide
],
),
); );
} }
Widget _buildDrawerItem({
required IconData icon,
required String title,
required int index,
}) {
return ListTile(
leading: Icon(icon),
title: Text(title),
selected: _currentIndex == index,
selectedTileColor: Theme.of(context).colorScheme.primary.withValues(alpha: 0.1),
onTap: () => _onNavigationTap(index),
);
}
@override
void dispose() {
// Nettoyer le cache si nécessaire
_pageCache.clear();
super.dispose();
}
} }

View File

@@ -62,31 +62,39 @@ class _LoginPageState extends State<LoginPage> {
_passwordController.text, _passwordController.text,
); );
if (user != null) { if (mounted) {
// Naviguer vers la page d'accueil if (user != null) {
Provider.of<UserProvider>(context, listen: false).setCurrentUser(user); // Naviguer vers la page d'accueil
Navigator.pushReplacementNamed(context, '/home'); Provider.of<UserProvider>(context, listen: false).setCurrentUser(user);
} else { Navigator.pushReplacementNamed(context, '/home');
// Échec de la connexion } else {
_showErrorMessage('Email ou mot de passe incorrect'); // Échec de la connexion
_showErrorMessage('Email ou mot de passe incorrect');
}
} }
} catch (e) { } catch (e) {
_showErrorMessage('Erreur lors de la connexion: ${e.toString()}'); if (mounted) {
_showErrorMessage('Erreur lors de la connexion: ${e.toString()}');
}
} finally { } finally {
setState(() { if (mounted) {
_isLoading = false; setState(() {
}); _isLoading = false;
});
}
} }
} }
void _showErrorMessage(String message) { void _showErrorMessage(String message) {
ScaffoldMessenger.of(context).showSnackBar( if (mounted) {
SnackBar( ScaffoldMessenger.of(context).showSnackBar(
content: Text(message), SnackBar(
backgroundColor: Colors.red, content: Text(message),
duration: Duration(seconds: 3), backgroundColor: Colors.red,
), duration: const Duration(seconds: 3),
); ),
);
}
} }
@override @override

View File

@@ -8,7 +8,7 @@ class ThemeProvider extends ChangeNotifier {
bool get isDarkMode { bool get isDarkMode {
if (_themeMode == ThemeMode.system) { if (_themeMode == ThemeMode.system) {
return WidgetsBinding.instance.window.platformBrightness == return WidgetsBinding.instance.platformDispatcher.platformBrightness ==
Brightness.dark; Brightness.dark;
} }
return _themeMode == ThemeMode.dark; return _themeMode == ThemeMode.dark;

View File

@@ -16,7 +16,7 @@ class TripService {
final List<dynamic> jsonList = json.decode(tripsJson); final List<dynamic> jsonList = json.decode(tripsJson);
return jsonList.map((json) => Trip.fromMap(json)).toList(); return jsonList.map((json) => Trip.fromMap(json)).toList();
} catch (e) { } catch (e) {
print('Erreur lors du chargement des voyages: $e'); //print('Erreur lors du chargement des voyages: $e');
return []; return [];
} }
} }
@@ -28,7 +28,7 @@ class TripService {
final jsonList = trips.map((trip) => trip.toMap()).toList(); final jsonList = trips.map((trip) => trip.toMap()).toList();
await prefs.setString(_tripsKey, json.encode(jsonList)); await prefs.setString(_tripsKey, json.encode(jsonList));
} catch (e) { } catch (e) {
print('Erreur lors de la sauvegarde des voyages: $e'); //print('Erreur lors de la sauvegarde des voyages: $e');
throw Exception('Erreur de sauvegarde'); throw Exception('Erreur de sauvegarde');
} }
} }
@@ -49,7 +49,7 @@ class TripService {
await saveTrips(trips); await saveTrips(trips);
return true; return true;
} catch (e) { } catch (e) {
print('Erreur lors de l\'ajout du voyage: $e'); //print('Erreur lors de l\'ajout du voyage: $e');
return false; return false;
} }
} }
@@ -65,7 +65,7 @@ class TripService {
) )
.toList(); .toList();
} catch (e) { } catch (e) {
print('Erreur lors de la récupération des voyages: $e'); //print('Erreur lors de la récupération des voyages: $e');
return []; return [];
} }
} }
@@ -83,7 +83,7 @@ class TripService {
} }
return false; return false;
} catch (e) { } catch (e) {
print('Erreur lors de la mise à jour du voyage: $e'); //print('Erreur lors de la mise à jour du voyage: $e');
return false; return false;
} }
} }
@@ -101,7 +101,7 @@ class TripService {
} }
return false; return false;
} catch (e) { } catch (e) {
print('Erreur lors de la suppression du voyage: $e'); //print('Erreur lors de la suppression du voyage: $e');
return false; return false;
} }
} }

View File

@@ -26,7 +26,7 @@ class UserService {
return jsonList.map((json) => User.fromMap(json)).toList(); return jsonList.map((json) => User.fromMap(json)).toList();
} catch (e) { } catch (e) {
print('Erreur lors du chargement des utilisateurs: $e'); //print('Erreur lors du chargement des utilisateurs: $e');
return []; return [];
} }
} }
@@ -38,7 +38,7 @@ class UserService {
final jsonList = users.map((user) => user.toMap()).toList(); final jsonList = users.map((user) => user.toMap()).toList();
await file.writeAsString(json.encode(jsonList)); await file.writeAsString(json.encode(jsonList));
} catch (e) { } catch (e) {
print('Erreur lors de la sauvegarde des utilisateurs: $e'); //print('Erreur lors de la sauvegarde des utilisateurs: $e');
throw Exception('Erreur de sauvegarde'); throw Exception('Erreur de sauvegarde');
} }
} }
@@ -62,7 +62,7 @@ class UserService {
await saveUsers(users); await saveUsers(users);
return true; return true;
} catch (e) { } catch (e) {
print('Erreur lors de l\'ajout de l\'utilisateur: $e'); //print('Erreur lors de l\'ajout de l\'utilisateur: $e');
return false; return false;
} }
} }
@@ -89,7 +89,7 @@ class UserService {
return null; // Mot de passe incorrect return null; // Mot de passe incorrect
} catch (e) { } catch (e) {
print('Erreur lors de l\'authentification: $e'); //print('Erreur lors de l\'authentification: $e');
return null; return null;
} }
} }
@@ -102,7 +102,7 @@ class UserService {
(user) => user.email.toLowerCase() == email.toLowerCase(), (user) => user.email.toLowerCase() == email.toLowerCase(),
); );
} catch (e) { } catch (e) {
print('Erreur lors de la vérification de l\'email: $e'); //print('Erreur lors de la vérification de l\'email: $e');
return false; return false;
} }
} }
@@ -113,7 +113,7 @@ class UserService {
final users = await loadUsers(); final users = await loadUsers();
return users.firstWhere((user) => user.id == id); return users.firstWhere((user) => user.id == id);
} catch (e) { } catch (e) {
print('Utilisateur avec l\'ID $id non trouvé'); //print('Utilisateur avec l\'ID $id non trouvé');
return null; return null;
} }
} }
@@ -126,7 +126,7 @@ class UserService {
(user) => user.email.toLowerCase() == email.toLowerCase(), (user) => user.email.toLowerCase() == email.toLowerCase(),
); );
} catch (e) { } catch (e) {
print('Utilisateur avec l\'email $email non trouvé'); //print('Utilisateur avec l\'email $email non trouvé');
return null; return null;
} }
} }
@@ -144,7 +144,7 @@ class UserService {
} }
return false; // Utilisateur non trouvé return false; // Utilisateur non trouvé
} catch (e) { } catch (e) {
print('Erreur lors de la mise à jour de l\'utilisateur: $e'); //print('Erreur lors de la mise à jour de l\'utilisateur: $e');
return false; return false;
} }
} }
@@ -162,7 +162,7 @@ class UserService {
} }
return false; // Utilisateur non trouvé return false; // Utilisateur non trouvé
} catch (e) { } catch (e) {
print('Erreur lors de la suppression de l\'utilisateur: $e'); //print('Erreur lors de la suppression de l\'utilisateur: $e');
return false; return false;
} }
} }
@@ -195,7 +195,7 @@ class UserService {
return true; return true;
} catch (e) { } catch (e) {
print('Erreur lors du changement de mot de passe: $e'); //print('Erreur lors du changement de mot de passe: $e');
return false; return false;
} }
} }
@@ -219,7 +219,7 @@ class UserService {
return true; return true;
} catch (e) { } catch (e) {
print('Erreur lors de la réinitialisation du mot de passe: $e'); //print('Erreur lors de la réinitialisation du mot de passe: $e');
return false; return false;
} }
} }
@@ -230,7 +230,7 @@ class UserService {
final users = await loadUsers(); final users = await loadUsers();
return users.length; return users.length;
} catch (e) { } catch (e) {
print('Erreur lors du comptage des utilisateurs: $e'); //print('Erreur lors du comptage des utilisateurs: $e');
return 0; return 0;
} }
} }
@@ -240,7 +240,7 @@ class UserService {
try { try {
await saveUsers([]); await saveUsers([]);
} catch (e) { } catch (e) {
print('Erreur lors du vidage de la base de données: $e'); //print('Erreur lors du vidage de la base de données: $e');
} }
} }
@@ -266,7 +266,7 @@ class UserService {
await addUser(user); await addUser(user);
} }
} catch (e) { } catch (e) {
print('Erreur lors de la création des utilisateurs de test: $e'); //print('Erreur lors de la création des utilisateurs de test: $e');
} }
} }
} }

View File

@@ -39,7 +39,7 @@ dependencies:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
google_maps_flutter: ^2.13.1 google_maps_flutter: ^2.5.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@@ -66,7 +66,7 @@ flutter:
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
assets: assets:
- assets/icons/ - assets/icons/
- assets/images/ #- assets/images/
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images # https://flutter.dev/to/resolution-aware-images