Files
asciinevrdie/lib/src/features/game/widgets/active_buff_panel.dart
JiWoong Sul ff4ad4c9e7 feat(ui): UI 컴포넌트 및 위젯 개선
- 게임 플레이 화면 개선
- 캐로셀 네비게이션 개선
- 각 페이지 위젯 스타일 통일
- 레트로 스타일 공통 위젯 개선
2025-12-31 17:47:02 +09:00

206 lines
5.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:asciineverdie/data/game_text_l10n.dart' as l10n;
import 'package:asciineverdie/src/core/model/skill.dart';
/// 활성 버프 패널 위젯
///
/// 현재 적용 중인 버프 목록과 남은 시간을 표시.
class ActiveBuffPanel extends StatelessWidget {
const ActiveBuffPanel({
super.key,
required this.activeBuffs,
required this.currentMs,
});
final List<ActiveBuff> activeBuffs;
final int currentMs;
@override
Widget build(BuildContext context) {
if (activeBuffs.isEmpty) {
return Center(
child: Text(
l10n.uiNoActiveBuffs,
style: const TextStyle(
fontSize: 11,
color: Colors.grey,
fontStyle: FontStyle.italic,
),
),
);
}
return ListView.builder(
itemCount: activeBuffs.length,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
itemBuilder: (context, index) {
final buff = activeBuffs[index];
return _BuffRow(buff: buff, currentMs: currentMs);
},
);
}
}
/// 개별 버프 행 위젯
class _BuffRow extends StatelessWidget {
const _BuffRow({required this.buff, required this.currentMs});
final ActiveBuff buff;
final int currentMs;
@override
Widget build(BuildContext context) {
final remainingMs = buff.remainingDuration(currentMs);
final remainingSec = (remainingMs / 1000).toStringAsFixed(1);
final progress = remainingMs / buff.effect.durationMs;
final modifiers = _buildModifierList();
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
// 버프 아이콘
const Icon(Icons.trending_up, size: 14, color: Colors.lightBlue),
const SizedBox(width: 4),
// 버프 이름
Expanded(
child: Text(
buff.effect.name,
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
color: Colors.lightBlue,
),
overflow: TextOverflow.ellipsis,
),
),
// 남은 시간
Text(
'${remainingSec}s',
style: TextStyle(
fontSize: 10,
color: remainingMs < 3000 ? Colors.orange : Colors.grey,
fontWeight: remainingMs < 3000
? FontWeight.bold
: FontWeight.normal,
),
),
],
),
const SizedBox(height: 2),
// 남은 시간 프로그레스 바
ClipRRect(
borderRadius: BorderRadius.circular(2),
child: LinearProgressIndicator(
value: progress.clamp(0.0, 1.0),
minHeight: 3,
backgroundColor: Colors.grey.shade800,
valueColor: AlwaysStoppedAnimation(
progress > 0.3 ? Colors.lightBlue : Colors.orange,
),
),
),
// 효과 목록
if (modifiers.isNotEmpty) ...[
const SizedBox(height: 2),
Wrap(spacing: 6, runSpacing: 2, children: modifiers),
],
],
),
);
}
/// 버프 효과 목록 생성
List<Widget> _buildModifierList() {
final modifiers = <Widget>[];
final effect = buff.effect;
if (effect.atkModifier != 0) {
modifiers.add(
_ModifierChip(
label: l10n.statAtk,
value: effect.atkModifier,
isPositive: effect.atkModifier > 0,
),
);
}
if (effect.defModifier != 0) {
modifiers.add(
_ModifierChip(
label: l10n.statDef,
value: effect.defModifier,
isPositive: effect.defModifier > 0,
),
);
}
if (effect.criRateModifier != 0) {
modifiers.add(
_ModifierChip(
label: l10n.statCri,
value: effect.criRateModifier,
isPositive: effect.criRateModifier > 0,
),
);
}
if (effect.evasionModifier != 0) {
modifiers.add(
_ModifierChip(
label: l10n.statEva,
value: effect.evasionModifier,
isPositive: effect.evasionModifier > 0,
),
);
}
return modifiers;
}
}
/// 효과 칩 위젯
class _ModifierChip extends StatelessWidget {
const _ModifierChip({
required this.label,
required this.value,
required this.isPositive,
});
final String label;
final double value;
final bool isPositive;
@override
Widget build(BuildContext context) {
final color = isPositive ? Colors.green : Colors.red;
final sign = isPositive ? '+' : '';
final percent = (value * 100).round();
return Container(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(3),
),
child: Text(
'$label: $sign$percent%',
style: TextStyle(
fontSize: 8,
color: color,
fontWeight: FontWeight.w500,
),
),
);
}
}