feat(ui): Phase 8 UI/UX 개선 완료
- CombatLog 위젯 게임 화면에 통합 - HP/MP 바 추가 (HP < 20% 깜빡임 효과) - SkillPanel 추가 (쿨타임 완료 시 glow 효과) - combatLog 로컬라이제이션 (4개 언어) - 테스트 수정 (skipOffstage 처리)
This commit is contained in:
@@ -93,6 +93,9 @@
|
||||
"encumbrance": "Encumbrance",
|
||||
"@encumbrance": { "description": "Encumbrance section title" },
|
||||
|
||||
"combatLog": "Combat Log",
|
||||
"@combatLog": { "description": "Combat log panel title" },
|
||||
|
||||
"plotDevelopment": "Plot Development",
|
||||
"@plotDevelopment": { "description": "Plot development panel title" },
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"equipment": "Equipment",
|
||||
"inventory": "Inventory",
|
||||
"encumbrance": "Encumbrance",
|
||||
"combatLog": "戦闘ログ",
|
||||
"plotDevelopment": "Plot Development",
|
||||
"quests": "Quests",
|
||||
"traitName": "Name",
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"equipment": "장비",
|
||||
"inventory": "인벤토리",
|
||||
"encumbrance": "적재량",
|
||||
"combatLog": "전투 로그",
|
||||
"plotDevelopment": "스토리 진행",
|
||||
"quests": "퀘스트",
|
||||
"traitName": "이름",
|
||||
|
||||
@@ -275,6 +275,12 @@ abstract class L10n {
|
||||
/// **'Encumbrance'**
|
||||
String get encumbrance;
|
||||
|
||||
/// Combat log panel title
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Combat Log'**
|
||||
String get combatLog;
|
||||
|
||||
/// Plot development panel title
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
|
||||
@@ -97,6 +97,9 @@ class L10nEn extends L10n {
|
||||
@override
|
||||
String get encumbrance => 'Encumbrance';
|
||||
|
||||
@override
|
||||
String get combatLog => 'Combat Log';
|
||||
|
||||
@override
|
||||
String get plotDevelopment => 'Plot Development';
|
||||
|
||||
|
||||
@@ -97,6 +97,9 @@ class L10nJa extends L10n {
|
||||
@override
|
||||
String get encumbrance => 'Encumbrance';
|
||||
|
||||
@override
|
||||
String get combatLog => '戦闘ログ';
|
||||
|
||||
@override
|
||||
String get plotDevelopment => 'Plot Development';
|
||||
|
||||
|
||||
@@ -97,6 +97,9 @@ class L10nKo extends L10n {
|
||||
@override
|
||||
String get encumbrance => '적재량';
|
||||
|
||||
@override
|
||||
String get combatLog => '전투 로그';
|
||||
|
||||
@override
|
||||
String get plotDevelopment => '스토리 진행';
|
||||
|
||||
|
||||
@@ -97,6 +97,9 @@ class L10nZh extends L10n {
|
||||
@override
|
||||
String get encumbrance => 'Encumbrance';
|
||||
|
||||
@override
|
||||
String get combatLog => '战斗日志';
|
||||
|
||||
@override
|
||||
String get plotDevelopment => 'Plot Development';
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"equipment": "Equipment",
|
||||
"inventory": "Inventory",
|
||||
"encumbrance": "Encumbrance",
|
||||
"combatLog": "战斗日志",
|
||||
"plotDevelopment": "Plot Development",
|
||||
"quests": "Quests",
|
||||
"traitName": "Name",
|
||||
|
||||
@@ -10,7 +10,10 @@ import 'package:askiineverdie/src/core/notification/notification_service.dart';
|
||||
import 'package:askiineverdie/src/core/util/pq_logic.dart' as pq_logic;
|
||||
import 'package:askiineverdie/src/features/game/game_session_controller.dart';
|
||||
import 'package:askiineverdie/src/features/game/widgets/cinematic_view.dart';
|
||||
import 'package:askiineverdie/src/features/game/widgets/combat_log.dart';
|
||||
import 'package:askiineverdie/src/features/game/widgets/hp_mp_bar.dart';
|
||||
import 'package:askiineverdie/src/features/game/widgets/notification_overlay.dart';
|
||||
import 'package:askiineverdie/src/features/game/widgets/skill_panel.dart';
|
||||
import 'package:askiineverdie/src/features/game/widgets/stats_panel.dart';
|
||||
import 'package:askiineverdie/src/features/game/widgets/task_progress_panel.dart';
|
||||
|
||||
@@ -38,16 +41,28 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
||||
StoryAct _lastAct = StoryAct.prologue;
|
||||
bool _showingCinematic = false;
|
||||
|
||||
// Phase 8: 전투 로그 (Combat Log)
|
||||
final List<CombatLogEntry> _combatLogEntries = [];
|
||||
String _lastTaskCaption = '';
|
||||
|
||||
// 이전 상태 추적 (레벨업/퀘스트/Act 완료 감지용)
|
||||
int _lastLevel = 0;
|
||||
int _lastQuestCount = 0;
|
||||
int _lastPlotStageCount = 0;
|
||||
|
||||
void _checkSpecialEvents(GameState state) {
|
||||
// Phase 8: 태스크 변경 시 로그 추가
|
||||
final currentCaption = state.progress.currentTask.caption;
|
||||
if (currentCaption.isNotEmpty && currentCaption != _lastTaskCaption) {
|
||||
_addCombatLog(currentCaption, CombatLogType.normal);
|
||||
_lastTaskCaption = currentCaption;
|
||||
}
|
||||
|
||||
// 레벨업 감지
|
||||
if (state.traits.level > _lastLevel && _lastLevel > 0) {
|
||||
_specialAnimation = AsciiAnimationType.levelUp;
|
||||
_notificationService.showLevelUp(state.traits.level);
|
||||
_addCombatLog('Level Up! Now level ${state.traits.level}', CombatLogType.levelUp);
|
||||
_resetSpecialAnimationAfterFrame();
|
||||
|
||||
// Phase 9: Act 변경 감지 (레벨 기반)
|
||||
@@ -68,6 +83,7 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
||||
.lastOrNull;
|
||||
if (completedQuest != null) {
|
||||
_notificationService.showQuestComplete(completedQuest.caption);
|
||||
_addCombatLog('Quest Complete: ${completedQuest.caption}', CombatLogType.questComplete);
|
||||
}
|
||||
_resetSpecialAnimationAfterFrame();
|
||||
}
|
||||
@@ -83,6 +99,19 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
||||
_lastPlotStageCount = state.progress.plotStageCount;
|
||||
}
|
||||
|
||||
/// Phase 8: 전투 로그 추가 (Add Combat Log Entry)
|
||||
void _addCombatLog(String message, CombatLogType type) {
|
||||
_combatLogEntries.add(CombatLogEntry(
|
||||
message: message,
|
||||
timestamp: DateTime.now(),
|
||||
type: type,
|
||||
));
|
||||
// 최대 50개 유지
|
||||
if (_combatLogEntries.length > 50) {
|
||||
_combatLogEntries.removeAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 9: Act 시네마틱 표시 (Show Act Cinematic)
|
||||
Future<void> _showCinematicForAct(StoryAct act) async {
|
||||
if (_showingCinematic) return;
|
||||
@@ -309,6 +338,14 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
||||
_buildSectionHeader(l10n.stats),
|
||||
Expanded(flex: 2, child: StatsPanel(stats: state.stats)),
|
||||
|
||||
// Phase 8: HP/MP 바 (사망 위험 시 깜빡임)
|
||||
HpMpBar(
|
||||
hpCurrent: state.stats.hp,
|
||||
hpMax: state.stats.hpMax,
|
||||
mpCurrent: state.stats.mp,
|
||||
mpMax: state.stats.mpMax,
|
||||
),
|
||||
|
||||
// Experience 바
|
||||
_buildSectionHeader(l10n.experience),
|
||||
_buildProgressBar(
|
||||
@@ -323,6 +360,10 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
||||
// Spell Book
|
||||
_buildSectionHeader(l10n.spellBook),
|
||||
Expanded(flex: 2, child: _buildSpellsList(state)),
|
||||
|
||||
// Phase 8: 스킬 (Skills with cooldown glow)
|
||||
_buildSectionHeader('Skills'),
|
||||
Expanded(flex: 2, child: SkillPanel(skillSystem: state.skillSystem)),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -343,7 +384,7 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
||||
|
||||
// Inventory
|
||||
_buildPanelHeader(l10n.inventory),
|
||||
Expanded(flex: 3, child: _buildInventoryList(state)),
|
||||
Expanded(flex: 2, child: _buildInventoryList(state)),
|
||||
|
||||
// Encumbrance 바
|
||||
_buildSectionHeader(l10n.encumbrance),
|
||||
@@ -352,6 +393,10 @@ class _GamePlayScreenState extends State<GamePlayScreen>
|
||||
state.progress.encumbrance.max,
|
||||
Colors.orange,
|
||||
),
|
||||
|
||||
// Phase 8: 전투 로그 (Combat Log)
|
||||
_buildPanelHeader(l10n.combatLog),
|
||||
Expanded(flex: 2, child: CombatLog(entries: _combatLogEntries)),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
163
lib/src/features/game/widgets/hp_mp_bar.dart
Normal file
163
lib/src/features/game/widgets/hp_mp_bar.dart
Normal file
@@ -0,0 +1,163 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// HP/MP 바 위젯 (Phase 8: 사망 위험 시 깜빡임)
|
||||
///
|
||||
/// HP가 20% 미만일 때 빨간색 깜빡임 효과 표시
|
||||
class HpMpBar extends StatefulWidget {
|
||||
const HpMpBar({
|
||||
super.key,
|
||||
required this.hpCurrent,
|
||||
required this.hpMax,
|
||||
required this.mpCurrent,
|
||||
required this.mpMax,
|
||||
});
|
||||
|
||||
final int hpCurrent;
|
||||
final int hpMax;
|
||||
final int mpCurrent;
|
||||
final int mpMax;
|
||||
|
||||
@override
|
||||
State<HpMpBar> createState() => _HpMpBarState();
|
||||
}
|
||||
|
||||
class _HpMpBarState extends State<HpMpBar> with SingleTickerProviderStateMixin {
|
||||
late AnimationController _blinkController;
|
||||
late Animation<double> _blinkAnimation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_blinkController = AnimationController(
|
||||
duration: const Duration(milliseconds: 500),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_blinkAnimation = Tween<double>(begin: 1.0, end: 0.3).animate(
|
||||
CurvedAnimation(parent: _blinkController, curve: Curves.easeInOut),
|
||||
);
|
||||
|
||||
_updateBlinkState();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(HpMpBar oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
_updateBlinkState();
|
||||
}
|
||||
|
||||
void _updateBlinkState() {
|
||||
final hpRatio = widget.hpMax > 0 ? widget.hpCurrent / widget.hpMax : 1.0;
|
||||
|
||||
// HP < 20% 시 깜박임 시작
|
||||
if (hpRatio < 0.2 && hpRatio > 0) {
|
||||
if (!_blinkController.isAnimating) {
|
||||
_blinkController.repeat(reverse: true);
|
||||
}
|
||||
} else {
|
||||
_blinkController.stop();
|
||||
_blinkController.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_blinkController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hpRatio = widget.hpMax > 0 ? widget.hpCurrent / widget.hpMax : 0.0;
|
||||
final mpRatio = widget.mpMax > 0 ? widget.mpCurrent / widget.mpMax : 0.0;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// HP 바
|
||||
_buildBar(
|
||||
label: 'HP',
|
||||
current: widget.hpCurrent,
|
||||
max: widget.hpMax,
|
||||
ratio: hpRatio,
|
||||
color: Colors.red,
|
||||
isLow: hpRatio < 0.2 && hpRatio > 0,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
// MP 바
|
||||
_buildBar(
|
||||
label: 'MP',
|
||||
current: widget.mpCurrent,
|
||||
max: widget.mpMax,
|
||||
ratio: mpRatio,
|
||||
color: Colors.blue,
|
||||
isLow: false,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBar({
|
||||
required String label,
|
||||
required int current,
|
||||
required int max,
|
||||
required double ratio,
|
||||
required Color color,
|
||||
required bool isLow,
|
||||
}) {
|
||||
final bar = Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24,
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(fontSize: 10, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
child: LinearProgressIndicator(
|
||||
value: ratio.clamp(0.0, 1.0),
|
||||
backgroundColor: color.withValues(alpha: 0.2),
|
||||
valueColor: AlwaysStoppedAnimation<Color>(color),
|
||||
minHeight: 10,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
SizedBox(
|
||||
width: 60,
|
||||
child: Text(
|
||||
'$current/$max',
|
||||
style: const TextStyle(fontSize: 9),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// HP < 20% 시 깜박임 효과 적용
|
||||
if (isLow) {
|
||||
return AnimatedBuilder(
|
||||
animation: _blinkAnimation,
|
||||
builder: (context, child) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.red.withValues(alpha: (1 - _blinkAnimation.value) * 0.3),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: bar,
|
||||
);
|
||||
}
|
||||
|
||||
return bar;
|
||||
}
|
||||
}
|
||||
244
lib/src/features/game/widgets/skill_panel.dart
Normal file
244
lib/src/features/game/widgets/skill_panel.dart
Normal file
@@ -0,0 +1,244 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:askiineverdie/data/skill_data.dart';
|
||||
import 'package:askiineverdie/src/core/model/game_state.dart';
|
||||
import 'package:askiineverdie/src/core/model/skill.dart';
|
||||
|
||||
/// 스킬 패널 위젯 (Phase 8: 쿨타임 완료 시 빛남 효과)
|
||||
///
|
||||
/// 스킬 목록과 쿨타임 상태를 표시
|
||||
class SkillPanel extends StatefulWidget {
|
||||
const SkillPanel({super.key, required this.skillSystem});
|
||||
|
||||
final SkillSystemState skillSystem;
|
||||
|
||||
@override
|
||||
State<SkillPanel> createState() => _SkillPanelState();
|
||||
}
|
||||
|
||||
class _SkillPanelState extends State<SkillPanel>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _glowController;
|
||||
late Animation<double> _glowAnimation;
|
||||
|
||||
// 이전 쿨타임 완료 상태 추적
|
||||
final Map<String, bool> _previousReadyState = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_glowController = AnimationController(
|
||||
duration: const Duration(milliseconds: 800),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_glowAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
|
||||
CurvedAnimation(parent: _glowController, curve: Curves.easeInOut),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(SkillPanel oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
_checkCooldownCompletion();
|
||||
}
|
||||
|
||||
void _checkCooldownCompletion() {
|
||||
// 쿨타임 완료된 스킬이 있으면 glow 애니메이션 시작
|
||||
for (final skillState in widget.skillSystem.skillStates) {
|
||||
final skill = _getSkillById(skillState.skillId);
|
||||
if (skill == null) continue;
|
||||
|
||||
final isReady = skillState.isReady(
|
||||
widget.skillSystem.elapsedMs,
|
||||
skill.cooldownMs,
|
||||
);
|
||||
final wasReady = _previousReadyState[skillState.skillId] ?? true;
|
||||
|
||||
// 쿨타임 완료 전환 감지
|
||||
if (isReady && !wasReady) {
|
||||
_glowController
|
||||
..reset()
|
||||
..repeat(reverse: true);
|
||||
|
||||
// 2초 후 glow 중지
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
if (mounted) {
|
||||
_glowController.stop();
|
||||
_glowController.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_previousReadyState[skillState.skillId] = isReady;
|
||||
}
|
||||
}
|
||||
|
||||
Skill? _getSkillById(String id) {
|
||||
return SkillData.allSkills.where((s) => s.id == id).firstOrNull;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_glowController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final skillStates = widget.skillSystem.skillStates;
|
||||
|
||||
if (skillStates.isEmpty) {
|
||||
return const Center(
|
||||
child: Text('No skills', style: TextStyle(fontSize: 11)),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: skillStates.length,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
itemBuilder: (context, index) {
|
||||
final skillState = skillStates[index];
|
||||
final skill = _getSkillById(skillState.skillId);
|
||||
if (skill == null) return const SizedBox.shrink();
|
||||
|
||||
final isReady = skillState.isReady(
|
||||
widget.skillSystem.elapsedMs,
|
||||
skill.cooldownMs,
|
||||
);
|
||||
final remainingMs = skillState.remainingCooldown(
|
||||
widget.skillSystem.elapsedMs,
|
||||
skill.cooldownMs,
|
||||
);
|
||||
|
||||
return _SkillRow(
|
||||
skill: skill,
|
||||
rank: skillState.rank,
|
||||
isReady: isReady,
|
||||
remainingMs: remainingMs,
|
||||
glowAnimation: _glowAnimation,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 개별 스킬 행 위젯
|
||||
class _SkillRow extends StatelessWidget {
|
||||
const _SkillRow({
|
||||
required this.skill,
|
||||
required this.rank,
|
||||
required this.isReady,
|
||||
required this.remainingMs,
|
||||
required this.glowAnimation,
|
||||
});
|
||||
|
||||
final Skill skill;
|
||||
final int rank;
|
||||
final bool isReady;
|
||||
final int remainingMs;
|
||||
final Animation<double> glowAnimation;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cooldownText = isReady
|
||||
? 'Ready'
|
||||
: '${(remainingMs / 1000).toStringAsFixed(1)}s';
|
||||
|
||||
final skillIcon = _getSkillIcon(skill.type);
|
||||
final skillColor = _getSkillColor(skill.type);
|
||||
|
||||
Widget row = Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||
child: Row(
|
||||
children: [
|
||||
// 스킬 아이콘
|
||||
Icon(skillIcon, size: 14, color: skillColor),
|
||||
const SizedBox(width: 4),
|
||||
// 스킬 이름
|
||||
Expanded(
|
||||
child: Text(
|
||||
skill.name,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: isReady ? Colors.white : Colors.grey,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
// 랭크
|
||||
Text(
|
||||
'Lv.$rank',
|
||||
style: const TextStyle(fontSize: 9, color: Colors.grey),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
// 쿨타임 상태
|
||||
SizedBox(
|
||||
width: 40,
|
||||
child: Text(
|
||||
cooldownText,
|
||||
style: TextStyle(
|
||||
fontSize: 9,
|
||||
color: isReady ? Colors.green : Colors.orange,
|
||||
fontWeight: isReady ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
// 쿨타임 완료 시 glow 효과
|
||||
if (isReady) {
|
||||
return AnimatedBuilder(
|
||||
animation: glowAnimation,
|
||||
builder: (context, child) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: skillColor.withValues(alpha: glowAnimation.value * 0.5),
|
||||
blurRadius: 8 * glowAnimation.value,
|
||||
spreadRadius: 2 * glowAnimation.value,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: row,
|
||||
);
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
IconData _getSkillIcon(SkillType type) {
|
||||
switch (type) {
|
||||
case SkillType.attack:
|
||||
return Icons.flash_on;
|
||||
case SkillType.heal:
|
||||
return Icons.healing;
|
||||
case SkillType.buff:
|
||||
return Icons.arrow_upward;
|
||||
case SkillType.debuff:
|
||||
return Icons.arrow_downward;
|
||||
}
|
||||
}
|
||||
|
||||
Color _getSkillColor(SkillType type) {
|
||||
switch (type) {
|
||||
case SkillType.attack:
|
||||
return Colors.red;
|
||||
case SkillType.heal:
|
||||
return Colors.green;
|
||||
case SkillType.buff:
|
||||
return Colors.blue;
|
||||
case SkillType.debuff:
|
||||
return Colors.purple;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,10 +146,10 @@ void main() {
|
||||
_buildTestApp(GamePlayScreen(controller: controller)),
|
||||
);
|
||||
|
||||
// Stats 섹션 확인
|
||||
// Stats 섹션 확인 (스크롤로 인해 화면 밖에 있을 수 있음)
|
||||
expect(find.text('Stats'), findsOneWidget);
|
||||
expect(find.text('STR'), findsOneWidget);
|
||||
expect(find.text('CON'), findsOneWidget);
|
||||
expect(find.text('STR', skipOffstage: false), findsOneWidget);
|
||||
expect(find.text('CON', skipOffstage: false), findsOneWidget);
|
||||
|
||||
await controller.pause(saveOnStop: false);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user