feat : post respect ratio and height

This commit is contained in:
Yaël Perret
2025-08-26 22:32:11 +02:00
parent 174f86e581
commit 1332fddeeb
3 changed files with 634 additions and 76 deletions

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'dart:async';
class PostCardWidget extends StatelessWidget {
class PostCardWidget extends StatefulWidget {
final String title;
final String content;
final String authorName;
@@ -13,6 +14,7 @@ class PostCardWidget extends StatelessWidget {
final VoidCallback? onLike;
final VoidCallback? onComment;
final VoidCallback? onShare;
final double? aspectRatio; // Nouveau paramètre pour le ratio (largeur/hauteur)
const PostCardWidget({
Key? key,
@@ -28,8 +30,15 @@ class PostCardWidget extends StatelessWidget {
this.onLike,
this.onComment,
this.onShare,
this.aspectRatio, // null = ratio naturel de l'image, ex: 16/9, 4/3, 1/1
}) : super(key: key);
@override
State<PostCardWidget> createState() => _PostCardWidgetState();
}
class _PostCardWidgetState extends State<PostCardWidget> {
@override
Widget build(BuildContext context) {
return Card(
@@ -49,9 +58,9 @@ class PostCardWidget extends StatelessWidget {
// Photo de profil de l'auteur
CircleAvatar(
radius: 20,
backgroundImage: NetworkImage(authorImageUrl),
backgroundImage: NetworkImage(widget.authorImageUrl),
onBackgroundImageError: (_, __) {},
child: authorImageUrl.isEmpty
child: widget.authorImageUrl.isEmpty
? const Icon(Icons.person, size: 20)
: null,
),
@@ -62,7 +71,7 @@ class PostCardWidget extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
authorName,
widget.authorName,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w600,
color: Colors.white,
@@ -70,7 +79,7 @@ class PostCardWidget extends StatelessWidget {
),
const SizedBox(height: 2),
Text(
_formatDate(publishDate),
_formatDate(widget.publishDate),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.grey,
),
@@ -97,7 +106,7 @@ class PostCardWidget extends StatelessWidget {
children: [
// Contenu
Text(
content,
widget.content,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.white,
height: 1.4,
@@ -108,72 +117,82 @@ class PostCardWidget extends StatelessWidget {
),
// Images avec boutons d'action si présentes
if (imageUrls != null && imageUrls!.isNotEmpty)
Container(
//margin: const EdgeInsets.symmetric(vertical: 12.0),
height: 200,
child: Stack(
children: [
// Images
PageView.builder(
itemCount: imageUrls!.length,
itemBuilder: (context, index) {
return Container(
//margin: const EdgeInsets.symmetric(horizontal: 16.0),
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(0),
topRight: Radius.circular(0),
bottomLeft: Radius.circular(8),
bottomRight: Radius.circular(8),
),
),
child: ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(0),
topRight: Radius.circular(0),
bottomLeft: Radius.circular(8),
bottomRight: Radius.circular(8),
),
child: _buildImage(imageUrls![index]),
),
);
},
),
// Boutons d'action en overlay à droite
Positioned(
right: 24,
top: 0,
bottom: 0,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_VerticalActionButton(
icon: Icons.favorite_outline,
count: likesCount,
onPressed: onLike,
),
const SizedBox(height: 16),
_VerticalActionButton(
icon: Icons.comment_outlined,
count: commentsCount,
onPressed: onComment,
),
const SizedBox(height: 16),
_VerticalActionButton(
icon: Icons.share_outlined,
count: sharesCount,
onPressed: onShare,
),
],
),
),
],
if (widget.imageUrls != null && widget.imageUrls!.isNotEmpty)
FutureBuilder<double>(
future: getImageHeightWithRatio(
widget.imageUrls![0], // Utilise la première image pour calculer la hauteur
MediaQuery.of(context).size.width - 32, // Largeur du conteneur (avec padding)
widget.aspectRatio, // Ratio forcé ou null pour le ratio naturel
),
builder: (context, snapshot) {
final imageHeight = snapshot.data ?? 200.0; // Hauteur par défaut si pas encore calculée
return Container(
height: imageHeight,
child: Stack(
children: [
// Images
PageView.builder(
itemCount: widget.imageUrls!.length,
itemBuilder: (context, index) {
return Container(
//margin: const EdgeInsets.symmetric(horizontal: 16.0),
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(0),
topRight: Radius.circular(0),
bottomLeft: Radius.circular(8),
bottomRight: Radius.circular(8),
),
),
child: ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(0),
topRight: Radius.circular(0),
bottomLeft: Radius.circular(8),
bottomRight: Radius.circular(8),
),
child: _buildImage(widget.imageUrls![index]),
),
);
},
),
// Boutons d'action en overlay à droite
Positioned(
right: 24,
top: 0,
bottom: 0,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_VerticalActionButton(
icon: Icons.favorite_outline,
count: widget.likesCount,
onPressed: widget.onLike,
),
const SizedBox(height: 16),
_VerticalActionButton(
icon: Icons.comment_outlined,
count: widget.commentsCount,
onPressed: widget.onComment,
),
const SizedBox(height: 16),
_VerticalActionButton(
icon: Icons.share_outlined,
count: widget.sharesCount,
onPressed: widget.onShare,
),
],
),
),
],
),
);
},
),
// Si pas d'image, boutons d'action en bas à droite
if (imageUrls == null || imageUrls!.isEmpty)
if (widget.imageUrls == null || widget.imageUrls!.isEmpty)
Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 16.0),
child: Row(
@@ -182,20 +201,20 @@ class PostCardWidget extends StatelessWidget {
children: [
_HorizontalActionButton(
icon: Icons.favorite_outline,
count: likesCount,
onPressed: onLike,
count: widget.likesCount,
onPressed: widget.onLike,
),
const SizedBox(width: 16),
_HorizontalActionButton(
icon: Icons.comment_outlined,
count: commentsCount,
onPressed: onComment,
count: widget.commentsCount,
onPressed: widget.onComment,
),
const SizedBox(width: 16),
_HorizontalActionButton(
icon: Icons.share_outlined,
count: sharesCount,
onPressed: onShare,
count: widget.sharesCount,
onPressed: widget.onShare,
),
],
),
@@ -223,6 +242,66 @@ class PostCardWidget extends StatelessWidget {
}
}
/// Récupère les dimensions de l'image (largeur et hauteur)
Future<Size?> getImageDimensions(String imageUrl) async {
try {
ImageProvider imageProvider;
if (imageUrl.startsWith('assets/')) {
imageProvider = AssetImage(imageUrl);
} else {
imageProvider = NetworkImage(imageUrl);
}
final ImageStream stream = imageProvider.resolve(const ImageConfiguration());
final Completer<Size?> completer = Completer();
late ImageStreamListener listener;
listener = ImageStreamListener((ImageInfo info, bool synchronousCall) {
final double width = info.image.width.toDouble();
final double height = info.image.height.toDouble();
stream.removeListener(listener);
completer.complete(Size(width, height));
}, onError: (dynamic exception, StackTrace? stackTrace) {
stream.removeListener(listener);
completer.complete(null);
});
stream.addListener(listener);
return completer.future;
} catch (e) {
print('Erreur lors de la récupération des dimensions de l\'image: $e');
return null;
}
}
/// Récupère uniquement la hauteur de l'image
Future<double?> getImageHeight(String imageUrl) async {
final dimensions = await getImageDimensions(imageUrl);
return dimensions?.height;
}
/// Calcule la hauteur de l'image en fonction de la largeur du conteneur
Future<double?> getImageHeightForWidth(String imageUrl, double containerWidth) async {
final dimensions = await getImageDimensions(imageUrl);
if (dimensions == null) return null;
final aspectRatio = dimensions.width / dimensions.height;
return containerWidth / aspectRatio;
}
/// Calcule la hauteur en respectant un ratio spécifique ou le ratio naturel
Future<double> getImageHeightWithRatio(String imageUrl, double containerWidth, double? forcedRatio) async {
if (forcedRatio != null) {
// Utilise le ratio forcé (largeur/hauteur)
return containerWidth / forcedRatio;
} else {
// Utilise le ratio naturel de l'image
final naturalHeight = await getImageHeightForWidth(imageUrl, containerWidth);
return naturalHeight ?? 200.0; // Fallback si l'image ne peut pas être chargée
}
}
Widget _buildImage(String imageUrl) {
// Vérifie si c'est une image d'assets ou une URL réseau
if (imageUrl.startsWith('assets/')) {
@@ -354,7 +433,7 @@ class _VerticalActionButtonState extends State<_VerticalActionButton>
),
child: Icon(
widget.icon,
size: 24,
size: 30,
color: Colors.white,
),
),
@@ -365,7 +444,7 @@ class _VerticalActionButtonState extends State<_VerticalActionButton>
widget.count! > 999 ? '999+' : '${widget.count}',
style: const TextStyle(
color: Colors.white,
fontSize: 12,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),