fix(app): X 상세 화면 프로필 사진/표시 이름 누락 수정

- ScheduleDetail에 profileDisplayName, profileAvatarUrl 필드 추가
- X 섹션: profile.displayName으로 표시 이름, profile.avatarUrl로 프로필 사진 표시
- 아바타 URL이 없을 때 이니셜 폴백 유지
- @username 표시 수정 (displayName이 아닌 실제 username 사용)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-03-29 14:21:24 +09:00
parent fc38678fbd
commit 998125333b
2 changed files with 55 additions and 24 deletions

View file

@ -55,6 +55,9 @@ class ScheduleDetail {
final String? content;
final List<String> 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<String, dynamic> json) {
@ -103,6 +108,8 @@ class ScheduleDetail {
content: json['content'] as String?,
imageUrls: (json['imageUrls'] as List<dynamic>?)?.cast<String>() ?? [],
postUrl: json['postUrl'] as String?,
profileDisplayName: (json['profile'] as Map<String, dynamic>?)?['displayName'] as String?,
profileAvatarUrl: (json['profile'] as Map<String, dynamic>?)?['avatarUrl'] as String?,
);
}

View file

@ -423,7 +423,9 @@ class _ScheduleDetailViewState extends ConsumerState<ScheduleDetailView> {
/// 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<ScheduleDetailView> {
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<ScheduleDetailView> {
],
),
Text(
'@$displayName',
'@$username',
style: TextStyle(
fontSize: 13,
color: Colors.grey[500],
@ -691,6 +689,32 @@ class _ScheduleDetailViewState extends ConsumerState<ScheduleDetailView> {
);
}
/// ()
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(