feat : post respect ratio and height
This commit is contained in:
@@ -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,
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user