refactoring
This commit is contained in:
@@ -1,8 +1,26 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
import 'package:afterwork/data/models/creator_model.dart';
|
||||
import 'package:afterwork/data/models/event_model.dart';
|
||||
import 'package:afterwork/data/models/participant_model.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:afterwork/data/providers/user_provider.dart';
|
||||
import 'package:afterwork/core/constants/urls.dart';
|
||||
import '../location/location_picker_screen.dart';
|
||||
|
||||
class AddEventDialog extends StatefulWidget {
|
||||
const AddEventDialog({super.key});
|
||||
final String userId;
|
||||
final String userName;
|
||||
final String userLastName;
|
||||
|
||||
const AddEventDialog({
|
||||
super.key,
|
||||
required this.userId,
|
||||
required this.userName,
|
||||
required this.userLastName,
|
||||
});
|
||||
|
||||
@override
|
||||
_AddEventDialogState createState() => _AddEventDialogState();
|
||||
@@ -14,10 +32,10 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
||||
String _description = '';
|
||||
DateTime? _selectedDate;
|
||||
String? _imagePath;
|
||||
String _location = '';
|
||||
String _location = 'Abidjan'; // Par défaut à Cocody, Abidjan
|
||||
String _category = '';
|
||||
String _link = '';
|
||||
LatLng? _selectedLatLng;
|
||||
LatLng? _selectedLatLng = const LatLng(5.348722, -3.985038); // Par défaut à Cocody, Abidjan
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -26,29 +44,31 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
||||
borderRadius: BorderRadius.circular(15.0),
|
||||
),
|
||||
backgroundColor: const Color(0xFF2C2C3E),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildTitleField(),
|
||||
const SizedBox(height: 10),
|
||||
_buildDescriptionField(),
|
||||
const SizedBox(height: 10),
|
||||
_buildDatePicker(),
|
||||
const SizedBox(height: 10),
|
||||
_buildLocationField(context),
|
||||
const SizedBox(height: 10),
|
||||
_buildCategoryField(),
|
||||
const SizedBox(height: 10),
|
||||
_buildImagePicker(),
|
||||
const SizedBox(height: 10),
|
||||
_buildLinkField(),
|
||||
const SizedBox(height: 20),
|
||||
_buildSubmitButton(),
|
||||
],
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildTitleField(),
|
||||
const SizedBox(height: 10),
|
||||
_buildDescriptionField(),
|
||||
const SizedBox(height: 10),
|
||||
_buildDatePicker(),
|
||||
const SizedBox(height: 10),
|
||||
_buildLocationField(context),
|
||||
const SizedBox(height: 10),
|
||||
_buildCategoryField(),
|
||||
const SizedBox(height: 10),
|
||||
_buildImagePicker(),
|
||||
const SizedBox(height: 10),
|
||||
_buildLinkField(),
|
||||
const SizedBox(height: 20),
|
||||
_buildSubmitButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -71,12 +91,14 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
||||
style: const TextStyle(color: Colors.white),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
print('Erreur: Titre est vide');
|
||||
return 'Veuillez entrer un titre';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) {
|
||||
_title = value ?? '';
|
||||
print('Titre sauvegardé: $_title');
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -98,12 +120,14 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
||||
maxLines: 3,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
print('Erreur: Description est vide');
|
||||
return 'Veuillez entrer une description';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) {
|
||||
_description = value ?? '';
|
||||
print('Description sauvegardée: $_description');
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -120,7 +144,10 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
||||
if (picked != null && picked != _selectedDate) {
|
||||
setState(() {
|
||||
_selectedDate = picked;
|
||||
print('Date sélectionnée: $_selectedDate');
|
||||
});
|
||||
} else {
|
||||
print('Date non sélectionnée ou égale à la précédente');
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
@@ -158,7 +185,10 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
||||
setState(() {
|
||||
_selectedLatLng = pickedLocation;
|
||||
_location = '${pickedLocation.latitude}, ${pickedLocation.longitude}';
|
||||
print('Localisation sélectionnée: $_location');
|
||||
});
|
||||
} else {
|
||||
print('Localisation non sélectionnée');
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
@@ -199,6 +229,7 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
||||
style: const TextStyle(color: Colors.white),
|
||||
onSaved: (value) {
|
||||
_category = value ?? '';
|
||||
print('Catégorie sauvegardée: $_category');
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -207,6 +238,7 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
// Logique pour sélectionner une image
|
||||
print('Image Picker activé');
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
|
||||
@@ -218,7 +250,9 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
_imagePath == null ? 'Sélectionnez une image' : 'Image sélectionnée',
|
||||
_imagePath == null
|
||||
? 'Sélectionnez une image'
|
||||
: 'Image sélectionnée: $_imagePath',
|
||||
style: const TextStyle(color: Colors.white70),
|
||||
),
|
||||
const Icon(Icons.image, color: Colors.white70),
|
||||
@@ -244,17 +278,69 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
||||
style: const TextStyle(color: Colors.white),
|
||||
onSaved: (value) {
|
||||
_link = value ?? '';
|
||||
print('Lien sauvegardé: $_link');
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSubmitButton() {
|
||||
return ElevatedButton(
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
_formKey.currentState!.save();
|
||||
// Logique pour soumettre les données
|
||||
Navigator.of(context).pop();
|
||||
print('Formulaire validé');
|
||||
|
||||
// Créer l'événement en utilisant l'ID réel de l'utilisateur pour le créateur et les participants
|
||||
EventModel newEvent = EventModel(
|
||||
title: _title,
|
||||
description: _description,
|
||||
date: _selectedDate?.toIso8601String() ?? '',
|
||||
location: _location,
|
||||
category: _category,
|
||||
link: _link,
|
||||
imageUrl: _imagePath ?? '',
|
||||
creator: CreatorModel(
|
||||
id: widget.userId,
|
||||
nom: widget.userName,
|
||||
prenoms: widget.userLastName,
|
||||
),
|
||||
participants: [
|
||||
ParticipantModel(
|
||||
id: widget.userId,
|
||||
nom: widget.userName,
|
||||
prenoms: widget.userLastName,
|
||||
)
|
||||
],
|
||||
id: '',
|
||||
);
|
||||
|
||||
// Convertir l'événement en JSON
|
||||
Map<String, dynamic> eventData = newEvent.toJson();
|
||||
print('Données JSON de l\'événement: $eventData');
|
||||
|
||||
// Envoyer la requête POST à l'API
|
||||
final response = await http.post(
|
||||
Uri.parse('${Urls.baseUrl}/events'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode(eventData),
|
||||
);
|
||||
|
||||
print('Statut de la réponse: ${response.statusCode}');
|
||||
print('Réponse brute: ${response.body}');
|
||||
|
||||
if (response.statusCode == 201) {
|
||||
// Création réussie
|
||||
print('Événement créé avec succès');
|
||||
Navigator.of(context).pop(true);
|
||||
} else {
|
||||
// Gérer l'erreur
|
||||
print('Erreur lors de la création de l\'événement: ${response.reasonPhrase}');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Erreur: ${response.reasonPhrase}')),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
print('Le formulaire n\'est pas valide');
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
@@ -265,40 +351,8 @@ class _AddEventDialogState extends State<AddEventDialog> {
|
||||
padding: const EdgeInsets.symmetric(vertical: 12.0),
|
||||
minimumSize: const Size(double.infinity, 40),
|
||||
),
|
||||
child: const Text('Ajouter l\'événement', style: TextStyle(color: Colors.white)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LocationPickerScreen extends StatelessWidget {
|
||||
const LocationPickerScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Sélectionnez une localisation'),
|
||||
backgroundColor: const Color(0xFF1E1E2C),
|
||||
),
|
||||
body: GoogleMap(
|
||||
initialCameraPosition: const CameraPosition(
|
||||
target: LatLng(48.8566, 2.3522), // Paris par défaut
|
||||
zoom: 12.0,
|
||||
),
|
||||
markers: Set<Marker>.of(<Marker>[
|
||||
Marker(
|
||||
markerId: const MarkerId('selectedLocation'),
|
||||
position: LatLng(48.8566, 2.3522), // Position par défaut
|
||||
draggable: true,
|
||||
onDragEnd: (newPosition) {
|
||||
Navigator.of(context).pop(newPosition);
|
||||
},
|
||||
)
|
||||
]),
|
||||
onTap: (position) {
|
||||
Navigator.of(context).pop(position);
|
||||
},
|
||||
),
|
||||
child: const Text('Ajouter l\'événement',
|
||||
style: TextStyle(color: Colors.white)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:afterwork/data/datasources/event_remote_data_source.dart';
|
||||
|
||||
/// Widget pour afficher une carte d'événement.
|
||||
class EventCard extends StatelessWidget {
|
||||
final String eventId;
|
||||
final EventRemoteDataSource eventRemoteDataSource;
|
||||
final String userId;
|
||||
final String userName;
|
||||
final String userLastName;
|
||||
final String profileImage;
|
||||
final String name;
|
||||
final String datePosted;
|
||||
@@ -19,6 +26,11 @@ class EventCard extends StatelessWidget {
|
||||
|
||||
const EventCard({
|
||||
Key? key,
|
||||
required this.eventId,
|
||||
required this.eventRemoteDataSource,
|
||||
required this.userId,
|
||||
required this.userName,
|
||||
required this.userLastName,
|
||||
required this.profileImage,
|
||||
required this.name,
|
||||
required this.datePosted,
|
||||
@@ -33,7 +45,7 @@ class EventCard extends StatelessWidget {
|
||||
required this.onShare,
|
||||
required this.onParticipate,
|
||||
required this.onCloseEvent,
|
||||
required this.onMoreOptions, required String assetImage,
|
||||
required this.onMoreOptions,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -65,6 +77,7 @@ class EventCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
/// Construire l'en-tête de la carte avec les informations de l'utilisateur.
|
||||
Widget _buildHeader() {
|
||||
return Row(
|
||||
children: [
|
||||
@@ -94,16 +107,17 @@ class EventCard extends StatelessWidget {
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.more_vert, color: Colors.white),
|
||||
onPressed: onMoreOptions,
|
||||
onPressed: _onMoreOptions,
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close, color: Colors.white),
|
||||
onPressed: onCloseEvent,
|
||||
onPressed: _onCloseEvent,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// Afficher les détails de l'événement.
|
||||
Widget _buildEventDetails() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -125,6 +139,7 @@ class EventCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
/// Afficher l'image de l'événement.
|
||||
Widget _buildEventImage() {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10.0),
|
||||
@@ -134,6 +149,7 @@ class EventCard extends StatelessWidget {
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
print('Erreur de chargement de l\'image: $error');
|
||||
return Image.asset(
|
||||
'lib/assets/images/placeholder.png',
|
||||
height: 180,
|
||||
@@ -145,6 +161,7 @@ class EventCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
/// Afficher les icônes d'interaction (réagir, commenter, partager).
|
||||
Widget _buildInteractionRow() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
@@ -174,6 +191,7 @@ class EventCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
/// Bouton d'interaction personnalisé.
|
||||
Widget _buildIconButton({
|
||||
required IconData icon,
|
||||
required String label,
|
||||
@@ -192,9 +210,10 @@ class EventCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
/// Bouton pour participer à l'événement.
|
||||
Widget _buildParticipateButton() {
|
||||
return ElevatedButton(
|
||||
onPressed: onParticipate,
|
||||
onPressed: _onParticipate,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF1DBF73),
|
||||
shape: RoundedRectangleBorder(
|
||||
@@ -206,4 +225,54 @@ class EventCard extends StatelessWidget {
|
||||
child: const Text('Participer', style: TextStyle(color: Colors.white)),
|
||||
);
|
||||
}
|
||||
|
||||
// Logique pour réagir à l'événement.
|
||||
void _onReact() async {
|
||||
try {
|
||||
print('Tentative de réaction à l\'événement $eventId par l\'utilisateur $userId');
|
||||
await eventRemoteDataSource.reactToEvent(eventId, userId);
|
||||
print('Réaction à l\'événement réussie');
|
||||
// Mettre à jour l'interface utilisateur, par exemple augmenter le compteur de réactions.
|
||||
} catch (e) {
|
||||
// Gérer l'erreur.
|
||||
print('Erreur lors de la réaction à l\'événement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Logique pour commenter l'événement.
|
||||
void _onComment() {
|
||||
// Implémenter la logique pour commenter un événement.
|
||||
print('Commentaire sur l\'événement $eventId par l\'utilisateur $userId');
|
||||
}
|
||||
|
||||
// Logique pour partager l'événement.
|
||||
void _onShare() {
|
||||
// Implémenter la logique pour partager un événement.
|
||||
print('Partage de l\'événement $eventId par l\'utilisateur $userId');
|
||||
}
|
||||
|
||||
// Logique pour participer à l'événement.
|
||||
void _onParticipate() async {
|
||||
try {
|
||||
print('Tentative de participation à l\'événement $eventId par l\'utilisateur $userId');
|
||||
await eventRemoteDataSource.participateInEvent(eventId, userId);
|
||||
print('Participation à l\'événement réussie');
|
||||
// Mettre à jour l'interface utilisateur, par exemple afficher un message de succès.
|
||||
} catch (e) {
|
||||
// Gérer l'erreur.
|
||||
print('Erreur lors de la participation à l\'événement: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// Logique pour fermer l'événement.
|
||||
void _onCloseEvent() {
|
||||
// Implémenter la logique pour fermer un événement.
|
||||
print('Fermeture de l\'événement $eventId');
|
||||
}
|
||||
|
||||
// Logique pour afficher plus d'options.
|
||||
void _onMoreOptions() {
|
||||
// Implémenter la logique pour afficher plus d'options.
|
||||
print('Affichage des options supplémentaires pour l\'événement $eventId');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,71 +1,129 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../dialogs/add_event_dialog.dart';
|
||||
import 'package:afterwork/data/models/event_model.dart';
|
||||
import 'package:afterwork/data/datasources/event_remote_data_source.dart';
|
||||
import 'event_card.dart';
|
||||
import '../dialogs/add_event_dialog.dart';
|
||||
|
||||
/// Écran principal pour afficher les événements.
|
||||
class EventScreen extends StatelessWidget {
|
||||
const EventScreen({super.key});
|
||||
final EventRemoteDataSource eventRemoteDataSource;
|
||||
final String userId;
|
||||
final String userName; // Nom de l'utilisateur
|
||||
final String userLastName; // Prénom de l'utilisateur
|
||||
const EventScreen({
|
||||
Key? key,
|
||||
required this.eventRemoteDataSource,
|
||||
required this.userId,
|
||||
required this.userName,
|
||||
required this.userLastName,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Événements'),
|
||||
title: const Text(
|
||||
'Événements',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF1DBF73), // Définit la couleur verte du texte
|
||||
),
|
||||
),
|
||||
backgroundColor: const Color(0xFF1E1E2C),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add_circle_outline, size: 28, color: Color(0xFF1DBF73)),
|
||||
onPressed: () {
|
||||
_showAddEventDialog(context);
|
||||
onPressed: () async {
|
||||
final eventData = await showDialog<Map<String, dynamic>>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AddEventDialog(
|
||||
userId: userId,
|
||||
userName: userName,
|
||||
userLastName: userLastName,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (eventData != null) {
|
||||
// Appeler l'API pour créer un nouvel événement.
|
||||
try {
|
||||
print('Tentative de création d\'un nouvel événement par l\'utilisateur $userId');
|
||||
await eventRemoteDataSource.createEvent(eventData as EventModel);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Événement ajouté avec succès !')),
|
||||
);
|
||||
} catch (e) {
|
||||
print('Erreur lors de la création de l\'événement: $e');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Erreur : $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: ListView.builder(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
itemCount: 10,
|
||||
itemBuilder: (context, index) {
|
||||
return EventCard(
|
||||
profileImage: 'lib/assets/images/profile_picture.png',
|
||||
name: 'Nom Prénom',
|
||||
datePosted: 'Posté le 24/08/2024',
|
||||
eventTitle: 'Titre de l\'événement',
|
||||
eventDescription: 'Description détaillée de l\'événement...',
|
||||
eventImageUrl: 'lib/assets/images/profile_picture.png',
|
||||
reactionsCount: 120,
|
||||
commentsCount: 45,
|
||||
sharesCount: 30,
|
||||
onReact: () {
|
||||
// Logique pour réagir à l'événement
|
||||
body: FutureBuilder<List<EventModel>>(
|
||||
future: eventRemoteDataSource.getAllEvents(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError) {
|
||||
print('Erreur lors de la récupération des événements: ${snapshot.error}');
|
||||
return Center(child: Text('Erreur: ${snapshot.error}'));
|
||||
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return const Center(child: Text('Aucun événement trouvé.'));
|
||||
}
|
||||
|
||||
final events = snapshot.data!;
|
||||
print('Nombre d\'événements récupérés: ${events.length}');
|
||||
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
itemCount: events.length,
|
||||
itemBuilder: (context, index) {
|
||||
final event = events[index];
|
||||
print('Affichage de l\'événement ${event.id}');
|
||||
|
||||
return EventCard(
|
||||
eventRemoteDataSource: eventRemoteDataSource,
|
||||
userId: userId,
|
||||
eventId: event.id,
|
||||
userName: userName,
|
||||
userLastName: userLastName,
|
||||
profileImage: 'lib/assets/images/profile_picture.png',
|
||||
name: '$userName $userLastName',
|
||||
datePosted: 'Posté le 24/08/2024',
|
||||
eventTitle: event.title,
|
||||
eventDescription: event.description,
|
||||
eventImageUrl: event.imageUrl ?? 'lib/assets/images/placeholder.png',
|
||||
reactionsCount: 120, // Exemple de valeur
|
||||
commentsCount: 45, // Exemple de valeur
|
||||
sharesCount: 30, // Exemple de valeur
|
||||
onReact: () {
|
||||
print('Réaction à l\'événement ${event.id}');
|
||||
},
|
||||
onComment: () {
|
||||
print('Commentaire sur l\'événement ${event.id}');
|
||||
},
|
||||
onShare: () {
|
||||
print('Partage de l\'événement ${event.id}');
|
||||
},
|
||||
onParticipate: () {
|
||||
print('Participation à l\'événement ${event.id}');
|
||||
},
|
||||
onCloseEvent: () {
|
||||
print('Fermeture de l\'événement ${event.id}');
|
||||
},
|
||||
onMoreOptions: () {
|
||||
print('Affichage des options pour l\'événement ${event.id}');
|
||||
},
|
||||
);
|
||||
},
|
||||
onComment: () {
|
||||
// Logique pour commenter l'événement
|
||||
},
|
||||
onShare: () {
|
||||
// Logique pour partager l'événement
|
||||
},
|
||||
onParticipate: () {
|
||||
// Logique pour participer à l'événement
|
||||
},
|
||||
onCloseEvent: () {
|
||||
// Logique pour fermer l'événement
|
||||
},
|
||||
onMoreOptions: () {
|
||||
// Logique pour afficher plus d'options
|
||||
},
|
||||
assetImage: 'lib/assets/images/placeholder.png', // Ajoutez ce paramètre requis
|
||||
);
|
||||
},
|
||||
),
|
||||
backgroundColor: const Color(0xFF1E1E2C),
|
||||
);
|
||||
}
|
||||
|
||||
void _showAddEventDialog(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return const AddEventDialog();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,26 @@ import 'package:afterwork/presentation/screens/profile/profile_screen.dart';
|
||||
import 'package:afterwork/presentation/screens/social/social_screen.dart';
|
||||
import 'package:afterwork/presentation/screens/establishments/establishments_screen.dart';
|
||||
import 'package:afterwork/presentation/screens/home/home_content.dart';
|
||||
import 'package:afterwork/data/datasources/event_remote_data_source.dart';
|
||||
|
||||
/// Classe principale pour l'écran d'accueil de l'application.
|
||||
/// Cette classe gère la navigation entre les différentes sections de l'application
|
||||
/// en utilisant un [TabController] pour contrôler les différents onglets.
|
||||
/// Les actions de l'AppBar sont également personnalisées pour offrir des fonctionnalités
|
||||
/// spécifiques comme la recherche, la publication et la messagerie.
|
||||
class HomeScreen extends StatefulWidget {
|
||||
const HomeScreen({super.key});
|
||||
final EventRemoteDataSource eventRemoteDataSource;
|
||||
final String userId;
|
||||
final String userName;
|
||||
final String userLastName;
|
||||
|
||||
const HomeScreen({
|
||||
Key? key,
|
||||
required this.eventRemoteDataSource,
|
||||
required this.userId,
|
||||
required this.userName,
|
||||
required this.userLastName,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_HomeScreenState createState() => _HomeScreenState();
|
||||
@@ -18,25 +35,32 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Initialisation du TabController avec 5 onglets.
|
||||
_tabController = TabController(length: 5, vsync: this);
|
||||
debugPrint('HomeScreen initialisé avec userId: ${widget.userId}, userName: ${widget.userName}, userLastName: ${widget.userLastName}');
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Nettoyage du TabController pour éviter les fuites de mémoire.
|
||||
_tabController.dispose();
|
||||
super.dispose();
|
||||
debugPrint('HomeScreen dispose appelé');
|
||||
}
|
||||
|
||||
/// Gestion des sélections dans le menu contextuel de l'AppBar.
|
||||
void _onMenuSelected(BuildContext context, String option) {
|
||||
// Implémente la logique pour chaque option ici
|
||||
switch (option) {
|
||||
case 'Publier':
|
||||
// Redirige vers la page de publication
|
||||
debugPrint('Option "Publier" sélectionnée');
|
||||
// Rediriger vers la page de publication.
|
||||
break;
|
||||
case 'Story':
|
||||
// Redirige vers la page de création de Story
|
||||
debugPrint('Option "Story" sélectionnée');
|
||||
// Rediriger vers la page de création de Story.
|
||||
break;
|
||||
default:
|
||||
debugPrint('Option inconnue sélectionnée: $option');
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -45,22 +69,25 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.black, // Fond noir pour l'AppBar
|
||||
elevation: 0, // Enlève l'ombre sous l'AppBar
|
||||
backgroundColor: Colors.black,
|
||||
elevation: 0,
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Image.asset(
|
||||
'lib/assets/images/logo.png', // Chemin correct de ton logo
|
||||
'lib/assets/images/logo.png', // Chemin correct de votre logo.
|
||||
height: 40,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
// Bouton +
|
||||
// Bouton pour ajouter du contenu (Publier, Story).
|
||||
CircleAvatar(
|
||||
backgroundColor: Colors.white, // Cercle blanc
|
||||
radius: 18, // Réduit la taille du bouton
|
||||
backgroundColor: Colors.white,
|
||||
radius: 18,
|
||||
child: PopupMenuButton<String>(
|
||||
onSelected: (value) => _onMenuSelected(context, value),
|
||||
onSelected: (value) {
|
||||
_onMenuSelected(context, value);
|
||||
debugPrint('Menu contextuel sélectionné: $value');
|
||||
},
|
||||
itemBuilder: (context) => [
|
||||
const PopupMenuItem(
|
||||
value: 'Publier',
|
||||
@@ -71,39 +98,48 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
child: Text('Story'),
|
||||
),
|
||||
],
|
||||
icon: const Icon(Icons.add, color: Colors.blueAccent, size: 20), // Icône bleue légèrement plus petite
|
||||
color: Colors.white, // Menu contextuel en blanc
|
||||
icon: const Icon(Icons.add, color: Colors.blueAccent, size: 20),
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8), // Réduit l'espacement entre les boutons
|
||||
// Bouton Recherche
|
||||
const SizedBox(width: 8), // Espacement entre les boutons.
|
||||
|
||||
// Bouton Recherche.
|
||||
CircleAvatar(
|
||||
backgroundColor: Colors.white,
|
||||
radius: 18, // Réduit la taille du bouton
|
||||
radius: 18,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.search, color: Colors.blueAccent, size: 20), // Icône bleue légèrement plus petite
|
||||
icon: const Icon(Icons.search, color: Colors.blueAccent, size: 20),
|
||||
onPressed: () {
|
||||
// Implémente la logique de recherche ici
|
||||
debugPrint('Bouton Recherche appuyé');
|
||||
// Implémenter la logique de recherche ici.
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8), // Réduit l'espacement entre les boutons
|
||||
// Bouton Messagerie
|
||||
const SizedBox(width: 8), // Espacement entre les boutons.
|
||||
|
||||
// Bouton Messagerie.
|
||||
CircleAvatar(
|
||||
backgroundColor: Colors.white,
|
||||
radius: 18, // Réduit la taille du bouton
|
||||
radius: 18,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.message, color: Colors.blueAccent, size: 20), // Icône bleue légèrement plus petite
|
||||
icon: const Icon(Icons.message, color: Colors.blueAccent, size: 20),
|
||||
onPressed: () {
|
||||
// Implémente la logique de messagerie ici
|
||||
debugPrint('Bouton Messagerie appuyé');
|
||||
// Implémenter la logique de messagerie ici.
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8), // Réduit l'espacement entre les boutons
|
||||
const SizedBox(width: 8), // Espacement entre les boutons.
|
||||
],
|
||||
bottom: TabBar(
|
||||
controller: _tabController,
|
||||
indicatorColor: Colors.blueAccent,
|
||||
labelColor: Colors.white, // Couleur du texte sélectionné.
|
||||
unselectedLabelColor: Colors.grey[400], // Couleur du texte non sélectionné.
|
||||
onTap: (index) {
|
||||
debugPrint('Onglet sélectionné: $index');
|
||||
},
|
||||
tabs: const [
|
||||
Tab(icon: Icon(Icons.home), text: 'Accueil'),
|
||||
Tab(icon: Icon(Icons.event), text: 'Événements'),
|
||||
@@ -115,15 +151,20 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
),
|
||||
body: TabBarView(
|
||||
controller: _tabController,
|
||||
children: const [
|
||||
HomeContentScreen(), // Contenu de l'accueil
|
||||
EventScreen(), // Écran des événements
|
||||
EstablishmentsScreen(), // Écran des établissements
|
||||
SocialScreen(), // Écran social
|
||||
ProfileScreen(), // Écran du profil
|
||||
children: [
|
||||
const HomeContentScreen(), // Contenu de l'accueil.
|
||||
EventScreen(
|
||||
eventRemoteDataSource: widget.eventRemoteDataSource,
|
||||
userId: widget.userId,
|
||||
userName: widget.userName,
|
||||
userLastName: widget.userLastName,
|
||||
), // Écran des événements.
|
||||
const EstablishmentsScreen(), // Écran des établissements.
|
||||
const SocialScreen(), // Écran social.
|
||||
const ProfileScreen(), // Écran du profil.
|
||||
],
|
||||
),
|
||||
backgroundColor: Colors.black, // Arrière-plan de l'écran en noir
|
||||
backgroundColor: Colors.black, // Arrière-plan de l'écran en noir.
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
|
||||
class LocationPickerScreen extends StatelessWidget {
|
||||
const LocationPickerScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Sélectionnez une localisation'),
|
||||
backgroundColor: const Color(0xFF1E1E2C),
|
||||
),
|
||||
body: GoogleMap(
|
||||
initialCameraPosition: const CameraPosition(
|
||||
target: LatLng(48.8566, 2.3522), // Paris par défaut
|
||||
zoom: 12.0,
|
||||
),
|
||||
markers: <Marker>{
|
||||
Marker(
|
||||
markerId: const MarkerId('selectedLocation'),
|
||||
position: const LatLng(48.8566, 2.3522), // Position par défaut
|
||||
draggable: true,
|
||||
onDragEnd: (newPosition) {
|
||||
print('Nouvelle position sélectionnée: $newPosition');
|
||||
Navigator.of(context).pop(newPosition);
|
||||
},
|
||||
)
|
||||
},
|
||||
onTap: (position) {
|
||||
print('Position tapée: $position');
|
||||
Navigator.of(context).pop(position);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,14 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:afterwork/data/datasources/user_remote_data_source.dart';
|
||||
import 'package:afterwork/data/models/user_model.dart';
|
||||
import 'package:afterwork/presentation/screens/home/home_screen.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:afterwork/data/services/hash_password.dart';
|
||||
import 'package:afterwork/data/services/secure_storage.dart';
|
||||
import 'package:afterwork/data/services/preferences_helper.dart';
|
||||
|
||||
import '../../../data/datasources/event_remote_data_source.dart';
|
||||
|
||||
class LoginScreen extends StatefulWidget {
|
||||
const LoginScreen({super.key});
|
||||
@@ -13,12 +19,15 @@ class LoginScreen extends StatefulWidget {
|
||||
|
||||
class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStateMixin {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
String _userId = '';
|
||||
String _email = '';
|
||||
String _password = '';
|
||||
bool _isPasswordVisible = false;
|
||||
bool _isSubmitting = false;
|
||||
|
||||
final UserRemoteDataSource _userRemoteDataSource = UserRemoteDataSource(http.Client());
|
||||
final SecureStorage _secureStorage = SecureStorage();
|
||||
final PreferencesHelper _preferencesHelper = PreferencesHelper();
|
||||
|
||||
late AnimationController _controller;
|
||||
late Animation<double> _buttonScaleAnimation;
|
||||
@@ -41,12 +50,14 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// Afficher/Masquer le mot de passe
|
||||
void _togglePasswordVisibility() {
|
||||
setState(() {
|
||||
_isPasswordVisible = !_isPasswordVisible;
|
||||
});
|
||||
}
|
||||
|
||||
/// Soumission du formulaire d'authentification
|
||||
void _submit() async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
setState(() {
|
||||
@@ -54,25 +65,65 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
});
|
||||
_formKey.currentState!.save();
|
||||
|
||||
try {
|
||||
UserModel user = await _userRemoteDataSource.authenticateUser(_email, _password);
|
||||
print('Connexion réussie : ${user.email}');
|
||||
print("===== DEBUT DE LA SOUMISSION DU FORMULAIRE =====");
|
||||
print("Email: $_email");
|
||||
print("Mot de passe: $_password");
|
||||
|
||||
// Navigation vers la page d'accueil
|
||||
try {
|
||||
print('Début de l\'authentification'); // Débogage
|
||||
|
||||
// Hachage du mot de passe avec SHA-256
|
||||
String hashedPassword = hashPassword(_password);
|
||||
print("Mot de passe haché: $hashedPassword");
|
||||
|
||||
// Authentification via l'API avec un timeout
|
||||
UserModel user = await _userRemoteDataSource
|
||||
.authenticateUser(_email, hashedPassword, "unique_user_id")
|
||||
.timeout(
|
||||
Duration(seconds: 10),
|
||||
onTimeout: () {
|
||||
throw TimeoutException('Le temps de connexion a expiré. Veuillez réessayer.');
|
||||
},
|
||||
);
|
||||
|
||||
print('Connexion réussie : ${user.userId} - ${user.email}');
|
||||
|
||||
// Sauvegarde des données de l'utilisateur après authentification
|
||||
await _secureStorage.saveUserId(user.userId);
|
||||
await _preferencesHelper.saveUserName(user.nom);
|
||||
await _preferencesHelper.saveUserLastName(user.prenoms);
|
||||
|
||||
print("===== SAUVEGARDE DES DONNÉES UTILISATEUR =====");
|
||||
print("User ID: ${user.userId}");
|
||||
print("User Name: ${user.nom}");
|
||||
print("User Last Name: ${user.prenoms}");
|
||||
|
||||
// Navigation vers l'écran d'accueil
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const HomeScreen()),
|
||||
MaterialPageRoute(
|
||||
builder: (context) => HomeScreen(
|
||||
eventRemoteDataSource: EventRemoteDataSource(http.Client()),
|
||||
userId: user.userId,
|
||||
userName: user.nom,
|
||||
userLastName: user.prenoms,
|
||||
),
|
||||
),
|
||||
);
|
||||
print("===== NAVIGATION VERS HOME SCREEN =====");
|
||||
} catch (e) {
|
||||
print('Erreur lors de la connexion: $e');
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(e.toString())),
|
||||
SnackBar(content: Text('Erreur : ${e.toString()}')),
|
||||
);
|
||||
} finally {
|
||||
print('Fin du processus d\'authentification'); // Débogage
|
||||
setState(() {
|
||||
_isSubmitting = false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
print("===== FORMULAIRE NON VALIDE =====");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +153,7 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Logo avec légère animation
|
||||
// Logo animé
|
||||
AnimatedBuilder(
|
||||
animation: _controller,
|
||||
builder: (context, child) {
|
||||
@@ -154,15 +205,18 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
style: const TextStyle(color: Colors.white),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
print("Erreur: Le champ email est vide");
|
||||
return 'Veuillez entrer votre email';
|
||||
}
|
||||
if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
|
||||
print("Erreur: Le format de l'email est invalide");
|
||||
return 'Veuillez entrer un email valide';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) {
|
||||
_email = value ?? ''; // Utiliser une chaîne vide si value est null
|
||||
_email = value ?? ''; // Utiliser une chaîne vide si value est null
|
||||
print("Email sauvegardé: $_email");
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
@@ -180,9 +234,8 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
prefixIcon: const Icon(Icons.lock, color: Colors.white),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_isPasswordVisible ? Icons.visibility : Icons.visibility_off,
|
||||
color: Colors.white,
|
||||
),
|
||||
_isPasswordVisible ? Icons.visibility : Icons.visibility_off,
|
||||
color: Colors.white),
|
||||
onPressed: _togglePasswordVisibility,
|
||||
),
|
||||
),
|
||||
@@ -190,15 +243,18 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
style: const TextStyle(color: Colors.white),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
print("Erreur: Le champ mot de passe est vide");
|
||||
return 'Veuillez entrer votre mot de passe';
|
||||
}
|
||||
if (value.length < 6) {
|
||||
print("Erreur: Le mot de passe est trop court");
|
||||
return 'Le mot de passe doit comporter au moins 6 caractères';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) {
|
||||
_password = value ?? ''; // Utiliser une chaîne vide si value est null
|
||||
_password = value ?? ''; // Utiliser une chaîne vide si value est null
|
||||
print("Mot de passe sauvegardé: $_password");
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
@@ -225,6 +281,7 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// Naviguer vers la page d'inscription
|
||||
print("Redirection vers la page d'inscription");
|
||||
},
|
||||
child: const Text(
|
||||
'Pas encore de compte ? Inscrivez-vous',
|
||||
|
||||
@@ -7,274 +7,388 @@ class ProfileScreen extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Profil'),
|
||||
backgroundColor: Colors.black,
|
||||
title: const Text('Profil',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF1DBF73), // Définit la couleur verte du texte
|
||||
),
|
||||
),
|
||||
backgroundColor: const Color(0xFF1E1E2C),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.settings),
|
||||
icon: const Icon(Icons.settings, color: Colors.white),
|
||||
onPressed: () {
|
||||
// Naviguer vers la page des paramètres
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
SliverList(
|
||||
delegate: SliverChildListDelegate([
|
||||
// Informations de l'Utilisateur
|
||||
_buildUserInfoSection(context),
|
||||
const Divider(),
|
||||
// Options de Modification
|
||||
_buildEditOptions(context),
|
||||
const Divider(),
|
||||
// Statistiques Personnelles
|
||||
_buildStatisticsSection(context),
|
||||
const Divider(),
|
||||
// Historique
|
||||
_buildHistorySection(context),
|
||||
const Divider(),
|
||||
// Préférences et Paramètres
|
||||
_buildPreferencesSection(context),
|
||||
const Divider(),
|
||||
// Autres Fonctions
|
||||
_buildSupportSection(context),
|
||||
const Divider(),
|
||||
// Suppression de Compte
|
||||
_buildAccountDeletionSection(context),
|
||||
]),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
children: [
|
||||
_buildUserInfoCard(),
|
||||
const SizedBox(height: 20),
|
||||
_buildEditOptionsCard(),
|
||||
const SizedBox(height: 20),
|
||||
_buildStatisticsSectionCard(),
|
||||
const SizedBox(height: 20),
|
||||
_buildExpandableSectionCard(
|
||||
title: 'Historique',
|
||||
icon: Icons.history,
|
||||
children: [
|
||||
_buildAnimatedListTile(
|
||||
icon: Icons.event_note,
|
||||
label: 'Historique des Événements',
|
||||
onTap: () {
|
||||
// Naviguer vers l'historique des événements
|
||||
},
|
||||
),
|
||||
_buildAnimatedListTile(
|
||||
icon: Icons.history,
|
||||
label: 'Historique des Publications',
|
||||
onTap: () {
|
||||
// Naviguer vers l'historique des publications
|
||||
},
|
||||
),
|
||||
_buildAnimatedListTile(
|
||||
icon: Icons.bookmark,
|
||||
label: 'Historique de Réservations',
|
||||
onTap: () {
|
||||
// Naviguer vers l'historique des réservations
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildExpandableSectionCard(
|
||||
title: 'Préférences et Paramètres',
|
||||
icon: Icons.settings,
|
||||
children: [
|
||||
_buildAnimatedListTile(
|
||||
icon: Icons.privacy_tip,
|
||||
label: 'Paramètres de confidentialité',
|
||||
onTap: () {
|
||||
// Naviguer vers les paramètres de confidentialité
|
||||
},
|
||||
),
|
||||
_buildAnimatedListTile(
|
||||
icon: Icons.notifications,
|
||||
label: 'Notifications',
|
||||
onTap: () {
|
||||
// Naviguer vers les paramètres de notification
|
||||
},
|
||||
),
|
||||
_buildAnimatedListTile(
|
||||
icon: Icons.language,
|
||||
label: 'Langue de l\'application',
|
||||
onTap: () {
|
||||
// Naviguer vers les paramètres de langue
|
||||
},
|
||||
),
|
||||
_buildAnimatedListTile(
|
||||
icon: Icons.format_paint,
|
||||
label: 'Thème de l\'application',
|
||||
onTap: () {
|
||||
// Naviguer vers les paramètres de thème
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildSupportSectionCard(),
|
||||
const SizedBox(height: 20),
|
||||
_buildAccountDeletionCard(context),
|
||||
],
|
||||
),
|
||||
backgroundColor: const Color(0xFF1E1E2C),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildUserInfoCard() {
|
||||
return Card(
|
||||
color: const Color(0xFF292B37),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
const CircleAvatar(
|
||||
radius: 50,
|
||||
backgroundImage: AssetImage('lib/assets/images/profile_picture.png'),
|
||||
backgroundColor: Colors.transparent,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const Text(
|
||||
'GBANE Dahoud',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
letterSpacing: 1.2,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
'pseudo',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey[400],
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
'gbanedahoud@lions.dev',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey[600],
|
||||
decoration: TextDecoration.underline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEditOptionsCard() {
|
||||
return Card(
|
||||
color: const Color(0xFF292B37),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildAnimatedListTile(
|
||||
icon: Icons.edit,
|
||||
label: 'Éditer le profil',
|
||||
onTap: () {
|
||||
// Naviguer vers la page d'édition de profil
|
||||
},
|
||||
),
|
||||
_buildAnimatedListTile(
|
||||
icon: Icons.camera_alt,
|
||||
label: 'Changer la photo de profil',
|
||||
onTap: () {
|
||||
// Naviguer vers la page de changement de photo de profil
|
||||
},
|
||||
),
|
||||
_buildAnimatedListTile(
|
||||
icon: Icons.lock,
|
||||
label: 'Changer le mot de passe',
|
||||
onTap: () {
|
||||
// Naviguer vers la page de changement de mot de passe
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildUserInfoSection(BuildContext context) {
|
||||
return const Column(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 50,
|
||||
backgroundImage: AssetImage('lib/assets/images/profile_picture.png'), //Photo de profil
|
||||
Widget _buildStatisticsSectionCard() {
|
||||
return Card(
|
||||
color: const Color(0xFF292B37),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Statistiques Personnelles',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
_buildStatTile(
|
||||
icon: Icons.event,
|
||||
label: 'Événements Participés',
|
||||
value: '12',
|
||||
),
|
||||
_buildStatTile(
|
||||
icon: Icons.place,
|
||||
label: 'Établissements Visités',
|
||||
value: '8',
|
||||
),
|
||||
_buildStatTile(
|
||||
icon: Icons.post_add,
|
||||
label: 'Publications',
|
||||
value: '24',
|
||||
),
|
||||
_buildStatTile(
|
||||
icon: Icons.group,
|
||||
label: 'Amis/Followers',
|
||||
value: '150',
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Text(
|
||||
'GBANE Dahoud',
|
||||
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
'pseudo',
|
||||
style: TextStyle(fontSize: 16, color: Colors.grey),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
'gbanedahoud@lions.dev',
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEditOptions(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.edit, color: Colors.blueAccent),
|
||||
title: const Text('Éditer le profil'),
|
||||
onTap: () {
|
||||
// Naviguer vers la page d'édition de profil
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.camera_alt, color: Colors.blueAccent),
|
||||
title: const Text('Changer la photo de profil'),
|
||||
onTap: () {
|
||||
// Naviguer vers la page de changement de photo de profil
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.lock, color: Colors.blueAccent),
|
||||
title: const Text('Changer le mot de passe'),
|
||||
onTap: () {
|
||||
// Naviguer vers la page de changement de mot de passe
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatisticsSection(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'Statistiques Personnelles',
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.event, color: Colors.blueAccent),
|
||||
title: const Text('Événements Participés'),
|
||||
trailing: const Text('12'), // Exemple de valeur
|
||||
onTap: () {
|
||||
// Naviguer vers la page des événements participés
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.place, color: Colors.blueAccent),
|
||||
title: const Text('Établissements Visités'),
|
||||
trailing: const Text('8'), // Exemple de valeur
|
||||
onTap: () {
|
||||
// Naviguer vers la page des établissements visités
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.post_add, color: Colors.blueAccent),
|
||||
title: const Text('Publications'),
|
||||
trailing: const Text('24'), // Exemple de valeur
|
||||
onTap: () {
|
||||
// Naviguer vers la page des publications
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.group, color: Colors.blueAccent),
|
||||
title: const Text('Amis/Followers'),
|
||||
trailing: const Text('150'), // Exemple de valeur
|
||||
onTap: () {
|
||||
// Naviguer vers la page des amis ou followers
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHistorySection(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'Historique',
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.event_note, color: Colors.blueAccent),
|
||||
title: const Text('Historique des Événements'),
|
||||
onTap: () {
|
||||
// Naviguer vers l'historique des événements
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.history, color: Colors.blueAccent),
|
||||
title: const Text('Historique des Publications'),
|
||||
onTap: () {
|
||||
// Naviguer vers l'historique des publications
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.bookmark, color: Colors.blueAccent),
|
||||
title: const Text('Historique de Réservations'),
|
||||
onTap: () {
|
||||
// Naviguer vers l'historique des réservations
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPreferencesSection(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'Préférences et Paramètres',
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.privacy_tip, color: Colors.blueAccent),
|
||||
title: const Text('Paramètres de confidentialité'),
|
||||
onTap: () {
|
||||
// Naviguer vers les paramètres de confidentialité
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.notifications, color: Colors.blueAccent),
|
||||
title: const Text('Notifications'),
|
||||
onTap: () {
|
||||
// Naviguer vers les paramètres de notification
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.language, color: Colors.blueAccent),
|
||||
title: const Text('Langue de l\'application'),
|
||||
onTap: () {
|
||||
// Naviguer vers les paramètres de langue
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.format_paint, color: Colors.blueAccent),
|
||||
title: const Text('Thème de l\'application'),
|
||||
onTap: () {
|
||||
// Naviguer vers les paramètres de thème
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSupportSection(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'Support et Assistance',
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.help, color: Colors.blueAccent),
|
||||
title: const Text('Support et Assistance'),
|
||||
onTap: () {
|
||||
// Naviguer vers la page de support
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.article, color: Colors.blueAccent),
|
||||
title: const Text('Conditions d\'utilisation'),
|
||||
onTap: () {
|
||||
// Naviguer vers les conditions d'utilisation
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.privacy_tip, color: Colors.blueAccent),
|
||||
title: const Text('Politique de confidentialité'),
|
||||
onTap: () {
|
||||
// Naviguer vers la politique de confidentialité
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAccountDeletionSection(BuildContext context) {
|
||||
return ListTile(
|
||||
leading: const Icon(Icons.delete, color: Colors.redAccent),
|
||||
title: const Text(
|
||||
'Supprimer le compte',
|
||||
style: TextStyle(color: Colors.redAccent),
|
||||
),
|
||||
onTap: () {
|
||||
// Implémenter la logique de suppression de compte
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildExpandableSectionCard({
|
||||
required String title,
|
||||
required IconData icon,
|
||||
required List<Widget> children,
|
||||
}) {
|
||||
return Card(
|
||||
color: const Color(0xFF292B37),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
child: ExpansionTile(
|
||||
title: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
leading: Icon(icon, color: const Color(0xFF1DBF73)),
|
||||
iconColor: const Color(0xFF1DBF73),
|
||||
collapsedIconColor: const Color(0xFF1DBF73),
|
||||
children: children,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSupportSectionCard() {
|
||||
return Card(
|
||||
color: const Color(0xFF292B37),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
child: Column(
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(16.0),
|
||||
child: Text(
|
||||
'Support et Assistance',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildAnimatedListTile(
|
||||
icon: Icons.help,
|
||||
label: 'Support et Assistance',
|
||||
onTap: () {
|
||||
// Naviguer vers la page de support
|
||||
},
|
||||
),
|
||||
_buildAnimatedListTile(
|
||||
icon: Icons.article,
|
||||
label: 'Conditions d\'utilisation',
|
||||
onTap: () {
|
||||
// Naviguer vers les conditions d'utilisation
|
||||
},
|
||||
),
|
||||
_buildAnimatedListTile(
|
||||
icon: Icons.privacy_tip,
|
||||
label: 'Politique de confidentialité',
|
||||
onTap: () {
|
||||
// Naviguer vers la politique de confidentialité
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAccountDeletionCard(BuildContext context) {
|
||||
return Card(
|
||||
color: const Color(0xFF292B37),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
child: ListTile(
|
||||
leading: const Icon(Icons.delete, color: Colors.redAccent),
|
||||
title: const Text(
|
||||
'Supprimer le compte',
|
||||
style: TextStyle(color: Colors.redAccent, fontWeight: FontWeight.bold),
|
||||
),
|
||||
onTap: () {
|
||||
_showDeleteConfirmationDialog(context);
|
||||
},
|
||||
hoverColor: Colors.red.withOpacity(0.1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showDeleteConfirmationDialog(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: const Color(0xFF1E1E2C),
|
||||
title: const Text(
|
||||
'Confirmer la suppression',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
content: const Text(
|
||||
'Êtes-vous sûr de vouloir supprimer votre compte ? Cette action est irréversible.',
|
||||
style: TextStyle(color: Colors.white70),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(); // Fermer le popup
|
||||
},
|
||||
child: const Text(
|
||||
'Annuler',
|
||||
style: TextStyle(color: Color(0xFF1DBF73)),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// Logique de suppression du compte ici
|
||||
Navigator.of(context).pop(); // Fermer le popup après la suppression
|
||||
},
|
||||
child: const Text(
|
||||
'Supprimer',
|
||||
style: TextStyle(color: Colors.redAccent),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAnimatedListTile({
|
||||
required IconData icon,
|
||||
required String label,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
splashColor: Colors.blueAccent.withOpacity(0.2),
|
||||
child: ListTile(
|
||||
leading: Icon(icon, color: const Color(0xFF1DBF73)),
|
||||
title: Text(
|
||||
label,
|
||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
|
||||
),
|
||||
hoverColor: Colors.blue.withOpacity(0.1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatTile({
|
||||
required IconData icon,
|
||||
required String label,
|
||||
required String value,
|
||||
}) {
|
||||
return ListTile(
|
||||
leading: Icon(icon, color: const Color(0xFF1DBF73)),
|
||||
title: Text(label, style: const TextStyle(color: Colors.white)),
|
||||
trailing: Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
hoverColor: Colors.blue.withOpacity(0.1),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user