import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:asciineverdie/src/core/animation/character_frames.dart'; import 'package:asciineverdie/src/core/animation/race_character_frames.dart'; /// 종족 미리보기 위젯 /// /// 새 캐릭터 생성 화면에서 선택한 종족의 idle 애니메이션을 보여줌. /// RichText 기반 색상 적용. class RacePreview extends StatefulWidget { const RacePreview({super.key, required this.raceId}); /// 종족 ID (예: "byte_human", "kernel_giant") final String raceId; @override State createState() => _RacePreviewState(); } class _RacePreviewState extends State { Timer? _timer; int _currentFrame = 0; @override void initState() { super.initState(); _startAnimation(); } @override void didUpdateWidget(RacePreview oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.raceId != widget.raceId) { _currentFrame = 0; } } @override void dispose() { _timer?.cancel(); super.dispose(); } void _startAnimation() { _timer = Timer.periodic(const Duration(milliseconds: 400), (_) { if (!mounted) return; // 프레임 빌드 중이면 다음 프레임까지 대기 (WASM 모드 안정성) if (SchedulerBinding.instance.schedulerPhase == SchedulerPhase.persistentCallbacks) { SchedulerBinding.instance.addPostFrameCallback((_) { if (mounted) { setState(() { _currentFrame++; }); } }); } else { setState(() { _currentFrame++; }); } }); } @override Widget build(BuildContext context) { final raceData = RaceCharacterFrames.get(widget.raceId); final frames = raceData?.idle ?? RaceCharacterFrames.defaultFrames.idle; final frame = frames[_currentFrame % frames.length]; return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.black, borderRadius: BorderRadius.circular(8), border: Border.all( color: Theme.of(context).colorScheme.outline.withValues(alpha: 0.3), ), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // 캐릭터 프레임 _buildColoredFrame(frame), ], ), ); } /// RichText 기반 색상 적용 프레임 Widget _buildColoredFrame(CharacterFrame frame) { return Column( mainAxisSize: MainAxisSize.min, children: frame.lines.map((line) => _buildColoredLine(line)).toList(), ); } /// 한 줄을 색상 적용하여 렌더링 Widget _buildColoredLine(String line) { final spans = []; for (var i = 0; i < line.length; i++) { final char = line[i]; spans.add( TextSpan( text: char, style: TextStyle( fontFamily: 'JetBrainsMono', fontSize: 18, height: 1.2, color: _getCharColor(char), ), ), ); } return RichText(text: TextSpan(children: spans)); } /// 문자별 색상 매핑 Color _getCharColor(String char) { // 공격/이펙트 (시안) if ('><=!+~'.contains(char)) return Colors.cyan; // 데미지/글리치 (마젠타) if ('*@#\$%&'.contains(char)) return const Color(0xFFFF00FF); // 특수 문자 (노랑) if ('☠◈◉'.contains(char)) return Colors.yellow; // 대형 문자 (밝은 녹색) if ('█▓░'.contains(char)) return Colors.lightGreen; // 기본 (흰색) return Colors.white; } }