feat(arena): 아레나 전투 화면 및 위젯 개선

- ArenaBattleScreen UI 개선
- 장비 비교 리스트 기능 확장
This commit is contained in:
JiWoong Sul
2026-01-06 18:29:06 +09:00
parent 2efd50a09d
commit afc3c18ae4
2 changed files with 386 additions and 58 deletions

View File

@@ -9,7 +9,7 @@ import 'package:asciineverdie/src/shared/retro_colors.dart';
// 임시 문자열 (추후 l10n으로 이동)
const _myEquipmentTitle = 'MY EQUIPMENT';
const _enemyEquipmentTitle = 'ENEMY EQUIPMENT';
const _selectSlotLabel = 'SELECT';
const _selectedLabel = 'SELECTED';
const _recommendedLabel = 'BEST';
/// 좌우 대칭 장비 비교 리스트
@@ -50,6 +50,41 @@ class _ArenaEquipmentCompareListState extends State<ArenaEquipmentCompareList> {
/// 현재 확장된 슬롯 (탭하여 비교 중인 슬롯)
EquipmentSlot? _expandedSlot;
/// 스크롤 컨트롤러 (자동 스크롤용)
final ScrollController _scrollController = ScrollController();
/// 슬롯별 행 높이 (대략적 계산용)
static const double _rowHeight = 40.0;
static const double _expandedHeight = 200.0;
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
/// 선택된 슬롯으로 자동 스크롤
void _scrollToSlot(EquipmentSlot slot) {
final index = EquipmentSlot.values.indexOf(slot);
if (index < 0) return;
// 현재 확장된 슬롯까지의 높이 계산
double targetOffset = 0;
for (int i = 0; i < index; i++) {
targetOffset += _rowHeight;
if (_expandedSlot == EquipmentSlot.values[i]) {
targetOffset += _expandedHeight;
}
}
// 부드럽게 스크롤
_scrollController.animateTo(
targetOffset,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOutCubic,
);
}
@override
Widget build(BuildContext context) {
return Column(
@@ -60,6 +95,7 @@ class _ArenaEquipmentCompareListState extends State<ArenaEquipmentCompareList> {
// 장비 리스트
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount: EquipmentSlot.values.length,
itemBuilder: (context, index) {
final slot = EquipmentSlot.values[index];
@@ -137,9 +173,17 @@ class _ArenaEquipmentCompareListState extends State<ArenaEquipmentCompareList> {
// 슬롯 행 (좌우 대칭)
GestureDetector(
onTap: () {
// 탭하면 즉시 선택 + 확장 + 자동 스크롤
widget.onSlotSelected(slot);
setState(() {
_expandedSlot = isExpanded ? null : slot;
});
// 확장될 때만 스크롤 (다음 프레임에서 실행)
if (!isExpanded) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToSlot(slot);
});
}
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 4),
@@ -393,30 +437,37 @@ class _ArenaEquipmentCompareListState extends State<ArenaEquipmentCompareList> {
),
),
const SizedBox(height: 10),
// 선택 버튼
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
widget.onSlotSelected(slot);
setState(() => _expandedSlot = null);
},
style: ElevatedButton.styleFrom(
backgroundColor: RetroColors.goldOf(context),
foregroundColor: RetroColors.backgroundOf(context),
padding: const EdgeInsets.symmetric(vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
// 선택됨 인디케이터 (SELECT 버튼 대신)
Container(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
decoration: BoxDecoration(
color: RetroColors.goldOf(context).withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(4),
border: Border.all(
color: RetroColors.goldOf(context),
width: 2,
),
child: Text(
_selectSlotLabel,
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 7,
color: RetroColors.backgroundOf(context),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.check_circle,
color: RetroColors.goldOf(context),
size: 16,
),
),
const SizedBox(width: 6),
Text(
_selectedLabel,
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 7,
color: RetroColors.goldOf(context),
fontWeight: FontWeight.bold,
),
),
],
),
),
],