feat(ui): 반응형 레이아웃 및 저장 시스템 개선
## 반응형 레이아웃 - app.dart: 화면 크기별 레이아웃 분기 로직 추가 (+173 라인) - game_play_screen.dart: 반응형 UI 구조 개선 - layouts/, pages/ 디렉토리 추가 (새 레이아웃 시스템) - carousel_nav_bar.dart: 캐러셀 네비게이션 바 추가 - enhanced_animation_panel.dart: 향상된 애니메이션 패널 ## 저장 시스템 - save_manager.dart: 저장 관리 기능 확장 - save_repository.dart: 저장소 인터페이스 개선 - save_service.dart: 저장 서비스 로직 추가 ## UI 개선 - notification_service.dart: 알림 시스템 기능 확장 - notification_overlay.dart: 오버레이 UI 개선 - equipment_stats_panel.dart: 장비 스탯 패널 개선 - cinematic_view.dart: 시네마틱 뷰 개선 - new_character_screen.dart: 캐릭터 생성 화면 개선 ## 다국어 - game_text_l10n.dart: 텍스트 추가 (+182 라인) ## 테스트 - 관련 테스트 파일 업데이트
This commit is contained in:
121
lib/src/features/game/widgets/carousel_nav_bar.dart
Normal file
121
lib/src/features/game/widgets/carousel_nav_bar.dart
Normal file
@@ -0,0 +1,121 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:askiineverdie/data/game_text_l10n.dart' as l10n;
|
||||
|
||||
/// 캐로셀 페이지 인덱스
|
||||
enum CarouselPage {
|
||||
skills, // 0: 스킬
|
||||
inventory, // 1: 인벤토리
|
||||
equipment, // 2: 장비
|
||||
character, // 3: 캐릭터시트 (기본)
|
||||
combatLog, // 4: 전투로그
|
||||
quest, // 5: 퀘스트
|
||||
story, // 6: 스토리
|
||||
}
|
||||
|
||||
/// 캐로셀 네비게이션 바
|
||||
///
|
||||
/// 7개의 페이지 버튼을 표시하고 현재 페이지를 하이라이트.
|
||||
/// 버튼 탭 시 해당 페이지로 이동.
|
||||
class CarouselNavBar extends StatelessWidget {
|
||||
const CarouselNavBar({
|
||||
super.key,
|
||||
required this.currentPage,
|
||||
required this.onPageSelected,
|
||||
});
|
||||
|
||||
final int currentPage;
|
||||
final ValueChanged<int> onPageSelected;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 56,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
border: Border(top: BorderSide(color: Theme.of(context).dividerColor)),
|
||||
),
|
||||
child: Row(
|
||||
children: CarouselPage.values.map((page) {
|
||||
final isSelected = page.index == currentPage;
|
||||
return Expanded(
|
||||
child: _NavButton(
|
||||
page: page,
|
||||
isSelected: isSelected,
|
||||
onTap: () => onPageSelected(page.index),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 개별 네비게이션 버튼
|
||||
class _NavButton extends StatelessWidget {
|
||||
const _NavButton({
|
||||
required this.page,
|
||||
required this.isSelected,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
final CarouselPage page;
|
||||
final bool isSelected;
|
||||
final VoidCallback onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final (icon, label) = _getIconAndLabel(page);
|
||||
final theme = Theme.of(context);
|
||||
final color = isSelected
|
||||
? theme.colorScheme.primary
|
||||
: theme.colorScheme.onSurfaceVariant;
|
||||
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
decoration: isSelected
|
||||
? BoxDecoration(
|
||||
color: theme.colorScheme.primaryContainer.withValues(
|
||||
alpha: 0.5,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
)
|
||||
: null,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(icon, size: 20, color: color),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 9,
|
||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
||||
color: color,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 페이지별 아이콘과 라벨
|
||||
(IconData, String) _getIconAndLabel(CarouselPage page) {
|
||||
return switch (page) {
|
||||
CarouselPage.skills => (Icons.auto_fix_high, l10n.navSkills),
|
||||
CarouselPage.inventory => (Icons.inventory_2, l10n.navInventory),
|
||||
CarouselPage.equipment => (Icons.shield, l10n.navEquipment),
|
||||
CarouselPage.character => (Icons.person, l10n.navCharacter),
|
||||
CarouselPage.combatLog => (Icons.list_alt, l10n.navCombatLog),
|
||||
CarouselPage.story => (Icons.auto_stories, l10n.navStory),
|
||||
CarouselPage.quest => (Icons.flag, l10n.navQuest),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user