201 lines
9.3 KiB
Dart
201 lines
9.3 KiB
Dart
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/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
|
|
|
|
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 via le fournisseur (Provider)
|
|
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);
|
|
|
|
// 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); // Chargement de plus d'amis
|
|
}
|
|
}
|
|
|
|
@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: [
|
|
// 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);
|
|
} else {
|
|
debugPrint("[LOG] Rafraîchissement en cours, action ignorée.");
|
|
}
|
|
},
|
|
),
|
|
],
|
|
),
|
|
body: SafeArea(
|
|
child: Column(
|
|
children: [
|
|
// Widget de recherche d'amis en haut de l'écran
|
|
const Padding(
|
|
padding: EdgeInsets.all(8.0),
|
|
child: SearchFriends(),
|
|
),
|
|
Expanded(
|
|
// 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.
|
|
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é'),
|
|
);
|
|
}
|
|
|
|
return GridView.builder(
|
|
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,
|
|
itemBuilder: (context, index) {
|
|
final friend = friendsProvider.friendsList[index];
|
|
// 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,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 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)
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|