diff --git a/app/lib/models/schedule.dart b/app/lib/models/schedule.dart index 489d8bd..fbe70a5 100644 --- a/app/lib/models/schedule.dart +++ b/app/lib/models/schedule.dart @@ -55,6 +55,9 @@ class ScheduleDetail { final String? content; final List imageUrls; final String? postUrl; + // X 프로필 + final String? profileDisplayName; + final String? profileAvatarUrl; ScheduleDetail({ required this.id, @@ -75,6 +78,8 @@ class ScheduleDetail { this.content, this.imageUrls = const [], this.postUrl, + this.profileDisplayName, + this.profileAvatarUrl, }); factory ScheduleDetail.fromJson(Map json) { @@ -103,6 +108,8 @@ class ScheduleDetail { content: json['content'] as String?, imageUrls: (json['imageUrls'] as List?)?.cast() ?? [], postUrl: json['postUrl'] as String?, + profileDisplayName: (json['profile'] as Map?)?['displayName'] as String?, + profileAvatarUrl: (json['profile'] as Map?)?['avatarUrl'] as String?, ); } diff --git a/app/lib/views/schedule/schedule_detail_view.dart b/app/lib/views/schedule/schedule_detail_view.dart index 0e43323..82ec77c 100644 --- a/app/lib/views/schedule/schedule_detail_view.dart +++ b/app/lib/views/schedule/schedule_detail_view.dart @@ -423,7 +423,9 @@ class _ScheduleDetailViewState extends ConsumerState { /// X 섹션 Widget _buildXSection(ScheduleDetail schedule) { - final displayName = schedule.username ?? 'Unknown'; + final username = schedule.username ?? 'Unknown'; + final displayName = schedule.profileDisplayName ?? username; + final avatarUrl = schedule.profileAvatarUrl; return Container( decoration: BoxDecoration( @@ -440,28 +442,24 @@ class _ScheduleDetailViewState extends ConsumerState { child: Row( children: [ // 프로필 이미지 - Container( - width: 40, - height: 40, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: [Colors.grey[700]!, Colors.grey[900]!], - ), - shape: BoxShape.circle, - ), - child: Center( - child: Text( - displayName[0].toUpperCase(), - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - ), - ), + avatarUrl != null + ? ClipOval( + child: CachedNetworkImage( + imageUrl: avatarUrl, + width: 40, + height: 40, + fit: BoxFit.cover, + placeholder: (_, _) => Container( + width: 40, height: 40, + decoration: BoxDecoration( + color: Colors.grey[300], + shape: BoxShape.circle, + ), + ), + errorWidget: (_, _, _) => _buildAvatarFallback(displayName), + ), + ) + : _buildAvatarFallback(displayName), const SizedBox(width: 12), Expanded( child: Column( @@ -484,7 +482,7 @@ class _ScheduleDetailViewState extends ConsumerState { ], ), Text( - '@$displayName', + '@$username', style: TextStyle( fontSize: 13, color: Colors.grey[500], @@ -691,6 +689,32 @@ class _ScheduleDetailViewState extends ConsumerState { ); } + /// 아바타 폴백 (이니셜) + Widget _buildAvatarFallback(String name) { + return Container( + width: 40, + height: 40, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [Colors.grey[700]!, Colors.grey[900]!], + ), + shape: BoxShape.circle, + ), + child: Center( + child: Text( + name[0].toUpperCase(), + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + ); + } + /// 인증 배지 Widget _buildVerifiedBadge() { return SizedBox(