- 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 경로 업데이트
162 lines
4.8 KiB
Dart
162 lines
4.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import 'package:asciineverdie/data/game_text_l10n.dart' as l10n;
|
|
import 'package:asciineverdie/l10n/app_localizations.dart';
|
|
import 'package:asciineverdie/src/shared/l10n/game_data_l10n.dart';
|
|
import 'package:asciineverdie/src/core/model/game_state.dart';
|
|
import 'package:asciineverdie/src/core/model/potion.dart';
|
|
import 'package:asciineverdie/src/features/game/widgets/potion_inventory_panel.dart';
|
|
|
|
/// 인벤토리 페이지 (캐로셀)
|
|
///
|
|
/// 골드, 아이템 목록, 물약 인벤토리, 무게 표시.
|
|
class InventoryPage extends StatelessWidget {
|
|
const InventoryPage({
|
|
super.key,
|
|
required this.inventory,
|
|
required this.potionInventory,
|
|
required this.encumbrance,
|
|
});
|
|
|
|
final Inventory inventory;
|
|
final PotionInventory potionInventory;
|
|
final ProgressBarState encumbrance;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final localizations = L10n.of(context);
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
// 인벤토리 (아이템)
|
|
_buildSectionHeader(context, localizations.inventory),
|
|
Expanded(flex: 2, child: _buildInventoryList(context)),
|
|
|
|
// 물약
|
|
_buildSectionHeader(context, l10n.uiPotions),
|
|
Expanded(
|
|
flex: 2,
|
|
child: PotionInventoryPanel(inventory: potionInventory),
|
|
),
|
|
|
|
// 무게 (Encumbrance)
|
|
_buildSectionHeader(context, localizations.encumbrance),
|
|
_buildProgressBar(context),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildSectionHeader(BuildContext context, String title) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
|
color: Theme.of(context).colorScheme.primaryContainer,
|
|
child: Text(
|
|
title,
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 18,
|
|
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildInventoryList(BuildContext context) {
|
|
final localizations = L10n.of(context);
|
|
|
|
return ListView.builder(
|
|
itemCount: inventory.items.length + 1, // +1 for gold
|
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
|
itemBuilder: (context, index) {
|
|
if (index == 0) {
|
|
// 골드 표시
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 3),
|
|
child: Row(
|
|
children: [
|
|
const Icon(
|
|
Icons.monetization_on,
|
|
size: 16,
|
|
color: Colors.amber,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
localizations.gold,
|
|
style: const TextStyle(fontSize: 18),
|
|
),
|
|
),
|
|
Text(
|
|
'${inventory.gold}',
|
|
style: const TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.amber,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
final item = inventory.items[index - 1];
|
|
final translatedName = GameDataL10n.translateItemString(
|
|
context,
|
|
item.name,
|
|
);
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 3),
|
|
child: Row(
|
|
children: [
|
|
const Icon(Icons.inventory_2, size: 16, color: Colors.grey),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
translatedName,
|
|
style: const TextStyle(fontSize: 18),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
Text(
|
|
'${item.count}',
|
|
style: const TextStyle(
|
|
fontSize: 17,
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildProgressBar(BuildContext context) {
|
|
final progress = encumbrance.max > 0
|
|
? (encumbrance.position / encumbrance.max).clamp(0.0, 1.0)
|
|
: 0.0;
|
|
final percentage = (progress * 100).toInt();
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.all(12),
|
|
child: Column(
|
|
children: [
|
|
LinearProgressIndicator(
|
|
value: progress,
|
|
backgroundColor: Colors.orange.withValues(alpha: 0.2),
|
|
valueColor: const AlwaysStoppedAnimation<Color>(Colors.orange),
|
|
minHeight: 12,
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
'$percentage%',
|
|
style: TextStyle(fontSize: 15, color: Colors.grey.shade600),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|