Refactoring + Checkpoint
This commit is contained in:
@@ -1,16 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'dart:io';
|
||||
|
||||
import '../../widgets/category_field.dart';
|
||||
import 'dart:io'; // Pour l'usage des fichiers (image)
|
||||
import '../../widgets/fields/category_field.dart'; // Importation des widgets personnalisés
|
||||
import '../../widgets/date_picker.dart';
|
||||
import '../../widgets/description_field.dart';
|
||||
import '../../widgets/link_field.dart';
|
||||
import '../../widgets/location_field.dart';
|
||||
import '../../widgets/fields/description_field.dart';
|
||||
import '../../widgets/fields/link_field.dart';
|
||||
import '../../widgets/fields/location_field.dart';
|
||||
import '../../widgets/submit_button.dart';
|
||||
import '../../widgets/title_field.dart';
|
||||
import '../../widgets/fields/title_field.dart';
|
||||
import '../../widgets/image_preview_picker.dart';
|
||||
import '../../widgets/fields/tags_field.dart';
|
||||
import '../../widgets/fields/attendees_field.dart';
|
||||
import '../../widgets/fields/organizer_field.dart';
|
||||
import '../../widgets/fields/transport_info_field.dart';
|
||||
import '../../widgets/fields/accommodation_info_field.dart';
|
||||
import '../../widgets/fields/privacy_rules_field.dart';
|
||||
import '../../widgets/fields/security_protocol_field.dart';
|
||||
import '../../widgets/fields/parking_field.dart';
|
||||
import '../../widgets/fields/accessibility_field.dart';
|
||||
import '../../widgets/fields/participation_fee_field.dart';
|
||||
|
||||
/// Page pour ajouter un événement
|
||||
/// Permet à l'utilisateur de remplir un formulaire avec des détails sur l'événement
|
||||
class AddEventPage extends StatefulWidget {
|
||||
final String userId;
|
||||
final String userFirstName;
|
||||
@@ -28,22 +39,37 @@ class AddEventPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _AddEventPageState extends State<AddEventPage> {
|
||||
final _formKey = GlobalKey<FormState>(); // Form key for validation
|
||||
final _formKey = GlobalKey<FormState>(); // Clé pour la validation du formulaire
|
||||
|
||||
// Variables pour stocker les données de l'événement
|
||||
String _title = '';
|
||||
String _description = '';
|
||||
DateTime? _selectedDate;
|
||||
DateTime? _endDate;
|
||||
String _location = 'Abidjan';
|
||||
String _category = '';
|
||||
String _link = '';
|
||||
LatLng? _selectedLatLng = const LatLng(5.348722, -3.985038); // Default coordinates
|
||||
File? _selectedImageFile; // Store the selected image
|
||||
String _organizer = '';
|
||||
List<String> _tags = [];
|
||||
int _maxParticipants = 0;
|
||||
LatLng? _selectedLatLng = const LatLng(5.348722, -3.985038); // Coordonnées par défaut
|
||||
File? _selectedImageFile; // Image sélectionnée
|
||||
String _status = 'Actif';
|
||||
String _organizerEmail = '';
|
||||
String _organizerPhone = '';
|
||||
int _participationFee = 0;
|
||||
String _privacyRules = '';
|
||||
String _transportInfo = '';
|
||||
String _accommodationInfo = '';
|
||||
bool _isAccessible = false;
|
||||
bool _hasParking = false;
|
||||
String _securityProtocol = '';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Ajouter un événement'),
|
||||
title: const Text('Créer un événement'),
|
||||
backgroundColor: const Color(0xFF1E1E2C),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
@@ -63,6 +89,8 @@ class _AddEventPageState extends State<AddEventPage> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
// Section d'informations de base
|
||||
_buildSectionHeader('Informations de base'),
|
||||
ImagePreviewPicker(
|
||||
onImagePicked: (File? imageFile) {
|
||||
setState(() {
|
||||
@@ -80,39 +108,126 @@ class _AddEventPageState extends State<AddEventPage> {
|
||||
onDatePicked: (picked) => setState(() {
|
||||
_selectedDate = picked;
|
||||
}),
|
||||
label: 'Date de début',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
DatePickerField(
|
||||
selectedDate: _endDate,
|
||||
onDatePicked: (picked) => setState(() {
|
||||
_endDate = picked;
|
||||
}),
|
||||
label: 'Date de fin',
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
LocationField(
|
||||
location: _location,
|
||||
selectedLatLng: _selectedLatLng,
|
||||
onLocationPicked: (pickedLocation) => setState(() {
|
||||
_selectedLatLng = pickedLocation;
|
||||
_location = '${pickedLocation?.latitude}, ${pickedLocation?.longitude}';
|
||||
}),
|
||||
onLocationPicked: (value) => setState(() => _location = (value ?? 'Abidjan') as String),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
CategoryField(onSaved: (value) => setState(() => _category = value ?? '')),
|
||||
CategoryField(
|
||||
onSaved: (value) => setState(() => _category = value ?? ''),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
LinkField(onSaved: (value) => setState(() => _link = value ?? '')),
|
||||
LinkField(
|
||||
onSaved: (value) => setState(() => _link = value ?? ''),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
AttendeesField(
|
||||
onSaved: (value) => setState(() => _maxParticipants = value ?? 0),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
TagsField(
|
||||
onSaved: (value) => setState(() => _tags = value ?? []),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
OrganizerField(
|
||||
onSaved: (value) => setState(() => _organizer = value ?? ''),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
TransportInfoField(
|
||||
onSaved: (value) => setState(() => _transportInfo = value ?? ''),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
AccommodationInfoField(
|
||||
onSaved: (value) => setState(() => _accommodationInfo = value ?? ''),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
PrivacyRulesField(
|
||||
onSaved: (value) => setState(() => _privacyRules = value ?? ''),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
SecurityProtocolField(
|
||||
onSaved: (value) => setState(() => _securityProtocol = value ?? ''),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
ParkingField(
|
||||
onSaved: (value) => setState(() => _hasParking = (value as bool?) ?? false),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
AccessibilityField(
|
||||
onSaved: (value) => setState(() => _participationFee = (value as int?) ?? 0),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
ParticipationFeeField(
|
||||
onSaved: (value) => setState(() => _participationFee = (value as int?) ?? 0),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
SubmitButton(
|
||||
onPressed: () {
|
||||
if (_formKey.currentState?.validate() ?? false) {
|
||||
// Log des données de l'événement avant l'envoi
|
||||
print('Titre de l\'événement : $_title');
|
||||
print('Description de l\'événement : $_description');
|
||||
print('Date de début : $_selectedDate');
|
||||
print('Date de fin : $_endDate');
|
||||
print('Lieu : $_location');
|
||||
print('Catégorie : $_category');
|
||||
print('Lien de l\'événement : $_link');
|
||||
print('Organisateur : $_organizer');
|
||||
print('Tags : $_tags');
|
||||
print('Maximum de participants : $_maxParticipants');
|
||||
print('Image sélectionnée : $_selectedImageFile');
|
||||
print('Transport : $_transportInfo');
|
||||
print('Hébergement : $_accommodationInfo');
|
||||
print('Règles de confidentialité : $_privacyRules');
|
||||
print('Protocole de sécurité : $_securityProtocol');
|
||||
print('Parking disponible : $_hasParking');
|
||||
print('Accessibilité : $_isAccessible');
|
||||
print('Frais de participation : $_participationFee');
|
||||
// Logique d'envoi des données vers le backend...
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Événement créé avec succès !')),
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Veuillez remplir tous les champs requis')),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Bouton en bas de l'écran
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: SubmitButton(
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
_formKey.currentState!.save();
|
||||
// Logic to add the event goes here
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// En-tête de section pour mieux organiser les champs
|
||||
Widget _buildSectionHeader(String title) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logger/logger.dart'; // Pour la gestion des logs.
|
||||
|
||||
import '../../../data/models/event_model.dart';
|
||||
import '../../widgets/event_header.dart';
|
||||
@@ -7,19 +8,22 @@ import '../../widgets/event_interaction_row.dart';
|
||||
import '../../widgets/event_status_badge.dart';
|
||||
import '../../widgets/swipe_background.dart';
|
||||
|
||||
class EventCard extends StatelessWidget {
|
||||
final EventModel event;
|
||||
final String userId;
|
||||
final String userFirstName;
|
||||
final String userLastName;
|
||||
final String status;
|
||||
final VoidCallback onReact;
|
||||
final VoidCallback onComment;
|
||||
final VoidCallback onShare;
|
||||
final VoidCallback onParticipate;
|
||||
final VoidCallback onCloseEvent;
|
||||
final VoidCallback onReopenEvent;
|
||||
final Function onRemoveEvent;
|
||||
/// Widget représentant une carte d'événement affichant les informations
|
||||
/// principales de l'événement avec diverses options d'interaction.
|
||||
class EventCard extends StatefulWidget {
|
||||
final EventModel event; // Modèle de données pour l'événement.
|
||||
final String userId; // ID de l'utilisateur affichant l'événement.
|
||||
final String userFirstName; // Prénom de l'utilisateur.
|
||||
final String userLastName; // Nom de l'utilisateur.
|
||||
final String profileImageUrl; // Image de profile
|
||||
final String status; // Statut de l'événement (ouvert ou fermé).
|
||||
final VoidCallback onReact; // Callback pour réagir à l'événement.
|
||||
final VoidCallback onComment; // Callback pour commenter l'événement.
|
||||
final VoidCallback onShare; // Callback pour partager l'événement.
|
||||
final VoidCallback onParticipate; // Callback pour participer à l'événement.
|
||||
final VoidCallback onCloseEvent; // Callback pour fermer l'événement.
|
||||
final VoidCallback onReopenEvent; // Callback pour rouvrir l'événement.
|
||||
final Function onRemoveEvent; // Fonction pour supprimer l'événement.
|
||||
|
||||
const EventCard({
|
||||
Key? key,
|
||||
@@ -27,6 +31,7 @@ class EventCard extends StatelessWidget {
|
||||
required this.userId,
|
||||
required this.userFirstName,
|
||||
required this.userLastName,
|
||||
required this.profileImageUrl,
|
||||
required this.status,
|
||||
required this.onReact,
|
||||
required this.onComment,
|
||||
@@ -37,79 +42,142 @@ class EventCard extends StatelessWidget {
|
||||
required this.onRemoveEvent,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_EventCardState createState() => _EventCardState();
|
||||
}
|
||||
|
||||
class _EventCardState extends State<EventCard> {
|
||||
bool _isExpanded = false; // Contrôle si la description est développée.
|
||||
static const int _descriptionThreshold = 100; // Limite de caractères.
|
||||
bool _isClosed = false; // Ajout d'une variable pour suivre l'état de l'événement.
|
||||
final Logger _logger = Logger();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_isClosed = widget.event.status == 'fermé'; // Initialiser l'état selon le statut de l'événement.
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final GlobalKey menuKey = GlobalKey();
|
||||
_logger.i("Construction de la carte d'événement"); // Log pour la construction du widget.
|
||||
final GlobalKey menuKey = GlobalKey(); // Clé pour le menu contextuel.
|
||||
final String descriptionText = widget.event.description; // Description de l'événement.
|
||||
final bool shouldTruncate = descriptionText.length > _descriptionThreshold; // Détermine si le texte doit être tronqué.
|
||||
|
||||
return Dismissible(
|
||||
key: ValueKey(event.id),
|
||||
direction: event.status == 'fermé'
|
||||
key: ValueKey(widget.event.id), // Clé unique pour chaque carte d'événement.
|
||||
direction: widget.event.status == 'fermé' // Direction du glissement basée sur le statut.
|
||||
? DismissDirection.startToEnd
|
||||
: DismissDirection.endToStart,
|
||||
onDismissed: (direction) {
|
||||
if (event.status == 'fermé') {
|
||||
onReopenEvent();
|
||||
onDismissed: (direction) { // Action déclenchée lors d'un glissement.
|
||||
if (_isClosed) {
|
||||
_logger.i("Rouverte de l'événement ${widget.event.id}");
|
||||
widget.onReopenEvent();
|
||||
setState(() {
|
||||
_isClosed = false; // Mise à jour de l'état local.
|
||||
});
|
||||
} else {
|
||||
onCloseEvent();
|
||||
onRemoveEvent(event.id);
|
||||
_logger.i("Fermeture de l'événement ${widget.event.id}");
|
||||
widget.onCloseEvent();
|
||||
widget.onRemoveEvent(widget.event.id); // Suppression de l'événement.
|
||||
setState(() {
|
||||
_isClosed = true; // Mise à jour de l'état local.
|
||||
});
|
||||
}
|
||||
},
|
||||
background: SwipeBackground(
|
||||
color: event.status == 'fermé' ? Colors.green : Colors.red,
|
||||
icon: event.status == 'fermé' ? Icons.lock_open : Icons.lock,
|
||||
label: event.status == 'fermé' ? 'Rouvrir' : 'Fermer',
|
||||
background: SwipeBackground( // Arrière-plan pour les actions de glissement.
|
||||
color: _isClosed ? Colors.green : Colors.red,
|
||||
icon: _isClosed ? Icons.lock_open : Icons.lock,
|
||||
label: _isClosed ? 'Rouvrir' : 'Fermer',
|
||||
),
|
||||
child: Card(
|
||||
color: const Color(0xFF2C2C3E),
|
||||
color: const Color(0xFF2C2C3E), // Couleur de fond de la carte.
|
||||
margin: const EdgeInsets.symmetric(vertical: 10.0),
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), // Bordure arrondie.
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
padding: const EdgeInsets.all(12.0), // Marge intérieure de la carte.
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Affichage de l'en-tête de l'événement.
|
||||
EventHeader(
|
||||
userFirstName: userFirstName,
|
||||
userLastName: userLastName,
|
||||
eventDate: event.startDate,
|
||||
imageUrl: event.imageUrl,
|
||||
creatorFirstName: widget.event.creatorFirstName,
|
||||
creatorLastName: widget.event.creatorLastName,
|
||||
profileImageUrl: widget.event.profileImageUrl,
|
||||
eventDate: widget.event.startDate,
|
||||
imageUrl: widget.event.imageUrl,
|
||||
menuKey: menuKey,
|
||||
menuContext: context,
|
||||
location: event.location,
|
||||
onClose: () { },
|
||||
location: widget.event.location,
|
||||
onClose: () {
|
||||
_logger.i("Menu de fermeture actionné pour l'événement ${widget.event.id}");
|
||||
},
|
||||
),
|
||||
const Divider(color: Colors.white24),
|
||||
const Divider(color: Colors.white24), // Ligne de séparation visuelle.
|
||||
|
||||
Row(
|
||||
children: [
|
||||
const Spacer(), // Pusher le badge statut à la droite.
|
||||
EventStatusBadge(status: status),
|
||||
const Spacer(), // Pousse le badge de statut à droite.
|
||||
EventStatusBadge(status: widget.status), // Badge de statut.
|
||||
],
|
||||
),
|
||||
|
||||
Text(
|
||||
event.title,
|
||||
widget.event.title, // Titre de l'événement.
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
event.description,
|
||||
style: const TextStyle(color: Colors.white70, fontSize: 14),
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
const SizedBox(height: 5), // Espacement entre le titre et la description.
|
||||
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_isExpanded = !_isExpanded; // Change l'état d'expansion.
|
||||
});
|
||||
_logger.i("Changement d'état d'expansion pour la description de l'événement ${widget.event.id}");
|
||||
},
|
||||
child: Text(
|
||||
_isExpanded || !shouldTruncate
|
||||
? descriptionText
|
||||
: "${descriptionText.substring(0, _descriptionThreshold)}...",
|
||||
style: const TextStyle(color: Colors.white70, fontSize: 14),
|
||||
maxLines: _isExpanded ? null : 3,
|
||||
overflow: _isExpanded ? TextOverflow.visible : TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
EventImage(imageUrl: event.imageUrl),
|
||||
const Divider(color: Colors.white24),
|
||||
if (shouldTruncate) // Bouton "Afficher plus" si la description est longue.
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_isExpanded = !_isExpanded;
|
||||
});
|
||||
_logger.i("Affichage de la description complète de l'événement ${widget.event.id}");
|
||||
},
|
||||
child: Text(
|
||||
_isExpanded ? "Afficher moins" : "Afficher plus",
|
||||
style: const TextStyle(
|
||||
color: Colors.blue,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10), // Espacement avant l'image.
|
||||
|
||||
EventImage(imageUrl: widget.event.imageUrl), // Affichage de l'image de l'événement.
|
||||
const Divider(color: Colors.white24), // Nouvelle ligne de séparation.
|
||||
|
||||
// Rangée pour les interactions de l'événement (réagir, commenter, partager).
|
||||
EventInteractionRow(
|
||||
onReact: onReact,
|
||||
onComment: onComment,
|
||||
onShare: onShare,
|
||||
reactionsCount: event.reactionsCount,
|
||||
commentsCount: event.commentsCount,
|
||||
sharesCount: event.sharesCount,
|
||||
onReact: widget.onReact,
|
||||
onComment: widget.onComment,
|
||||
onShare: widget.onShare,
|
||||
reactionsCount: widget.event.reactionsCount,
|
||||
commentsCount: widget.event.commentsCount,
|
||||
sharesCount: widget.event.sharesCount,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -118,3 +186,4 @@ class EventCard extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,12 +10,14 @@ class EventScreen extends StatefulWidget {
|
||||
final String userId;
|
||||
final String userFirstName;
|
||||
final String userLastName;
|
||||
final String profileImageUrl;
|
||||
|
||||
const EventScreen({
|
||||
Key? key,
|
||||
required this.userId,
|
||||
required this.userFirstName,
|
||||
required this.userLastName,
|
||||
required this.profileImageUrl,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -84,6 +86,7 @@ class _EventScreenState extends State<EventScreen> {
|
||||
userId: widget.userId,
|
||||
userFirstName: widget.userFirstName,
|
||||
userLastName: widget.userLastName,
|
||||
profileImageUrl: widget.profileImageUrl,
|
||||
onReact: () => _onReact(event.id),
|
||||
onComment: () => _onComment(event.id),
|
||||
onShare: () => _onShare(event.id),
|
||||
@@ -143,7 +146,7 @@ class _EventScreenState extends State<EventScreen> {
|
||||
|
||||
void _onCloseEvent(String eventId) {
|
||||
print('Fermeture de l\'événement $eventId');
|
||||
// Appeler le bloc pour fermer l'événement
|
||||
// Appeler le bloc pour fermer l'événement sans recharger la liste entière.
|
||||
context.read<EventBloc>().add(CloseEvent(eventId));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('L\'événement a été fermé avec succès.')),
|
||||
@@ -152,10 +155,12 @@ class _EventScreenState extends State<EventScreen> {
|
||||
|
||||
void _onReopenEvent(String eventId) {
|
||||
print('Réouverture de l\'événement $eventId');
|
||||
// Appeler le bloc pour rouvrir l'événement
|
||||
// Appeler le bloc pour rouvrir l'événement sans recharger la liste entière.
|
||||
context.read<EventBloc>().add(ReopenEvent(eventId));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('L\'événement a été rouvert avec succès.')),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../widgets/friend_card.dart';
|
||||
import '../../widgets/friend_detail_screen.dart';
|
||||
|
||||
class FriendsContent extends StatelessWidget {
|
||||
final List<Map<String, String>> friends = [
|
||||
{'name': 'Alice', 'imageUrl': 'https://example.com/image1.jpg'},
|
||||
{'name': 'Bob', 'imageUrl': 'https://example.com/image2.jpg'},
|
||||
{'name': 'Charlie', 'imageUrl': 'https://example.com/image3.jpg'},
|
||||
// Autres amis...
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0),
|
||||
itemCount: friends.length,
|
||||
itemBuilder: (context, index) {
|
||||
final friend = friends[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||
child: FriendCard(
|
||||
name: friend['name']!,
|
||||
imageUrl: friend['imageUrl']!,
|
||||
onTap: () => _navigateToFriendDetail(context, friend),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _navigateToFriendDetail(BuildContext context, Map<String, String> friend) {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => FriendDetailScreen(
|
||||
name: friend['name']!,
|
||||
imageUrl: friend['imageUrl']!,
|
||||
friendId: friend['friendId']!,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../../data/providers/friends_provider.dart';
|
||||
import '../../../domain/entities/friend.dart';
|
||||
import '../../widgets/friend_detail_screen.dart';
|
||||
import '../../widgets/friends_circle.dart';
|
||||
import '../../widgets/search_friends.dart';
|
||||
|
||||
/// [FriendsScreen] est l'écran principal permettant d'afficher et de gérer la liste des amis.
|
||||
/// Il inclut des fonctionnalités de pagination, de recherche, et de rafraîchissement manuel de la liste.
|
||||
/// Ce widget est un [StatefulWidget] afin de pouvoir mettre à jour dynamiquement la liste des amis.
|
||||
class FriendsScreen extends StatefulWidget {
|
||||
final String userId; // Identifiant de l'utilisateur pour récupérer ses amis
|
||||
|
||||
@@ -28,7 +29,7 @@ class _FriendsScreenState extends State<FriendsScreen> {
|
||||
|
||||
// Log pour indiquer le début du chargement des amis
|
||||
debugPrint("[LOG] Initialisation de la page : chargement des amis pour l'utilisateur ${widget.userId}");
|
||||
// Chargement initial de la liste d'amis
|
||||
// Chargement initial de la liste d'amis via le fournisseur (Provider)
|
||||
Provider.of<FriendsProvider>(context, listen: false).fetchFriends(widget.userId);
|
||||
}
|
||||
|
||||
@@ -46,12 +47,12 @@ class _FriendsScreenState extends State<FriendsScreen> {
|
||||
void _onScroll() {
|
||||
final provider = Provider.of<FriendsProvider>(context, listen: false);
|
||||
|
||||
// Ajout d'une marge de 200 pixels pour détecter le bas de la liste plus tôt
|
||||
// Ajout d'une marge de 200 pixels pour détecter le bas de la liste plus tôt.
|
||||
if (_scrollController.position.pixels >=
|
||||
_scrollController.position.maxScrollExtent - 200 &&
|
||||
!provider.isLoading && provider.hasMore) {
|
||||
debugPrint("[LOG] Scroll : Fin de liste atteinte, chargement de la page suivante.");
|
||||
provider.fetchFriends(widget.userId, loadMore: true);
|
||||
provider.fetchFriends(widget.userId, loadMore: true); // Chargement de plus d'amis
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,9 +65,11 @@ class _FriendsScreenState extends State<FriendsScreen> {
|
||||
appBar: AppBar(
|
||||
title: const Text('Mes Amis'),
|
||||
actions: [
|
||||
// Bouton pour rafraîchir la liste des amis
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
// Vérifie si la liste n'est pas en cours de chargement avant d'envoyer une nouvelle requête.
|
||||
if (!friendsProvider.isLoading) {
|
||||
debugPrint("[LOG] Bouton Refresh : demande de rafraîchissement de la liste des amis");
|
||||
friendsProvider.fetchFriends(widget.userId);
|
||||
@@ -80,13 +83,13 @@ class _FriendsScreenState extends State<FriendsScreen> {
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
// Widget de recherche d'amis en haut de l'écran
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
// Widget pour la recherche d'amis
|
||||
child: SearchFriends(),
|
||||
),
|
||||
Expanded(
|
||||
// Construction de la liste d'amis basée sur l'état du FriendsProvider
|
||||
// Construction de la liste d'amis avec un affichage en grille
|
||||
child: Consumer<FriendsProvider>(
|
||||
builder: (context, friendsProvider, child) {
|
||||
// Si le chargement est en cours et qu'il n'y a aucun ami, afficher un indicateur de chargement.
|
||||
@@ -103,40 +106,69 @@ class _FriendsScreenState extends State<FriendsScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
// Affichage de la grille des amis
|
||||
debugPrint("[LOG] Affichage de la grille des amis (nombre d'amis : ${friendsProvider.friendsList.length})");
|
||||
|
||||
return GridView.builder(
|
||||
controller: _scrollController,
|
||||
padding: const EdgeInsets.all(16),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 10,
|
||||
controller: _scrollController, // Utilisation du contrôleur pour la pagination
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2, // Deux amis par ligne
|
||||
crossAxisSpacing: 10,
|
||||
mainAxisSpacing: 10,
|
||||
childAspectRatio: 0.8, // Ajuste la taille des cartes
|
||||
),
|
||||
itemCount: friendsProvider.friendsList.length + (friendsProvider.isLoading && friendsProvider.hasMore ? 1 : 0),
|
||||
itemCount: friendsProvider.friendsList.length,
|
||||
itemBuilder: (context, index) {
|
||||
if (index >= friendsProvider.friendsList.length) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
final friend = friendsProvider.friendsList[index];
|
||||
debugPrint("[LOG] Affichage de l'ami à l'index $index avec ID : ${friend.friendId}");
|
||||
|
||||
return FriendsCircle(
|
||||
friend: friend,
|
||||
onTap: () {
|
||||
debugPrint("[LOG] Détail : Affichage des détails de l'ami ID : ${friend.friendId}");
|
||||
FriendDetailScreen.open(
|
||||
context,
|
||||
friend.friendId,
|
||||
friend.friendFirstName,
|
||||
friend.imageUrl ?? '',
|
||||
);
|
||||
},
|
||||
// Affichage de chaque ami dans une carte avec une animation
|
||||
return GestureDetector(
|
||||
onTap: () => _navigateToFriendDetail(context, friend), // Action au clic sur l'avatar
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
transform: Matrix4.identity()
|
||||
..scale(1.05), // Effet de zoom lors du survol
|
||||
child: Card(
|
||||
elevation: 6,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 50,
|
||||
backgroundImage: friend.imageUrl != null && friend.imageUrl!.isNotEmpty
|
||||
? (friend.imageUrl!.startsWith('https') // Vérifie si l'image est une URL réseau.
|
||||
? NetworkImage(friend.imageUrl!) // Charge l'image depuis une URL réseau.
|
||||
: AssetImage(friend.imageUrl!) as ImageProvider) // Sinon, charge depuis les ressources locales.
|
||||
: const AssetImage('lib/assets/images/default_avatar.png'), // Si aucune image, utilise l'image par défaut.
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"${friend.friendFirstName} ${friend.friendLastName}",
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
friend.status.name,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
friend.lastInteraction ?? 'Aucune interaction récente',
|
||||
style: const TextStyle(
|
||||
fontStyle: FontStyle.italic,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -145,4 +177,24 @@ class _FriendsScreenState extends State<FriendsScreen> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Navigation vers l'écran de détails de l'ami
|
||||
/// Permet de voir les informations complètes d'un ami lorsque l'utilisateur clique sur son avatar.
|
||||
void _navigateToFriendDetail(BuildContext context, Friend friend) {
|
||||
debugPrint("[LOG] Navigation : Détails de l'ami ${friend.friendFirstName} ${friend.friendLastName}");
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => FriendDetailScreen(
|
||||
friendFirstName: friend.friendFirstName, // Prénom de l'ami
|
||||
friendLastName: friend.friendLastName, // Nom de l'ami
|
||||
imageUrl: friend.imageUrl ?? '', // URL de l'image de l'ami (ou valeur par défaut)
|
||||
friendId: friend.friendId, // Identifiant unique de l'ami
|
||||
status: friend.status, // Statut de l'ami
|
||||
lastInteraction: friend.lastInteraction ?? 'Aucune', // Dernière interaction (si disponible)
|
||||
dateAdded: friend.dateAdded ?? 'Inconnu', // Date d'ajout de l'ami (si disponible)
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,20 +8,21 @@ import '../../widgets/friend_detail_screen.dart';
|
||||
import '../../widgets/friends_appbar.dart';
|
||||
import '../../widgets/search_friends.dart';
|
||||
|
||||
/// Écran d'affichage des amis avec gestion des amis via un provider.
|
||||
class FriendsScreenWithProvider extends StatelessWidget {
|
||||
final Logger _logger = Logger(); // Logger pour une meilleure traçabilité
|
||||
final Logger _logger = Logger(); // Logger pour la traçabilité détaillée.
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
appBar: FriendsAppBar(),
|
||||
backgroundColor: Colors.black, // Fond noir pour une ambiance immersive.
|
||||
appBar: FriendsAppBar(), // AppBar personnalisé pour l'écran.
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: SearchFriends(),
|
||||
child: SearchFriends(), // Barre de recherche pour trouver des amis.
|
||||
),
|
||||
Expanded(
|
||||
child: Consumer<FriendsProvider>(
|
||||
@@ -29,10 +30,10 @@ class FriendsScreenWithProvider extends StatelessWidget {
|
||||
final friends = friendsProvider.friendsList;
|
||||
|
||||
if (friends.isEmpty) {
|
||||
_logger.i("[LOG] Aucun ami trouvé");
|
||||
_logger.i("[LOG] Aucun ami trouvé."); // Log pour la recherche sans résultat.
|
||||
return const Center(
|
||||
child: Text(
|
||||
'Aucun ami trouvé',
|
||||
'Aucun ami trouvé', // Message affiché si aucun ami n'est trouvé.
|
||||
style: TextStyle(color: Colors.white70),
|
||||
),
|
||||
);
|
||||
@@ -43,6 +44,10 @@ class FriendsScreenWithProvider extends StatelessWidget {
|
||||
itemCount: friends.length,
|
||||
itemBuilder: (context, index) {
|
||||
final friend = friends[index];
|
||||
|
||||
// Log lorsque chaque ami est affiché
|
||||
_logger.i("[LOG] Affichage de l'ami : ${friend.friendFirstName ?? 'Ami inconnu'}");
|
||||
|
||||
return Dismissible(
|
||||
key: Key(friend.friendId),
|
||||
background: Container(
|
||||
@@ -53,7 +58,7 @@ class FriendsScreenWithProvider extends StatelessWidget {
|
||||
),
|
||||
onDismissed: (direction) {
|
||||
_logger.i("[LOG] Suppression de l'ami avec l'ID : ${friend.friendId}");
|
||||
friendsProvider.removeFriend(friend.friendId);
|
||||
friendsProvider.removeFriend(friend.friendId); // Suppression de l'ami via le provider.
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("Ami supprimé : ${friend.friendFirstName}")),
|
||||
);
|
||||
@@ -61,14 +66,16 @@ class FriendsScreenWithProvider extends StatelessWidget {
|
||||
child: FriendExpandingCard(
|
||||
name: friend.friendFirstName ?? 'Ami inconnu',
|
||||
imageUrl: friend.imageUrl ?? '',
|
||||
description: "Amis depuis ${friend.friendId}",
|
||||
onTap: () => _navigateToFriendDetail(context, friend),
|
||||
description: "Amis depuis ${friend.dateAdded ?? 'Inconnu'}\nStatut : ${friend.status ?? 'Inconnu'}",
|
||||
onTap: () {
|
||||
_navigateToFriendDetail(context, friend); // Navigation vers les détails de l'ami.
|
||||
},
|
||||
onMessageTap: () {
|
||||
_logger.i("[LOG] Envoi d'un message à l'ami : ${friend.friendFirstName ?? 'Ami inconnu'}");
|
||||
},
|
||||
onRemoveTap: () {
|
||||
_logger.i("[LOG] Tentative de suppression de l'ami : ${friend.friendFirstName ?? 'Ami inconnu'}");
|
||||
friendsProvider.removeFriend(friend.friendId);
|
||||
friendsProvider.removeFriend(friend.friendId); // Suppression via le provider.
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -83,13 +90,19 @@ class FriendsScreenWithProvider extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
/// Navigue vers l'écran de détails de l'ami.
|
||||
void _navigateToFriendDetail(BuildContext context, Friend friend) {
|
||||
_logger.i("[LOG] Navigation vers les détails de l'ami : ${friend.friendFirstName}");
|
||||
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => FriendDetailScreen(
|
||||
name: friend.friendFirstName,
|
||||
imageUrl: friend.imageUrl ?? '',
|
||||
friendId: friend.friendId,
|
||||
friendFirstName: friend.friendFirstName,
|
||||
friendLastName: friend.friendLastName,
|
||||
imageUrl: friend.imageUrl ?? '',
|
||||
status: friend.status,
|
||||
lastInteraction: friend.lastInteraction ?? 'Aucune',
|
||||
dateAdded: friend.dateAdded ?? 'Inconnu',
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../core/constants/colors.dart'; // Importez les couleurs dynamiques
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../../core/constants/colors.dart';
|
||||
import '../../../core/theme/theme_provider.dart';
|
||||
import '../../widgets/friend_suggestions.dart';
|
||||
import '../../widgets/group_list.dart';
|
||||
import '../../widgets/popular_activity_list.dart';
|
||||
@@ -7,45 +9,52 @@ import '../../widgets/recommended_event_list.dart';
|
||||
import '../../widgets/section_header.dart';
|
||||
import '../../widgets/story_section.dart';
|
||||
|
||||
/// Écran principal du contenu d'accueil, affichant diverses sections telles que
|
||||
/// les suggestions d'amis, les activités populaires, les groupes, etc.
|
||||
/// Les couleurs s'adaptent dynamiquement au thème sélectionné (clair ou sombre).
|
||||
class HomeContentScreen extends StatelessWidget {
|
||||
const HomeContentScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Récupération du fournisseur de thème pour appliquer le mode jour/nuit
|
||||
final themeProvider = Provider.of<ThemeProvider>(context);
|
||||
// Obtention des dimensions de l'écran pour adapter la mise en page
|
||||
final size = MediaQuery.of(context).size;
|
||||
print("Chargement de HomeContentScreen avec le thème actuel");
|
||||
|
||||
return SingleChildScrollView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 15.0), // Marges réduites
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 15.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Section de bienvenue
|
||||
_buildWelcomeCard(),
|
||||
// Carte de bienvenue avec couleurs dynamiques
|
||||
_buildWelcomeCard(themeProvider),
|
||||
const SizedBox(height: 15), // Espacement entre les sections
|
||||
|
||||
const SizedBox(height: 15), // Espacement vertical réduit
|
||||
|
||||
// Section "Moments populaires"
|
||||
// Section des "Moments populaires"
|
||||
_buildCard(
|
||||
context: context,
|
||||
themeProvider: themeProvider, // Fournit le thème
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SectionHeader(
|
||||
title: 'Moments populaires',
|
||||
icon: Icons.camera_alt,
|
||||
textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), // Taille ajustée
|
||||
textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||
),
|
||||
const SizedBox(height: 10), // Espace vertical réduit
|
||||
const SizedBox(height: 10),
|
||||
StorySection(size: size),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
const SizedBox(height: 15), // Espacement réduit
|
||||
|
||||
// Section des événements recommandés
|
||||
// Section des "Événements recommandés"
|
||||
_buildCard(
|
||||
context: context,
|
||||
themeProvider: themeProvider,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -54,17 +63,17 @@ class HomeContentScreen extends StatelessWidget {
|
||||
icon: Icons.star,
|
||||
textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||
),
|
||||
const SizedBox(height: 10), // Espacement réduit
|
||||
const SizedBox(height: 10),
|
||||
RecommendedEventList(size: size),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
const SizedBox(height: 15), // Espacement réduit
|
||||
|
||||
// Section des activités populaires
|
||||
// Section des "Activités populaires"
|
||||
_buildCard(
|
||||
context: context,
|
||||
themeProvider: themeProvider,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -73,17 +82,17 @@ class HomeContentScreen extends StatelessWidget {
|
||||
icon: Icons.local_activity,
|
||||
textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||
),
|
||||
const SizedBox(height: 10), // Espacement réduit
|
||||
const SizedBox(height: 10),
|
||||
PopularActivityList(size: size),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
const SizedBox(height: 15), // Espacement réduit
|
||||
|
||||
// Section des groupes sociaux
|
||||
// Section "Groupes à rejoindre"
|
||||
_buildCard(
|
||||
context: context,
|
||||
themeProvider: themeProvider,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -92,17 +101,17 @@ class HomeContentScreen extends StatelessWidget {
|
||||
icon: Icons.group_add,
|
||||
textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||
),
|
||||
const SizedBox(height: 10), // Espacement réduit
|
||||
const SizedBox(height: 10),
|
||||
GroupList(size: size),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
|
||||
const SizedBox(height: 15), // Espacement réduit
|
||||
|
||||
// Section des suggestions d'amis
|
||||
// Section des "Suggestions d'amis"
|
||||
_buildCard(
|
||||
context: context,
|
||||
themeProvider: themeProvider,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -111,7 +120,7 @@ class HomeContentScreen extends StatelessWidget {
|
||||
icon: Icons.person_add,
|
||||
textStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||
),
|
||||
const SizedBox(height: 10), // Espacement réduit
|
||||
const SizedBox(height: 10),
|
||||
FriendSuggestions(size: size),
|
||||
],
|
||||
),
|
||||
@@ -121,11 +130,13 @@ class HomeContentScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
// Widget pour la carte de bienvenue
|
||||
Widget _buildWelcomeCard() {
|
||||
/// Crée la carte de bienvenue, en utilisant les couleurs dynamiques en fonction du thème sélectionné.
|
||||
/// [themeProvider] fournit l'état actuel du thème pour adapter les couleurs.
|
||||
Widget _buildWelcomeCard(ThemeProvider themeProvider) {
|
||||
print("Création de la carte de bienvenue avec le thème actuel");
|
||||
return Card(
|
||||
elevation: 5,
|
||||
color: AppColors.surface, // Utilisation de la couleur dynamique pour la surface
|
||||
color: themeProvider.isDarkMode ? AppColors.darkSurface : AppColors.lightSurface,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
@@ -135,26 +146,32 @@ class HomeContentScreen extends StatelessWidget {
|
||||
Text(
|
||||
'Bienvenue, Dahoud!',
|
||||
style: TextStyle(
|
||||
color: AppColors.textPrimary, // Texte dynamique
|
||||
fontSize: 22, // Taille de police réduite
|
||||
fontWeight: FontWeight.w600, // Poids de police ajusté
|
||||
color: themeProvider.isDarkMode ? AppColors.darkOnPrimary : AppColors.lightPrimary,
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
Icon(Icons.waving_hand, color: Colors.orange.shade300, size: 24), // Taille de l'icône ajustée
|
||||
Icon(Icons.waving_hand, color: Colors.orange.shade300, size: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Widget générique pour créer une carte design avec des espaces optimisés
|
||||
Widget _buildCard({required BuildContext context, required Widget child}) {
|
||||
/// Crée une carte générique pour afficher des sections avec un style uniforme.
|
||||
/// [themeProvider] est utilisé pour ajuster les couleurs de la carte selon le mode jour/nuit.
|
||||
Widget _buildCard({
|
||||
required BuildContext context,
|
||||
required ThemeProvider themeProvider,
|
||||
required Widget child,
|
||||
}) {
|
||||
print("Création d'une carte de section avec le thème actuel");
|
||||
return Card(
|
||||
elevation: 3, // Réduction de l'élévation pour un look plus épuré
|
||||
color: AppColors.surface, // Utilisation de la couleur dynamique pour la surface
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), // Coins légèrement arrondis
|
||||
elevation: 3,
|
||||
color: themeProvider.isDarkMode ? AppColors.darkSurface : AppColors.lightSurface,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0), // Padding interne réduit pour un contenu plus compact
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart'; // Pour ThemeProvider
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:afterwork/presentation/screens/event/event_screen.dart';
|
||||
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';
|
||||
import 'package:afterwork/presentation/screens/notifications/notifications_screen.dart'; // Écran de notifications
|
||||
import 'package:afterwork/presentation/screens/notifications/notifications_screen.dart';
|
||||
import '../../../core/constants/colors.dart';
|
||||
import '../../../core/theme/theme_provider.dart';
|
||||
import '../friends/friends_screen.dart'; // Écran des amis
|
||||
import '../friends/friends_screen.dart';
|
||||
|
||||
class HomeScreen extends StatefulWidget {
|
||||
final EventRemoteDataSource eventRemoteDataSource;
|
||||
final String userId;
|
||||
final String userFirstName;
|
||||
final String userLastName;
|
||||
final String userProfileImage; // Image de profil de l'utilisateur
|
||||
final String userProfileImage;
|
||||
|
||||
const HomeScreen({
|
||||
Key? key,
|
||||
@@ -24,7 +24,7 @@ class HomeScreen extends StatefulWidget {
|
||||
required this.userId,
|
||||
required this.userFirstName,
|
||||
required this.userLastName,
|
||||
required this.userProfileImage, // Passer l'image de profil ici
|
||||
required this.userProfileImage,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -37,7 +37,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_tabController = TabController(length: 6, vsync: this); // Ajouter un onglet pour les notifications
|
||||
_tabController = TabController(length: 6, vsync: this);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -47,51 +47,40 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
}
|
||||
|
||||
void _onMenuSelected(BuildContext context, String option) {
|
||||
switch (option) {
|
||||
case 'Publier':
|
||||
print('Publier sélectionné');
|
||||
break;
|
||||
case 'Story':
|
||||
print('Story sélectionné');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
print('$option sélectionné'); // Log pour chaque option
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Accès au ThemeProvider
|
||||
final themeProvider = Provider.of<ThemeProvider>(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
body: NestedScrollView(
|
||||
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
||||
return <Widget>[
|
||||
SliverAppBar(
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
floating: true,
|
||||
pinned: true,
|
||||
snap: true,
|
||||
elevation: 2,
|
||||
backgroundColor: themeProvider.currentTheme.primaryColor,
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.all(4.0), // Ajustement du padding
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Image.asset(
|
||||
'lib/assets/images/logo.png',
|
||||
height: 40, // Taille ajustée du logo
|
||||
height: 40,
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
_buildActionIcon(Icons.add, 'Publier', context),
|
||||
_buildActionIcon(Icons.search, 'Rechercher', context),
|
||||
_buildActionIcon(Icons.message, 'Message', context),
|
||||
_buildNotificationsIcon(context, 5), // Gérer la logique des notifications ici
|
||||
|
||||
// Bouton pour basculer entre les thèmes
|
||||
_buildNotificationsIcon(context, 105),
|
||||
Switch(
|
||||
value: themeProvider.isDarkMode,
|
||||
onChanged: (value) {
|
||||
themeProvider.toggleTheme(); // Changer le thème
|
||||
themeProvider.toggleTheme();
|
||||
},
|
||||
activeColor: AppColors.accentColor,
|
||||
),
|
||||
@@ -99,23 +88,17 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
bottom: TabBar(
|
||||
controller: _tabController,
|
||||
indicatorColor: AppColors.lightPrimary,
|
||||
labelStyle: const TextStyle(
|
||||
fontSize: 12, // Taille réduite du texte
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
unselectedLabelStyle: const TextStyle(
|
||||
fontSize: 11, // Taille ajustée pour les onglets non sélectionnés
|
||||
),
|
||||
labelColor: AppColors.lightPrimary,
|
||||
unselectedLabelColor: AppColors.iconSecondary,
|
||||
|
||||
labelStyle: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
|
||||
unselectedLabelStyle: const TextStyle(fontSize: 11),
|
||||
labelColor: themeProvider.isDarkMode ? AppColors.darkOnPrimary : AppColors.lightOnPrimary,
|
||||
unselectedLabelColor: themeProvider.isDarkMode ? AppColors.darkIconSecondary : AppColors.lightIconSecondary,
|
||||
tabs: [
|
||||
const Tab(icon: Icon(Icons.home, size: 24), text: 'Accueil'),
|
||||
const Tab(icon: Icon(Icons.event, size: 24), text: 'Événements'),
|
||||
const Tab(icon: Icon(Icons.location_city, size: 24), text: 'Établissements'),
|
||||
const Tab(icon: Icon(Icons.people, size: 24), text: 'Social'),
|
||||
const Tab(icon: Icon(Icons.people_alt_outlined, size: 24), text: 'Ami(e)s'),
|
||||
_buildProfileTab(), // Onglet profil
|
||||
_buildProfileTab(),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -129,10 +112,11 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
userId: widget.userId,
|
||||
userFirstName: widget.userFirstName,
|
||||
userLastName: widget.userLastName,
|
||||
profileImageUrl: widget.userProfileImage,
|
||||
),
|
||||
const EstablishmentsScreen(),
|
||||
const SocialScreen(),
|
||||
FriendsScreen(userId: widget.userId), // Correction ici : passer l'userId
|
||||
FriendsScreen(userId: widget.userId),
|
||||
const ProfileScreen(),
|
||||
],
|
||||
),
|
||||
@@ -140,20 +124,19 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
);
|
||||
}
|
||||
|
||||
// Widget pour l'affichage de la photo de profil dans l'onglet
|
||||
Tab _buildProfileTab() {
|
||||
return Tab(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: Colors.blue,
|
||||
color: AppColors.secondary,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
child: CircleAvatar(
|
||||
radius: 16,
|
||||
backgroundColor: Colors.grey[200], // Couleur de fond par défaut
|
||||
backgroundColor: AppColors.surface,
|
||||
child: ClipOval(
|
||||
child: FadeInImage.assetNetwork(
|
||||
placeholder: 'lib/assets/images/user_placeholder.png',
|
||||
@@ -169,18 +152,17 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
);
|
||||
}
|
||||
|
||||
// Icône pour les notifications avec un badge
|
||||
Widget _buildNotificationsIcon(BuildContext context, int notificationCount) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6.0),
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none, // Permet d'afficher le badge en dehors des limites
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
CircleAvatar(
|
||||
backgroundColor: AppColors.surface,
|
||||
radius: 18,
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.notifications, color: AppColors.darkOnPrimary, size: 20),
|
||||
icon: Icon(Icons.notifications, color: AppColors.iconPrimary, size: 20),
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
@@ -197,21 +179,17 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
top: -6,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(2),
|
||||
decoration: const BoxDecoration(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: 18,
|
||||
minHeight: 18,
|
||||
),
|
||||
child: Text(
|
||||
notificationCount > 99 ? '99+' : '$notificationCount',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
@@ -221,7 +199,6 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
);
|
||||
}
|
||||
|
||||
// Icône d'action générique
|
||||
Widget _buildActionIcon(IconData iconData, String label, BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6.0),
|
||||
@@ -229,7 +206,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
backgroundColor: AppColors.surface,
|
||||
radius: 18,
|
||||
child: IconButton(
|
||||
icon: Icon(iconData, color: AppColors.darkOnPrimary, size: 20),
|
||||
icon: Icon(iconData, color: AppColors.iconPrimary, size: 20),
|
||||
onPressed: () {
|
||||
_onMenuSelected(context, label);
|
||||
},
|
||||
|
||||
@@ -2,14 +2,14 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../../core/constants/colors.dart';
|
||||
import '../../../data/providers/user_provider.dart';
|
||||
import '../../widgets/account_deletion_card.dart';
|
||||
import '../../widgets/cards/account_deletion_card.dart';
|
||||
import '../../widgets/cards/statistics_section_card.dart';
|
||||
import '../../widgets/cards/support_section_card.dart';
|
||||
import '../../widgets/custom_list_tile.dart';
|
||||
import '../../widgets/edit_options_card.dart';
|
||||
import '../../widgets/expandable_section_card.dart';
|
||||
import '../../widgets/cards/edit_options_card.dart';
|
||||
import '../../widgets/cards/expandable_section_card.dart';
|
||||
import '../../widgets/profile_header.dart';
|
||||
import '../../widgets/statistics_section_card.dart';
|
||||
import '../../widgets/support_section_card.dart';
|
||||
import '../../widgets/user_info_card.dart';
|
||||
import '../../widgets/cards/user_info_card.dart';
|
||||
|
||||
class ProfileScreen extends StatelessWidget {
|
||||
const ProfileScreen({super.key});
|
||||
|
||||
Reference in New Issue
Block a user