Files
asciinevrdie/lib/src/features/settings/retro_settings_widgets.dart
JiWoong Sul 864a866039 refactor(ui): 위젯 분리 및 화면 개선
- game_play_screen에서 desktop 패널 위젯 분리
- death_overlay에서 death_buttons, death_combat_log 분리
- mobile_carousel_layout에서 mobile_options_menu 분리
- 아레나 위젯 개선 (arena_hp_bar, result_panel 등)
- settings_screen에서 retro_settings_widgets 분리
- 기타 위젯 리팩토링 및 import 경로 업데이트
2026-02-23 15:49:38 +09:00

382 lines
10 KiB
Dart

import 'package:flutter/material.dart';
import 'package:asciineverdie/src/shared/retro_colors.dart';
import 'package:asciineverdie/src/shared/widgets/retro_widgets.dart';
/// 설정 화면에서 사용하는 레트로 스타일 서브 위젯들
/// 섹션 타이틀
class RetroSectionTitle extends StatelessWidget {
const RetroSectionTitle({super.key, required this.title});
final String title;
@override
Widget build(BuildContext context) {
return Row(
children: [
Container(width: 4, height: 14, color: RetroColors.goldOf(context)),
const SizedBox(width: 8),
Text(
title.toUpperCase(),
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 10,
color: RetroColors.goldOf(context),
letterSpacing: 1,
),
),
],
);
}
}
/// 선택 가능한 아이템
class RetroSelectableItem extends StatelessWidget {
const RetroSelectableItem({
super.key,
required this.child,
required this.isSelected,
required this.onTap,
});
final Widget child;
final bool isSelected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
decoration: BoxDecoration(
color: isSelected
? RetroColors.goldOf(context).withValues(alpha: 0.15)
: Colors.transparent,
border: Border.all(
color: isSelected
? RetroColors.goldOf(context)
: RetroColors.borderOf(context),
width: isSelected ? 2 : 1,
),
),
child: child,
),
);
}
}
/// 볼륨 슬라이더
class RetroVolumeSlider extends StatelessWidget {
const RetroVolumeSlider({
super.key,
required this.label,
required this.icon,
required this.value,
required this.onChanged,
});
final String label;
final IconData icon;
final double value;
final ValueChanged<double> onChanged;
@override
Widget build(BuildContext context) {
final percentage = (value * 100).round();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
value == 0 ? Icons.volume_off : icon,
size: 14,
color: RetroColors.goldOf(context),
),
const SizedBox(width: 8),
Text(
label.toUpperCase(),
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 8,
color: RetroColors.textPrimaryOf(context),
),
),
const Spacer(),
Text(
'$percentage%',
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 8,
color: RetroColors.goldOf(context),
),
),
],
),
const SizedBox(height: 8),
RetroSlider(value: value, onChanged: onChanged),
],
);
}
}
/// 레트로 스타일 슬라이더
class RetroSlider extends StatelessWidget {
const RetroSlider({
super.key,
required this.value,
required this.onChanged,
});
final double value;
final ValueChanged<double> onChanged;
@override
Widget build(BuildContext context) {
return SliderTheme(
data: SliderThemeData(
trackHeight: 8,
activeTrackColor: RetroColors.goldOf(context),
inactiveTrackColor: RetroColors.borderOf(context),
thumbColor: RetroColors.goldLightOf(context),
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 8),
overlayColor: RetroColors.goldOf(context).withValues(alpha: 0.2),
trackShape: const RectangularSliderTrackShape(),
),
child: Slider(value: value, onChanged: onChanged, divisions: 10),
);
}
}
/// 디버그 토글
class RetroDebugToggle extends StatelessWidget {
const RetroDebugToggle({
super.key,
required this.icon,
required this.label,
required this.description,
required this.value,
required this.onChanged,
});
final IconData icon;
final String label;
final String description;
final bool value;
final ValueChanged<bool> onChanged;
@override
Widget build(BuildContext context) {
return Row(
children: [
Icon(icon, size: 14, color: RetroColors.textPrimaryOf(context)),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 8,
color: RetroColors.textPrimaryOf(context),
),
),
Text(
description,
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 6,
color: RetroColors.textMutedOf(context),
),
),
],
),
),
RetroToggle(value: value, onChanged: onChanged),
],
);
}
}
/// 레트로 스타일 토글
class RetroToggle extends StatelessWidget {
const RetroToggle({
super.key,
required this.value,
required this.onChanged,
});
final bool value;
final ValueChanged<bool> onChanged;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => onChanged(!value),
child: Container(
width: 44,
height: 24,
decoration: BoxDecoration(
color: value
? RetroColors.goldOf(context).withValues(alpha: 0.3)
: RetroColors.borderOf(context).withValues(alpha: 0.3),
border: Border.all(
color: value
? RetroColors.goldOf(context)
: RetroColors.borderOf(context),
width: 2,
),
),
child: AnimatedAlign(
duration: const Duration(milliseconds: 150),
alignment: value ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
width: 18,
height: 18,
margin: const EdgeInsets.all(1),
color: value
? RetroColors.goldOf(context)
: RetroColors.textMutedOf(context),
),
),
),
);
}
}
/// 레트로 스타일 칩
class RetroChip extends StatelessWidget {
const RetroChip({
super.key,
required this.label,
required this.isSelected,
required this.onTap,
});
final String label;
final bool isSelected;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: isSelected
? RetroColors.goldOf(context).withValues(alpha: 0.2)
: Colors.transparent,
border: Border.all(
color: isSelected
? RetroColors.goldOf(context)
: RetroColors.borderOf(context),
width: isSelected ? 2 : 1,
),
),
child: Text(
label,
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 8,
color: isSelected
? RetroColors.goldOf(context)
: RetroColors.textMutedOf(context),
),
),
),
);
}
}
/// 레트로 스타일 확인 다이얼로그
class RetroConfirmDialog extends StatelessWidget {
const RetroConfirmDialog({
super.key,
required this.title,
required this.message,
required this.confirmText,
required this.cancelText,
});
final String title;
final String message;
final String confirmText;
final String cancelText;
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.transparent,
child: Container(
constraints: const BoxConstraints(maxWidth: 360),
decoration: BoxDecoration(
color: RetroColors.backgroundOf(context),
border: Border.all(color: RetroColors.goldOf(context), width: 3),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 타이틀
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
color: RetroColors.goldOf(context).withValues(alpha: 0.2),
child: Text(
title,
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 10,
color: RetroColors.goldOf(context),
),
textAlign: TextAlign.center,
),
),
// 메시지
Padding(
padding: const EdgeInsets.all(16),
child: Text(
message,
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 8,
color: RetroColors.textPrimaryOf(context),
height: 1.8,
),
textAlign: TextAlign.center,
),
),
// 버튼
Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 12),
child: Row(
children: [
Expanded(
child: RetroTextButton(
text: cancelText,
isPrimary: false,
onPressed: () => Navigator.of(context).pop(false),
),
),
const SizedBox(width: 8),
Expanded(
child: RetroTextButton(
text: confirmText,
onPressed: () => Navigator.of(context).pop(true),
),
),
],
),
),
],
),
),
);
}
}