refactoring
This commit is contained in:
@@ -77,6 +77,7 @@ class EventCard extends StatelessWidget {
|
||||
menuKey: menuKey,
|
||||
menuContext: context,
|
||||
location: event.location,
|
||||
onClose: () { },
|
||||
),
|
||||
const Divider(color: Colors.white24),
|
||||
Row(
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:afterwork/presentation/screens/event/event_card.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:afterwork/data/models/event_model.dart';
|
||||
import 'package:afterwork/presentation/screens/event/event_card.dart';
|
||||
import '../dialogs/add_event_dialog.dart';
|
||||
|
||||
import '../../state_management/event_bloc.dart';
|
||||
import '../dialogs/add_event_dialog.dart';
|
||||
|
||||
class EventScreen extends StatefulWidget {
|
||||
final String userId;
|
||||
@@ -42,7 +42,8 @@ class _EventScreenState extends State<EventScreen> {
|
||||
backgroundColor: const Color(0xFF1E1E2C),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add_circle_outline, size: 28, color: Color(0xFF1DBF73)),
|
||||
icon: const Icon(Icons.add_circle_outline,
|
||||
size: 28, color: Color(0xFF1DBF73)),
|
||||
onPressed: () {
|
||||
// Naviguer vers une nouvelle page pour ajouter un événement
|
||||
Navigator.push(
|
||||
@@ -95,7 +96,6 @@ class _EventScreenState extends State<EventScreen> {
|
||||
},
|
||||
status: event.status,
|
||||
);
|
||||
|
||||
},
|
||||
);
|
||||
} else if (state is EventError) {
|
||||
|
||||
42
lib/presentation/screens/friends/friends_content.dart
Normal file
42
lib/presentation/screens/friends/friends_content.dart
Normal file
@@ -0,0 +1,42 @@
|
||||
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']!,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
142
lib/presentation/screens/friends/friends_screen.dart
Normal file
142
lib/presentation/screens/friends/friends_screen.dart
Normal file
@@ -0,0 +1,142 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../../data/providers/friends_provider.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.
|
||||
class FriendsScreen extends StatefulWidget {
|
||||
final String userId; // Identifiant de l'utilisateur pour récupérer ses amis
|
||||
|
||||
const FriendsScreen({Key? key, required this.userId}) : super(key: key);
|
||||
|
||||
@override
|
||||
_FriendsScreenState createState() => _FriendsScreenState();
|
||||
}
|
||||
|
||||
class _FriendsScreenState extends State<FriendsScreen> {
|
||||
late ScrollController _scrollController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Initialisation du contrôleur de défilement pour la gestion de la pagination.
|
||||
_scrollController = ScrollController();
|
||||
_scrollController.addListener(_onScroll);
|
||||
|
||||
// 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
|
||||
Provider.of<FriendsProvider>(context, listen: false).fetchFriends(widget.userId);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Nettoyage du contrôleur de défilement pour éviter les fuites de mémoire.
|
||||
_scrollController.removeListener(_onScroll);
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
debugPrint("[LOG] Dispose : contrôleur de défilement supprimé");
|
||||
}
|
||||
|
||||
/// Méthode déclenchée lors du défilement de la liste.
|
||||
/// Vérifie si l'utilisateur a atteint le bas de la liste pour charger plus d'amis.
|
||||
void _onScroll() {
|
||||
final provider = Provider.of<FriendsProvider>(context, listen: false);
|
||||
if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent &&
|
||||
!provider.isLoading && provider.hasMore) {
|
||||
debugPrint("[LOG] Scroll : fin de liste atteinte, chargement de la page suivante");
|
||||
// Charger plus d'amis si on atteint la fin de la liste
|
||||
provider.fetchFriends(widget.userId);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Accès au fournisseur pour gérer les données et les états des amis.
|
||||
final friendsProvider = Provider.of<FriendsProvider>(context, listen: false);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Mes Amis'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
// Log de l'action de rafraîchissement
|
||||
debugPrint("[LOG] Bouton Refresh : demande de rafraîchissement de la liste des amis");
|
||||
// Rafraîchir la liste des amis
|
||||
friendsProvider.fetchFriends(widget.userId);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
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
|
||||
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.
|
||||
if (friendsProvider.isLoading && friendsProvider.friendsList.isEmpty) {
|
||||
debugPrint("[LOG] Chargement : affichage de l'indicateur de progression");
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
// Si la liste est vide après le chargement, afficher un message indiquant qu'aucun ami n'a été trouvé.
|
||||
if (friendsProvider.friendsList.isEmpty) {
|
||||
debugPrint("[LOG] Liste vide : Aucun ami trouvé");
|
||||
return const Center(
|
||||
child: Text('Aucun ami trouvé'),
|
||||
);
|
||||
}
|
||||
|
||||
// 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,
|
||||
crossAxisSpacing: 10,
|
||||
),
|
||||
itemCount: friendsProvider.friendsList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final friend = friendsProvider.friendsList[index];
|
||||
debugPrint("[LOG] Affichage de l'ami à l'index $index avec ID : ${friend.friendId}");
|
||||
|
||||
return FriendsCircle(
|
||||
friend: friend,
|
||||
onTap: () {
|
||||
// Log pour l'action de visualisation des détails d'un ami
|
||||
debugPrint("[LOG] Détail : Affichage des détails de l'ami ID : ${friend.friendId}");
|
||||
// Naviguer vers l'écran des détails de l'ami
|
||||
FriendDetailScreen.open(
|
||||
context,
|
||||
friend.friendId,
|
||||
friend.firstName ?? 'Ami inconnu',
|
||||
friend.imageUrl ?? '',
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../../../assets/animations/friend_expanding_card.dart';
|
||||
import '../../../data/providers/friends_provider.dart';
|
||||
import '../../../domain/entities/friend.dart';
|
||||
import '../../widgets/friend_detail_screen.dart';
|
||||
import '../../widgets/friends_appbar.dart';
|
||||
import '../../widgets/search_friends.dart';
|
||||
|
||||
/// [FriendsScreenWithProvider] est un écran qui affiche la liste des amis.
|
||||
/// Il utilise le provider [FriendsProvider] pour gérer les états et les données.
|
||||
/// Chaque action est loguée pour permettre une traçabilité complète.
|
||||
class FriendsScreenWithProvider extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
appBar: FriendsAppBar(),
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: SearchFriends(),
|
||||
),
|
||||
Expanded(
|
||||
child: Consumer<FriendsProvider>(
|
||||
builder: (context, friendsProvider, _) {
|
||||
final friends = friendsProvider.friendsList;
|
||||
|
||||
if (friends.isEmpty) {
|
||||
return const Center(
|
||||
child: Text(
|
||||
'Aucun ami trouvé',
|
||||
style: TextStyle(color: Colors.white70),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: friends.length,
|
||||
itemBuilder: (context, index) {
|
||||
final friend = friends[index];
|
||||
return Dismissible(
|
||||
key: Key(friend.friendId),
|
||||
background: Container(
|
||||
color: Colors.redAccent,
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.only(left: 20),
|
||||
child: const Icon(Icons.delete, color: Colors.white),
|
||||
),
|
||||
onDismissed: (direction) {
|
||||
debugPrint("[LOG] Suppression de l'ami avec l'ID : ${friend.friendId}");
|
||||
friendsProvider.removeFriend(friend.friendId);
|
||||
},
|
||||
child: FriendExpandingCard(
|
||||
name: friend.firstName ?? 'Ami inconnu',
|
||||
imageUrl: friend.imageUrl ?? '',
|
||||
description: "Amis depuis ${friend.friendId}",
|
||||
onTap: () => _navigateToFriendDetail(context, friend),
|
||||
onMessageTap: () {
|
||||
debugPrint("[LOG] Envoi d'un message à l'ami : ${friend.firstName ?? 'Ami inconnu'}");
|
||||
},
|
||||
onRemoveTap: () {
|
||||
debugPrint("[LOG] Tentative de suppression de l'ami : ${friend.firstName ?? 'Ami inconnu'}");
|
||||
friendsProvider.removeFriend(friend.friendId);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Navigue vers l'écran des détails de l'utilisateur (ami) récupéré via son `friendId`.
|
||||
void _navigateToFriendDetail(BuildContext context, Friend friend) {
|
||||
debugPrint("[LOG] Navigation vers les détails de l'ami : ${friend.firstName ?? 'Ami inconnu'}");
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => FriendDetailScreen(
|
||||
name: friend.firstName ?? 'Ami inconnu',
|
||||
imageUrl: friend.imageUrl ?? '',
|
||||
friendId: friend.friendId, // Passer l'ID pour récupérer les détails complets
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -6,17 +6,17 @@ 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'; // Importez l'écran de notifications
|
||||
|
||||
import 'package:afterwork/presentation/screens/notifications/notifications_screen.dart'; // Écran de notifications
|
||||
import '../../../core/constants/colors.dart';
|
||||
import '../../../core/theme/theme_provider.dart'; // Pour basculer le thème
|
||||
import '../../../core/theme/theme_provider.dart';
|
||||
import '../friends/friends_screen.dart'; // Écran des amis
|
||||
|
||||
class HomeScreen extends StatefulWidget {
|
||||
final EventRemoteDataSource eventRemoteDataSource;
|
||||
final String userId;
|
||||
final String userName;
|
||||
final String userLastName;
|
||||
final String userProfileImage; // Ajouter un champ pour l'image de profil de l'utilisateur
|
||||
final String userProfileImage; // Image de profil de l'utilisateur
|
||||
|
||||
const HomeScreen({
|
||||
Key? key,
|
||||
@@ -69,54 +69,53 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
|
||||
return <Widget>[
|
||||
SliverAppBar(
|
||||
backgroundColor: AppColors.backgroundColor, // Gère dynamiquement la couleur d'arrière-plan
|
||||
backgroundColor: AppColors.backgroundColor,
|
||||
floating: true,
|
||||
pinned: true,
|
||||
snap: true,
|
||||
elevation: 2, // Réduction de l'élévation pour un design plus léger
|
||||
elevation: 2,
|
||||
leading: Padding(
|
||||
padding: const EdgeInsets.all(4.0), // Réduction du padding
|
||||
padding: const EdgeInsets.all(4.0), // Ajustement du padding
|
||||
child: Image.asset(
|
||||
'lib/assets/images/logo.png',
|
||||
height: 40, // Taille réduite du logo
|
||||
height: 40, // Taille ajustée du logo
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
_buildActionIcon(Icons.add, 'Publier', context),
|
||||
_buildActionIcon(Icons.search, 'Rechercher', context),
|
||||
_buildActionIcon(Icons.message, 'Message', context),
|
||||
_buildNotificationsIcon(context, 45),
|
||||
_buildNotificationsIcon(context, 5), // Gérer la logique des notifications ici
|
||||
|
||||
// Ajout du bouton pour basculer entre les thèmes
|
||||
// Bouton pour basculer entre les thèmes
|
||||
Switch(
|
||||
value: themeProvider.isDarkMode,
|
||||
onChanged: (value) {
|
||||
themeProvider.toggleTheme(); // Bascule le thème lorsqu'on clique
|
||||
themeProvider.toggleTheme(); // Changer le thème
|
||||
},
|
||||
activeColor: AppColors.accentColor,
|
||||
),
|
||||
],
|
||||
bottom: TabBar(
|
||||
controller: _tabController,
|
||||
indicatorColor: AppColors.lightPrimary, // Tab active en bleu
|
||||
indicatorColor: AppColors.lightPrimary,
|
||||
labelStyle: const TextStyle(
|
||||
fontSize: 12, // Réduction de la taille du texte des onglets
|
||||
fontSize: 12, // Taille réduite du texte
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
unselectedLabelStyle: const TextStyle(
|
||||
fontSize: 11, // Réduction pour les onglets non sélectionnés
|
||||
fontSize: 11, // Taille ajustée pour les onglets non sélectionnés
|
||||
),
|
||||
// Changement des couleurs pour les tabs non sélectionnées et sélectionnées
|
||||
labelColor: AppColors.lightPrimary, // Tab active en bleu
|
||||
unselectedLabelColor: AppColors.iconSecondary, // Tabs non sélectionnées en blanc
|
||||
labelColor: AppColors.lightPrimary,
|
||||
unselectedLabelColor: AppColors.iconSecondary,
|
||||
|
||||
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.notifications, size: 24), text: 'Notifications'),
|
||||
_buildProfileTab(),
|
||||
const Tab(icon: Icon(Icons.people_alt_outlined, size: 24), text: 'Ami(e)s'),
|
||||
_buildProfileTab(), // Onglet profil
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -133,7 +132,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
),
|
||||
const EstablishmentsScreen(),
|
||||
const SocialScreen(),
|
||||
const NotificationsScreen(),
|
||||
FriendsScreen(userId: widget.userId), // Correction ici : passer l'userId
|
||||
const ProfileScreen(),
|
||||
],
|
||||
),
|
||||
@@ -141,27 +140,26 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
);
|
||||
}
|
||||
|
||||
// Widget pour afficher la photo de profil de l'utilisateur dans l'onglet
|
||||
// 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, // Définir la couleur de la bordure ici
|
||||
color: Colors.blue,
|
||||
width: 2.0,
|
||||
),
|
||||
),
|
||||
child: CircleAvatar(
|
||||
radius: 16, // Ajustez la taille si nécessaire
|
||||
backgroundColor: Colors.grey[200], // Couleur de fond pour le cas où l'image ne charge pas
|
||||
radius: 16,
|
||||
backgroundColor: Colors.grey[200], // Couleur de fond par défaut
|
||||
child: ClipOval(
|
||||
child: FadeInImage.assetNetwork(
|
||||
placeholder: 'lib/assets/images/user_placeholder.png', // Chemin de l'image par défaut
|
||||
placeholder: 'lib/assets/images/user_placeholder.png',
|
||||
image: widget.userProfileImage,
|
||||
fit: BoxFit.cover,
|
||||
imageErrorBuilder: (context, error, stackTrace) {
|
||||
// Si l'image ne charge pas, afficher une image par défaut
|
||||
return Image.asset('lib/assets/images/profile_picture.png', fit: BoxFit.cover);
|
||||
},
|
||||
),
|
||||
@@ -171,12 +169,12 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
);
|
||||
}
|
||||
|
||||
// Widget pour afficher l'icône de notifications avec un badge si nécessaire
|
||||
// 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 de positionner le badge en dehors des limites du Stack
|
||||
clipBehavior: Clip.none, // Permet d'afficher le badge en dehors des limites
|
||||
children: [
|
||||
CircleAvatar(
|
||||
backgroundColor: AppColors.surface,
|
||||
@@ -184,7 +182,6 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
child: IconButton(
|
||||
icon: const Icon(Icons.notifications, color: AppColors.darkOnPrimary, size: 20),
|
||||
onPressed: () {
|
||||
// Rediriger vers l'écran des notifications
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
@@ -194,7 +191,6 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
},
|
||||
),
|
||||
),
|
||||
// Affiche le badge si le nombre de notifications est supérieur à 0
|
||||
if (notificationCount > 0)
|
||||
Positioned(
|
||||
right: -6,
|
||||
@@ -202,7 +198,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(2),
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.red, // Couleur du badge
|
||||
color: Colors.red,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
@@ -210,7 +206,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
|
||||
minHeight: 18,
|
||||
),
|
||||
child: Text(
|
||||
notificationCount > 99 ? '99+' : '$notificationCount', // Affiche "99+" si le nombre dépasse 99
|
||||
notificationCount > 99 ? '99+' : '$notificationCount',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 10,
|
||||
@@ -225,14 +221,15 @@ 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), // Réduction de l'espacement
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6.0),
|
||||
child: CircleAvatar(
|
||||
backgroundColor: AppColors.surface,
|
||||
radius: 18, // Réduction de la taille des avatars
|
||||
radius: 18,
|
||||
child: IconButton(
|
||||
icon: Icon(iconData, color: AppColors.darkOnPrimary, size: 20), // Taille réduite de l'icône
|
||||
icon: Icon(iconData, color: AppColors.darkOnPrimary, size: 20),
|
||||
onPressed: () {
|
||||
_onMenuSelected(context, label);
|
||||
},
|
||||
|
||||
@@ -10,13 +10,14 @@ import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:loading_icon_button/loading_icon_button.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../../core/errors/exceptions.dart';
|
||||
import '../../../core/theme/theme_provider.dart';
|
||||
import '../../../data/datasources/event_remote_data_source.dart';
|
||||
import '../signup/SignUpScreen.dart';
|
||||
|
||||
/// Écran de connexion pour l'application AfterWork.
|
||||
/// Ce fichier contient des fonctionnalités comme la gestion de la connexion,
|
||||
/// l'authentification avec mot de passe en clair, la gestion des erreurs et un thème jour/nuit.
|
||||
/// L'écran de connexion où les utilisateurs peuvent s'authentifier.
|
||||
/// Toutes les actions sont loguées pour permettre un suivi dans le terminal et détecter les erreurs.
|
||||
class LoginScreen extends StatefulWidget {
|
||||
const LoginScreen({super.key});
|
||||
|
||||
@@ -24,19 +25,21 @@ class LoginScreen extends StatefulWidget {
|
||||
_LoginScreenState createState() => _LoginScreenState();
|
||||
}
|
||||
|
||||
class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStateMixin {
|
||||
final _formKey = GlobalKey<FormState>(); // Clé pour valider le formulaire de connexion.
|
||||
class _LoginScreenState extends State<LoginScreen>
|
||||
with SingleTickerProviderStateMixin {
|
||||
// Clé globale pour la validation du formulaire
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
// Champs utilisateur
|
||||
String _email = ''; // Email de l'utilisateur
|
||||
String _password = ''; // Mot de passe de l'utilisateur
|
||||
// Variables pour stocker l'email et le mot de passe saisis par l'utilisateur
|
||||
String _email = '';
|
||||
String _password = '';
|
||||
|
||||
// États de gestion
|
||||
bool _isPasswordVisible = false; // Pour afficher/masquer le mot de passe
|
||||
bool _isSubmitting = false; // Indicateur pour l'état de soumission du formulaire
|
||||
bool _showErrorMessage = false; // Affichage des erreurs
|
||||
// États de l'écran
|
||||
bool _isPasswordVisible = false; // Pour basculer la visibilité du mot de passe
|
||||
bool _isSubmitting = false; // Indique si la soumission du formulaire est en cours
|
||||
bool _showErrorMessage = false; // Affiche un message d'erreur si nécessaire
|
||||
|
||||
// Services pour les opérations
|
||||
// Sources de données et services
|
||||
final UserRemoteDataSource _userRemoteDataSource = UserRemoteDataSource(http.Client());
|
||||
final SecureStorage _secureStorage = SecureStorage();
|
||||
final PreferencesHelper _preferencesHelper = PreferencesHelper();
|
||||
@@ -44,7 +47,7 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
// Contrôleur pour le bouton de chargement
|
||||
final _btnController = LoadingButtonController();
|
||||
|
||||
// Contrôleur d'animation pour la transition des écrans
|
||||
// Contrôleur d'animation pour gérer la transition entre les écrans
|
||||
late AnimationController _animationController;
|
||||
|
||||
@override
|
||||
@@ -54,25 +57,25 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
);
|
||||
print("Contrôleur d'animation initialisé.");
|
||||
debugPrint("[LOG] Contrôleur d'animation initialisé.");
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
print("Ressources d'animation libérées.");
|
||||
debugPrint("[LOG] Ressources d'animation libérées.");
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/// Fonction pour basculer la visibilité du mot de passe
|
||||
/// Bascule la visibilité du mot de passe et logue l'état actuel.
|
||||
void _togglePasswordVisibility() {
|
||||
setState(() {
|
||||
_isPasswordVisible = !_isPasswordVisible;
|
||||
});
|
||||
print("Visibilité du mot de passe basculée: $_isPasswordVisible");
|
||||
debugPrint("[LOG] Visibilité du mot de passe basculée: $_isPasswordVisible");
|
||||
}
|
||||
|
||||
/// Fonction pour afficher un toast via FlutterToast
|
||||
/// Affiche un toast avec le message spécifié et logue l'action.
|
||||
void _showToast(String message) {
|
||||
Fluttertoast.showToast(
|
||||
msg: message,
|
||||
@@ -83,60 +86,79 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
textColor: Colors.white,
|
||||
fontSize: 16.0,
|
||||
);
|
||||
debugPrint("[LOG] Toast affiché : $message");
|
||||
}
|
||||
|
||||
/// Fonction soumettre le formulaire
|
||||
/// Soumet le formulaire de connexion et tente d'authentifier l'utilisateur.
|
||||
/// Toutes les étapes et erreurs sont loguées pour une traçabilité complète.
|
||||
Future<void> _submit() async {
|
||||
print("Tentative de soumission du formulaire de connexion.");
|
||||
debugPrint("[LOG] Tentative de soumission du formulaire de connexion.");
|
||||
|
||||
if (_formKey.currentState!.validate()) {
|
||||
setState(() {
|
||||
_isSubmitting = true;
|
||||
_showErrorMessage = false;
|
||||
});
|
||||
_formKey.currentState!.save();
|
||||
_formKey.currentState!.save(); // Sauvegarde des données saisies
|
||||
|
||||
try {
|
||||
_btnController.start();
|
||||
_btnController.start(); // Démarre l'animation de chargement du bouton
|
||||
debugPrint("[LOG] Appel à l'API pour authentifier l'utilisateur.");
|
||||
|
||||
// Appel à l'API pour authentifier l'utilisateur
|
||||
final UserModel user = await _userRemoteDataSource.authenticateUser(_email, _password);
|
||||
if (user == null) {
|
||||
throw Exception("L'utilisateur n'a pas été trouvé ou l'authentification a échoué.");
|
||||
}
|
||||
|
||||
print("Utilisateur authentifié : ${user.userId}");
|
||||
await _secureStorage.saveUserId(user.userId);
|
||||
await _preferencesHelper.saveUserName(user.nom);
|
||||
await _preferencesHelper.saveUserLastName(user.prenoms);
|
||||
_showToast("Connexion réussie !");
|
||||
// Validation de l'ID utilisateur
|
||||
if (user.userId.isNotEmpty) {
|
||||
debugPrint("[LOG] Utilisateur authentifié avec succès. ID: ${user.userId}");
|
||||
|
||||
// Navigation vers la page d'accueil
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => HomeScreen(
|
||||
eventRemoteDataSource: EventRemoteDataSource(http.Client()),
|
||||
userId: user.userId,
|
||||
userName: user.nom,
|
||||
userLastName: user.prenoms,
|
||||
userProfileImage: 'lib/assets/images/profile_picture.png',
|
||||
// Sauvegarde des informations utilisateur
|
||||
await _secureStorage.saveUserId(user.userId);
|
||||
await _preferencesHelper.saveUserName(user.nom);
|
||||
await _preferencesHelper.saveUserLastName(user.prenoms);
|
||||
|
||||
_showToast("Connexion réussie !");
|
||||
// Redirection vers l'écran d'accueil
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => HomeScreen(
|
||||
userId: user.userId,
|
||||
userName: user.nom,
|
||||
userLastName: user.prenoms,
|
||||
userProfileImage: 'lib/assets/images/profile_picture.png',
|
||||
eventRemoteDataSource: EventRemoteDataSource(http.Client()),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
} else {
|
||||
debugPrint("[ERROR] L'ID utilisateur est manquant dans la réponse.");
|
||||
_showToast("Erreur : ID utilisateur manquant.");
|
||||
}
|
||||
} catch (e) {
|
||||
print("Erreur lors de l'authentification : $e");
|
||||
_btnController.error();
|
||||
_showToast("Erreur lors de la connexion : ${e.toString()}");
|
||||
// Gestion des erreurs spécifiques et log de chaque type d'erreur
|
||||
if (e is ServerExceptionWithMessage) {
|
||||
debugPrint("[ERROR] Erreur serveur : ${e.message}");
|
||||
_showToast("Erreur serveur : ${e.message}");
|
||||
} else if (e is UnauthorizedException) {
|
||||
debugPrint("[ERROR] Erreur d'authentification : ${e.message}");
|
||||
_showToast("Erreur : ${e.message}");
|
||||
} else {
|
||||
debugPrint("[ERROR] Erreur lors de la connexion : $e");
|
||||
_showToast("Erreur lors de la connexion : ${e.toString()}");
|
||||
}
|
||||
_btnController.error(); // Affiche une erreur sur le bouton
|
||||
setState(() {
|
||||
_showErrorMessage = true;
|
||||
});
|
||||
} finally {
|
||||
_btnController.reset();
|
||||
_btnController.reset(); // Réinitialise l'état du bouton
|
||||
setState(() {
|
||||
_isSubmitting = false;
|
||||
_isSubmitting = false; // Réinitialise l'état de chargement
|
||||
});
|
||||
}
|
||||
} else {
|
||||
print("Échec de validation du formulaire.");
|
||||
debugPrint("[ERROR] Validation du formulaire échouée.");
|
||||
_btnController.reset();
|
||||
_showToast("Veuillez vérifier les informations saisies.");
|
||||
}
|
||||
@@ -152,19 +174,21 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
// Arrière-plan animé
|
||||
AnimatedContainer(
|
||||
duration: const Duration(seconds: 3),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
theme.colorScheme.primary,
|
||||
theme.colorScheme.secondary
|
||||
theme.colorScheme.secondary,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
),
|
||||
),
|
||||
// Spinner de chargement lors de la soumission
|
||||
if (_isSubmitting)
|
||||
const Center(
|
||||
child: SpinKitFadingCircle(
|
||||
@@ -172,6 +196,7 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
size: 50.0,
|
||||
),
|
||||
),
|
||||
// Icône de changement de thème
|
||||
Positioned(
|
||||
top: 40,
|
||||
right: 20,
|
||||
@@ -182,10 +207,11 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
),
|
||||
onPressed: () {
|
||||
themeProvider.toggleTheme();
|
||||
print("Thème basculé : ${themeProvider.isDarkMode ? 'Sombre' : 'Clair'}");
|
||||
debugPrint("[LOG] Thème basculé : ${themeProvider.isDarkMode ? 'Sombre' : 'Clair'}");
|
||||
},
|
||||
),
|
||||
),
|
||||
// Formulaire de connexion
|
||||
Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
@@ -209,18 +235,18 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
icon: Icons.email,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
print("Erreur : champ email vide.");
|
||||
debugPrint("[ERROR] Champ email vide.");
|
||||
return 'Veuillez entrer votre email';
|
||||
}
|
||||
if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
|
||||
print("Erreur : email invalide.");
|
||||
debugPrint("[ERROR] Email invalide.");
|
||||
return 'Veuillez entrer un email valide';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) {
|
||||
_email = value!;
|
||||
print("Email enregistré : $_email");
|
||||
debugPrint("[LOG] Email enregistré : $_email");
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
@@ -230,27 +256,25 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
obscureText: !_isPasswordVisible,
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_isPasswordVisible
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
_isPasswordVisible ? Icons.visibility : Icons.visibility_off,
|
||||
color: theme.iconTheme.color,
|
||||
),
|
||||
onPressed: _togglePasswordVisibility,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
print("Erreur : champ mot de passe vide.");
|
||||
debugPrint("[ERROR] Champ mot de passe vide.");
|
||||
return 'Veuillez entrer votre mot de passe';
|
||||
}
|
||||
if (value.length < 6) {
|
||||
print("Erreur : mot de passe trop court.");
|
||||
debugPrint("[ERROR] Mot de passe trop court.");
|
||||
return 'Le mot de passe doit comporter au moins 6 caractères';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) {
|
||||
_password = value!;
|
||||
print("Mot de passe enregistré.");
|
||||
debugPrint("[LOG] Mot de passe enregistré.");
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
@@ -272,28 +296,26 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
const SizedBox(height: 20),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
print("Redirection vers la page d'inscription");
|
||||
debugPrint("[LOG] Redirection vers la page d'inscription.");
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => SignUpScreen(),
|
||||
builder: (context) => const SignUpScreen(),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
'Pas encore de compte ? Inscrivez-vous',
|
||||
style: theme.textTheme.bodyMedium!
|
||||
.copyWith(color: Colors.white70),
|
||||
style: theme.textTheme.bodyMedium!.copyWith(color: Colors.white70),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
print("Mot de passe oublié");
|
||||
debugPrint("[LOG] Mot de passe oublié cliqué.");
|
||||
},
|
||||
child: Text(
|
||||
'Mot de passe oublié ?',
|
||||
style: theme.textTheme.bodyMedium!
|
||||
.copyWith(color: Colors.white70),
|
||||
style: theme.textTheme.bodyMedium!.copyWith(color: Colors.white70),
|
||||
),
|
||||
),
|
||||
if (_showErrorMessage)
|
||||
@@ -326,9 +348,8 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
),
|
||||
if (isKeyboardVisible)
|
||||
Text(
|
||||
'© 2024 LionsDev',
|
||||
style: theme.textTheme.bodyMedium!
|
||||
.copyWith(color: Colors.white70),
|
||||
'© 2024',
|
||||
style: theme.textTheme.bodyMedium!.copyWith(color: Colors.white70),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
@@ -339,7 +360,7 @@ class _LoginScreenState extends State<LoginScreen> with SingleTickerProviderStat
|
||||
);
|
||||
}
|
||||
|
||||
/// Widget réutilisable pour les champs de texte avec validation et design amélioré
|
||||
/// Méthode pour construire les champs de formulaire avec les styles adaptés.
|
||||
Widget _buildTextFormField({
|
||||
required String label,
|
||||
required IconData icon,
|
||||
|
||||
@@ -89,9 +89,6 @@ class _SignUpScreenState extends State<SignUpScreen> {
|
||||
|
||||
// Envoi des informations pour créer un nouvel utilisateur
|
||||
final createdUser = await _userRemoteDataSource.createUser(user);
|
||||
if (createdUser == null) {
|
||||
throw Exception("La création du compte a échoué.");
|
||||
}
|
||||
|
||||
print("Utilisateur créé : ${createdUser.userId}");
|
||||
|
||||
|
||||
101
lib/presentation/screens/social/social_card.dart
Normal file
101
lib/presentation/screens/social/social_card.dart
Normal file
@@ -0,0 +1,101 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../core/constants/colors.dart';
|
||||
import '../../../data/models/social_post_model.dart';
|
||||
import '../../widgets/social_header_widget.dart';
|
||||
import '../../widgets/social_interaction_row.dart';
|
||||
import '../../widgets/swipe_background.dart'; // Import du widget de swipe
|
||||
|
||||
class SocialCard extends StatelessWidget {
|
||||
final SocialPost post;
|
||||
final VoidCallback onLike;
|
||||
final VoidCallback onComment;
|
||||
final VoidCallback onShare;
|
||||
final VoidCallback onDeletePost;
|
||||
final VoidCallback onEditPost;
|
||||
|
||||
const SocialCard({
|
||||
Key? key,
|
||||
required this.post,
|
||||
required this.onLike,
|
||||
required this.onComment,
|
||||
required this.onShare,
|
||||
required this.onDeletePost,
|
||||
required this.onEditPost,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dismissible(
|
||||
key: ValueKey(post.postText),
|
||||
direction: DismissDirection.endToStart,
|
||||
onDismissed: (direction) {
|
||||
onDeletePost();
|
||||
},
|
||||
background: SwipeBackground(
|
||||
color: Colors.red,
|
||||
icon: Icons.delete,
|
||||
label: 'Supprimer',
|
||||
),
|
||||
child: Card(
|
||||
color: AppColors.cardColor,
|
||||
margin: const EdgeInsets.symmetric(vertical: 10.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SocialHeaderWidget(
|
||||
post: post,
|
||||
onEditPost: () {
|
||||
print('Modifier le post');
|
||||
},
|
||||
menuKey: GlobalKey(),
|
||||
menuContext: context,
|
||||
onClosePost: () {
|
||||
print('Close post');
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text (
|
||||
post.postText,
|
||||
style: TextStyle(
|
||||
color: AppColors.textSecondary,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (post.postImage.isNotEmpty)
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Image.asset(post.postImage, fit: BoxFit.cover),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: post.tags
|
||||
.map((tag) => Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: Text(
|
||||
tag,
|
||||
style: TextStyle(
|
||||
color: AppColors.accentColor,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SocialInteractionRow(
|
||||
post: post,
|
||||
onLike: onLike,
|
||||
onComment: onComment,
|
||||
onShare: onShare,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
103
lib/presentation/screens/social/social_content.dart
Normal file
103
lib/presentation/screens/social/social_content.dart
Normal file
@@ -0,0 +1,103 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../data/models/social_post_model.dart';
|
||||
import 'social_card.dart'; // Import de la SocialCard
|
||||
|
||||
class SocialContent extends StatefulWidget {
|
||||
const SocialContent({super.key});
|
||||
|
||||
@override
|
||||
_SocialContentState createState() => _SocialContentState();
|
||||
}
|
||||
|
||||
class _SocialContentState extends State<SocialContent> {
|
||||
final List<SocialPost> _posts = [
|
||||
SocialPost(
|
||||
userName: 'John Doe',
|
||||
userImage: 'lib/assets/images/profile_picture.png',
|
||||
postText: 'Une belle journée au parc avec des amis ! 🌳🌞',
|
||||
postImage: 'lib/assets/images/placeholder.png',
|
||||
likes: 12,
|
||||
comments: 4,
|
||||
badges: ['Explorer', 'Photographe'],
|
||||
tags: ['#Nature', '#FunDay'],
|
||||
shares: 25,
|
||||
),
|
||||
SocialPost(
|
||||
userName: 'Jane Smith',
|
||||
userImage: 'lib/assets/images/profile_picture.png',
|
||||
postText: 'Mon nouveau chat est tellement mignon 🐱',
|
||||
postImage: 'lib/assets/images/placeholder.png',
|
||||
likes: 30,
|
||||
comments: 8,
|
||||
badges: ['Animal Lover', 'Partageur'],
|
||||
tags: ['#Chat', '#Cuteness'],
|
||||
shares: 25,
|
||||
),
|
||||
SocialPost(
|
||||
userName: 'Alice Brown',
|
||||
userImage: 'lib/assets/images/profile_picture.png',
|
||||
postText: 'Café du matin avec une vue magnifique ☕️',
|
||||
postImage: 'lib/assets/images/placeholder.png',
|
||||
likes: 45,
|
||||
comments: 15,
|
||||
badges: ['Gourmet', 'Partageur'],
|
||||
tags: ['#Café', '#MorningVibes'],
|
||||
shares: 25,
|
||||
),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: _posts.length,
|
||||
itemBuilder: (context, index) {
|
||||
final post = _posts[index];
|
||||
return SocialCard(
|
||||
post: post,
|
||||
onLike: () {
|
||||
setState(() {
|
||||
_posts[index] = SocialPost(
|
||||
userName: post.userName,
|
||||
userImage: post.userImage,
|
||||
postText: post.postText,
|
||||
postImage: post.postImage,
|
||||
likes: post.likes + 1,
|
||||
comments: post.comments,
|
||||
badges: post.badges,
|
||||
tags: post.tags,
|
||||
shares: post.shares + 1,
|
||||
);
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Like ajouté')),
|
||||
);
|
||||
},
|
||||
onComment: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Commentaire ajouté')),
|
||||
);
|
||||
},
|
||||
onShare: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Post partagé')),
|
||||
);
|
||||
},
|
||||
onDeletePost: () {
|
||||
setState(() {
|
||||
_posts.removeAt(index);
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Post supprimé')),
|
||||
);
|
||||
},
|
||||
onEditPost: () {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Post modifié')),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'social_content.dart'; // Import du fichier qui contient SocialContent
|
||||
|
||||
class SocialScreen extends StatelessWidget {
|
||||
const SocialScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Text(
|
||||
'Social',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
return Scaffold(
|
||||
backgroundColor: const Color(0xFF1E1E2C), // Fond noir pour correspondre à un thème sombre
|
||||
appBar: AppBar(
|
||||
title: const Text(
|
||||
'Social',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.black, // AppBar avec fond noir pour un design cohérent
|
||||
),
|
||||
body: SocialContent(), // Appel à SocialContent pour afficher le contenu
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user