feat(front): 프론트 화면 개선 및 설정 저장소 추가

- front_screen_animation.dart: 프론트 화면 애니메이션 추가
- settings_repository.dart: 설정 저장소 구현
- front/widgets/: 프론트 화면 위젯 분리
- mobile_carousel_layout.dart: 모바일 레이아웃 개선
- app.dart: 앱 설정 개선
- game_text_l10n.dart: 텍스트 추가
This commit is contained in:
JiWoong Sul
2025-12-23 18:52:46 +09:00
parent e6af7dd91a
commit 549851f693
10 changed files with 722 additions and 183 deletions

View File

@@ -0,0 +1,169 @@
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:askiineverdie/src/core/animation/front_screen_animation.dart';
/// 프론트 화면용 Hero vs Glitch God ASCII 애니메이션 위젯
///
/// 작은 용사가 거대한 Glitch God에 맞서는 루프 애니메이션
/// 항상 검은 배경에 흰색 텍스트, 특수 효과만 컬러로 표시
class HeroVsBossAnimation extends StatefulWidget {
const HeroVsBossAnimation({super.key});
@override
State<HeroVsBossAnimation> createState() => _HeroVsBossAnimationState();
}
class _HeroVsBossAnimationState extends State<HeroVsBossAnimation> {
int _currentFrame = 0;
Timer? _timer;
final Random _random = Random();
@override
void initState() {
super.initState();
_startAnimation();
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
void _startAnimation() {
_timer = Timer.periodic(
const Duration(milliseconds: frontScreenAnimationIntervalMs),
(_) {
if (mounted) {
setState(() {
_currentFrame =
(_currentFrame + 1) % frontScreenAnimationFrameCount;
});
}
},
);
}
/// 글리치 효과: 랜덤 문자 대체
String _applyGlitchEffect(String frame) {
// 10% 확률로 글리치 효과 적용
if (_random.nextDouble() > 0.1) return frame;
const glitchChars = '@#\$%&*!?~';
final chars = frame.split('');
final glitchCount = _random.nextInt(5) + 1;
for (var i = 0; i < glitchCount; i++) {
final pos = _random.nextInt(chars.length);
if (chars[pos] != ' ' && chars[pos] != '\n') {
chars[pos] = glitchChars[_random.nextInt(glitchChars.length)];
}
}
return chars.join();
}
@override
Widget build(BuildContext context) {
final frame = _applyGlitchEffect(
frontScreenAnimationFrames[_currentFrame],
);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
decoration: BoxDecoration(
// 항상 검은 배경
color: Colors.black,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Colors.white24,
width: 1,
),
// 은은한 글로우 효과
boxShadow: [
BoxShadow(
color: Colors.cyan.withValues(alpha: 0.15),
blurRadius: 20,
spreadRadius: 2,
),
],
),
child: Column(
children: [
// ASCII 애니메이션 (흰색 텍스트)
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
frame,
style: const TextStyle(
fontFamily: 'JetBrainsMono',
fontSize: 10,
height: 1.1,
color: Colors.white,
letterSpacing: 0,
),
),
),
const SizedBox(height: 8),
// 하단 효과 바 (컬러)
_buildEffectBar(),
],
),
);
}
/// 하단 효과 바: 글리치/전투 효과 시각화
Widget _buildEffectBar() {
// 프레임에 따라 색상 변화
final colors = [
Colors.cyan,
Colors.purple,
Colors.red,
Colors.cyan,
Colors.yellow,
Colors.purple,
];
final currentColor = colors[_currentFrame % colors.length];
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 왼쪽 검 아이콘 (용사)
Icon(
Icons.flash_on,
size: 14,
color: Colors.yellow.withValues(alpha: 0.8),
),
const SizedBox(width: 8),
// 중앙 효과 바
Expanded(
child: Container(
height: 3,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.transparent,
currentColor.withValues(alpha: 0.7),
Colors.white.withValues(alpha: 0.9),
currentColor.withValues(alpha: 0.7),
Colors.transparent,
],
),
borderRadius: BorderRadius.circular(2),
),
),
),
const SizedBox(width: 8),
// 오른쪽 보스 아이콘
Icon(
Icons.whatshot,
size: 14,
color: Colors.red.withValues(alpha: 0.8),
),
],
);
}
}