feat(arena): 아레나 화면 구현
- ArenaScreen: 아레나 메인 화면 - ArenaSetupScreen: 전투 설정 화면 - ArenaBattleScreen: 전투 진행 화면 - 관련 위젯 추가
This commit is contained in:
208
lib/src/features/arena/widgets/arena_rank_card.dart
Normal file
208
lib/src/features/arena/widgets/arena_rank_card.dart
Normal file
@@ -0,0 +1,208 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:asciineverdie/src/core/l10n/game_data_l10n.dart';
|
||||
import 'package:asciineverdie/src/core/model/hall_of_fame.dart';
|
||||
import 'package:asciineverdie/src/shared/retro_colors.dart';
|
||||
|
||||
/// 아레나 순위 카드 위젯
|
||||
///
|
||||
/// 명예의 전당 캐릭터를 순위와 함께 표시
|
||||
class ArenaRankCard extends StatelessWidget {
|
||||
const ArenaRankCard({
|
||||
super.key,
|
||||
required this.entry,
|
||||
required this.rank,
|
||||
required this.score,
|
||||
this.isSelected = false,
|
||||
this.isHighlighted = false,
|
||||
this.compact = false,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
/// 캐릭터 엔트리
|
||||
final HallOfFameEntry entry;
|
||||
|
||||
/// 순위 (1-based)
|
||||
final int rank;
|
||||
|
||||
/// 아레나 점수
|
||||
final int score;
|
||||
|
||||
/// 선택 상태 (상대로 선택됨)
|
||||
final bool isSelected;
|
||||
|
||||
/// 하이라이트 상태 (내 캐릭터 표시)
|
||||
final bool isHighlighted;
|
||||
|
||||
/// 컴팩트 모드 (작은 사이즈)
|
||||
final bool compact;
|
||||
|
||||
/// 탭 콜백
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final rankColor = _getRankColor(rank);
|
||||
final rankIcon = _getRankIcon(rank);
|
||||
|
||||
// 배경색 결정
|
||||
Color bgColor;
|
||||
Color borderColor;
|
||||
if (isSelected) {
|
||||
bgColor = Colors.red.withValues(alpha: 0.15);
|
||||
borderColor = Colors.red;
|
||||
} else if (isHighlighted) {
|
||||
bgColor = Colors.blue.withValues(alpha: 0.15);
|
||||
borderColor = Colors.blue;
|
||||
} else {
|
||||
bgColor = RetroColors.panelBgOf(context);
|
||||
borderColor = RetroColors.borderOf(context);
|
||||
}
|
||||
|
||||
return Card(
|
||||
margin: EdgeInsets.symmetric(
|
||||
vertical: compact ? 2 : 4,
|
||||
horizontal: compact ? 0 : 8,
|
||||
),
|
||||
color: bgColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(compact ? 6 : 8),
|
||||
side: BorderSide(
|
||||
color: borderColor,
|
||||
width: (isSelected || isHighlighted) ? 2 : 1,
|
||||
),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(compact ? 6 : 8),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(compact ? 8 : 12),
|
||||
child: Row(
|
||||
children: [
|
||||
// 순위 배지
|
||||
_buildRankBadge(rankColor, rankIcon),
|
||||
SizedBox(width: compact ? 8 : 12),
|
||||
// 캐릭터 정보
|
||||
Expanded(child: _buildCharacterInfo(context)),
|
||||
// 점수
|
||||
if (!compact) _buildScoreColumn(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRankBadge(Color color, IconData? icon) {
|
||||
final size = compact ? 24.0 : 36.0;
|
||||
final iconSize = compact ? 12.0 : 18.0;
|
||||
final fontSize = compact ? 7.0 : 10.0;
|
||||
|
||||
return Container(
|
||||
width: size,
|
||||
height: size,
|
||||
decoration: BoxDecoration(
|
||||
color: color.withValues(alpha: 0.2),
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: color, width: compact ? 1.5 : 2),
|
||||
),
|
||||
child: Center(
|
||||
child: icon != null
|
||||
? Icon(icon, color: color, size: iconSize)
|
||||
: Text(
|
||||
'$rank',
|
||||
style: TextStyle(
|
||||
fontFamily: 'PressStart2P',
|
||||
fontSize: fontSize,
|
||||
color: color,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCharacterInfo(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 이름
|
||||
Text(
|
||||
entry.characterName,
|
||||
style: TextStyle(
|
||||
fontFamily: 'PressStart2P',
|
||||
fontSize: compact ? 6 : 9,
|
||||
color: RetroColors.textPrimaryOf(context),
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
SizedBox(height: compact ? 2 : 4),
|
||||
// 종족/클래스 + 레벨
|
||||
Text(
|
||||
compact
|
||||
? 'Lv.${entry.level}'
|
||||
: '${GameDataL10n.getRaceName(context, entry.race)} '
|
||||
'${GameDataL10n.getKlassName(context, entry.klass)} '
|
||||
'Lv.${entry.level}',
|
||||
style: TextStyle(
|
||||
fontFamily: 'PressStart2P',
|
||||
fontSize: compact ? 5 : 7,
|
||||
color: RetroColors.textSecondaryOf(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildScoreColumn(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
'$score',
|
||||
style: TextStyle(
|
||||
fontFamily: 'PressStart2P',
|
||||
fontSize: 10,
|
||||
color: RetroColors.goldOf(context),
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'SCORE',
|
||||
style: TextStyle(
|
||||
fontFamily: 'PressStart2P',
|
||||
fontSize: 6,
|
||||
color: RetroColors.textMutedOf(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Color _getRankColor(int rank) {
|
||||
switch (rank) {
|
||||
case 1:
|
||||
return Colors.amber.shade700;
|
||||
case 2:
|
||||
return Colors.grey.shade500;
|
||||
case 3:
|
||||
return Colors.brown.shade400;
|
||||
default:
|
||||
return Colors.blue.shade400;
|
||||
}
|
||||
}
|
||||
|
||||
IconData? _getRankIcon(int rank) {
|
||||
switch (rank) {
|
||||
case 1:
|
||||
return Icons.emoji_events;
|
||||
case 2:
|
||||
return Icons.workspace_premium;
|
||||
case 3:
|
||||
return Icons.military_tech;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user