feat: display and crop the user avatar correctly

This commit is contained in:
sBubshait 2025-08-06 00:00:06 +03:00
parent 1c8e357722
commit 2a05a9f8df
2 changed files with 115 additions and 48 deletions

View File

@ -1,24 +1,20 @@
class PostCreator { class PostCreator {
final String id; final String id;
final String displayName; final String displayName;
final String? avatar;
PostCreator({ PostCreator({required this.id, required this.displayName, this.avatar});
required this.id,
required this.displayName,
});
factory PostCreator.fromJson(Map<String, dynamic> json) { factory PostCreator.fromJson(Map<String, dynamic> json) {
return PostCreator( return PostCreator(
id: json['id']?.toString() ?? '', id: json['id']?.toString() ?? '',
displayName: json['displayName'] ?? '', displayName: json['displayName'] ?? '',
avatar: json['avatar']?.toString(),
); );
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {'id': id, 'displayName': displayName, 'avatar': avatar};
'id': id,
'displayName': displayName,
};
} }
} }
@ -53,7 +49,9 @@ class Post {
body: json['body'] ?? '', body: json['body'] ?? '',
likes: int.tryParse(json['likes']?.toString() ?? '0') ?? 0, likes: int.tryParse(json['likes']?.toString() ?? '0') ?? 0,
comments: int.tryParse(json['comments']?.toString() ?? '0') ?? 0, comments: int.tryParse(json['comments']?.toString() ?? '0') ?? 0,
creationDate: DateTime.parse(json['creationDate'] ?? DateTime.now().toIso8601String()), creationDate: DateTime.parse(
json['creationDate'] ?? DateTime.now().toIso8601String(),
),
liked: json['liked'] == true, liked: json['liked'] == true,
images: json['images'] != null ? List<String>.from(json['images']) : null, images: json['images'] != null ? List<String>.from(json['images']) : null,
); );
@ -113,7 +111,9 @@ class LikedUser {
return LikedUser( return LikedUser(
id: json['id']?.toString() ?? '', id: json['id']?.toString() ?? '',
displayName: json['displayName'] ?? '', displayName: json['displayName'] ?? '',
likeTime: DateTime.parse(json['likeTime'] ?? DateTime.now().toIso8601String()), likeTime: DateTime.parse(
json['likeTime'] ?? DateTime.now().toIso8601String(),
),
); );
} }
@ -157,10 +157,14 @@ class DetailedPost {
likes: int.tryParse(json['likes']?.toString() ?? '0') ?? 0, likes: int.tryParse(json['likes']?.toString() ?? '0') ?? 0,
comments: int.tryParse(json['comments']?.toString() ?? '0') ?? 0, comments: int.tryParse(json['comments']?.toString() ?? '0') ?? 0,
liked: json['liked'] == true, liked: json['liked'] == true,
creationDate: DateTime.parse(json['creationDate'] ?? DateTime.now().toIso8601String()), creationDate: DateTime.parse(
likedUsers: (json['likedUsers'] as List?) json['creationDate'] ?? DateTime.now().toIso8601String(),
?.map((user) => LikedUser.fromJson(user)) ),
.toList() ?? [], likedUsers:
(json['likedUsers'] as List?)
?.map((user) => LikedUser.fromJson(user))
.toList() ??
[],
images: json['images'] != null ? List<String>.from(json['images']) : null, images: json['images'] != null ? List<String>.from(json['images']) : null,
); );
} }
@ -186,8 +190,6 @@ class CreatePostRequest {
CreatePostRequest({required this.body}); CreatePostRequest({required this.body});
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return { return {'body': body};
'body': body,
};
} }
} }

View File

@ -380,6 +380,98 @@ class PostImagesCarousel extends StatelessWidget {
} }
} }
class UserAvatar extends StatelessWidget {
final String displayName;
final String? avatarUrl;
final double radius;
final Color? backgroundColor;
const UserAvatar({
Key? key,
required this.displayName,
this.avatarUrl,
this.radius = 20,
this.backgroundColor,
}) : super(key: key);
Color _getAvatarColor(String displayName) {
final colors = [
Color(0xFF32B0A5),
Color(0xFF4600B9),
Color(0xFF6A4C93),
Color(0xFFFF6347),
Color(0xFF32CD32),
Color(0xFF9932CC),
];
int hash = displayName.hashCode;
return colors[hash.abs() % colors.length];
}
String _getAvatarLetter(String displayName) {
return displayName.isNotEmpty ? displayName[0].toUpperCase() : '?';
}
bool _isValidImageUrl(String? url) {
return url != null && url.isNotEmpty && url.trim().isNotEmpty;
}
@override
Widget build(BuildContext context) {
final color = backgroundColor ?? _getAvatarColor(displayName);
final letter = _getAvatarLetter(displayName);
return CircleAvatar(
radius: radius,
backgroundColor: color,
child: _isValidImageUrl(avatarUrl)
? ClipOval(
child: Image.network(
avatarUrl!,
width: radius * 2,
height: radius * 2,
fit: BoxFit.cover,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return SizedBox(
width: radius * 2,
height: radius * 2,
child: Center(
child: SizedBox(
width: radius * 0.6,
height: radius * 0.6,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
),
),
);
},
errorBuilder: (context, error, stackTrace) {
return Text(
letter,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: radius * 0.8,
),
);
},
),
)
: Text(
letter,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: radius * 0.8,
),
),
);
}
}
class PostCard extends StatefulWidget { class PostCard extends StatefulWidget {
final Post post; final Post post;
@ -413,24 +505,6 @@ class _PostCardState extends State<PostCard> {
} }
} }
Color _getAvatarColor(String displayName) {
final colors = [
Color(0xFF32B0A5),
Color(0xFF4600B9),
Color(0xFF6A4C93),
Color(0xFFFF6347),
Color(0xFF32CD32),
Color(0xFF9932CC),
];
int hash = displayName.hashCode;
return colors[hash.abs() % colors.length];
}
String _getAvatarLetter(String displayName) {
return displayName.isNotEmpty ? displayName[0].toUpperCase() : '?';
}
void _sharePost(Post post) { void _sharePost(Post post) {
final shareText = final shareText =
'${post.creator.displayName} posted on Wesal.online:\n\n${post.body}'; '${post.creator.displayName} posted on Wesal.online:\n\n${post.body}';
@ -503,8 +577,6 @@ class _PostCardState extends State<PostCard> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final creator = _currentPost.creator; final creator = _currentPost.creator;
final avatarColor = _getAvatarColor(creator.displayName);
final avatarLetter = _getAvatarLetter(creator.displayName);
final relativeTime = InvitationUtils.getRelativeTime( final relativeTime = InvitationUtils.getRelativeTime(
_currentPost.creationDate, _currentPost.creationDate,
); );
@ -538,17 +610,10 @@ class _PostCardState extends State<PostCard> {
children: [ children: [
Row( Row(
children: [ children: [
CircleAvatar( UserAvatar(
displayName: creator.displayName,
avatarUrl: creator.avatar,
radius: 20, radius: 20,
backgroundColor: avatarColor,
child: Text(
avatarLetter,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
), ),
SizedBox(width: 12), SizedBox(width: 12),
Expanded( Expanded(