windows版本初步完成

This commit is contained in:
DelLevin-Home
2026-07-13 19:39:01 +08:00
parent 121cbf8b0b
commit 6c30026e32
37 changed files with 5583 additions and 624 deletions

View File

@@ -8,6 +8,7 @@ import '../../providers/app_provider.dart';
import '../../models/data_models.dart';
import '../../utils/user_prefs.dart';
import '../../utils/toast_util.dart';
import '../../utils/responsive.dart';
import 'game_reviews_page.dart';
import 'game_screenshots_page.dart';
import 'game_share_page.dart';
@@ -73,11 +74,212 @@ class _GameDetailPageState extends State<GameDetailPage> {
.where((g) => g.id == widget.game.id)
.firstOrNull ?? widget.game;
if (Breakpoint.isDesktop(context)) {
return _buildDesktopStyle(game, colors);
}
return _detailStyle == 1
? _buildOverlayStyle(game, colors)
: _buildStandardStyle(game, colors);
}
/// 桌面端左右分栏布局
Widget _buildDesktopStyle(Game game, ColorScheme colors) {
final hasCover = game.coverPath != null && game.coverPath!.isNotEmpty;
return Scaffold(
backgroundColor: colors.surface,
body: Column(
children: [
// 顶栏
Container(
height: 48,
decoration: BoxDecoration(
color: colors.surface,
border: Border(bottom: BorderSide(color: colors.outlineVariant, width: 0.5)),
),
child: Row(children: [
IconButton(
icon: Icon(Icons.arrow_back, color: colors.onSurface, size: 18),
onPressed: widget.embedded
? () => context.read<AppProvider>().selectGame(null)
: () => Navigator.pop(context),
),
Expanded(
child: Text(game.title,
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: colors.onSurface),
maxLines: 1, overflow: TextOverflow.ellipsis),
),
const SizedBox(width: 4),
]),
),
// 主体:左封面 + 右信息
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 左侧封面
Container(
width: 240,
padding: const EdgeInsets.all(20),
child: Column(
children: [
Container(
width: 200,
height: 280,
decoration: BoxDecoration(
color: colors.surfaceContainerHighest,
borderRadius: BorderRadius.circular(12),
boxShadow: hasCover
? [BoxShadow(color: Colors.black.withValues(alpha: 0.1), blurRadius: 12, offset: const Offset(0, 4))]
: null,
),
clipBehavior: Clip.antiAlias,
child: hasCover
? FadeInLocalImage(path: game.coverPath, fit: BoxFit.cover)
: Center(child: Icon(Icons.sports_esports_outlined, size: 48, color: colors.onSurface.withValues(alpha: 0.25))),
),
],
),
),
// 右侧信息(可滚动)
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(0, 20, 24, 80),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(game.title,
style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: colors.onSurface, height: 1.3)),
const SizedBox(height: 16),
Row(children: [
if (game.rating != null) ...[
Icon(Icons.star, size: 20, color: colors.onSurface),
const SizedBox(width: 4),
Text(game.rating!.toStringAsFixed(1),
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: colors.onSurface)),
const SizedBox(width: 16),
],
_buildStatusTag(game),
const SizedBox(width: 6),
_buildCategoryTag(game),
]),
Divider(height: 32, thickness: 0.5, color: colors.outline),
// 详细信息
if (game.platforms.isNotEmpty)
_buildDesktopInfoRow('平台', game.platforms.join(''), colors),
if (game.versions.isNotEmpty)
_buildDesktopInfoRow('版本', game.versions.join(''), colors),
if (game.genres.isNotEmpty) ...[
const SizedBox(height: 8),
Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
SizedBox(width: 56, child: Text('类型', style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.4)))),
Expanded(child: Wrap(spacing: 8, runSpacing: 8,
children: game.genres.map((g) => Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(color: colors.surfaceContainerHighest, borderRadius: BorderRadius.circular(16)),
child: Text(g, style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.6))),
)).toList(),
)),
]),
],
if (game.playTimeHours > 0 || game.playTimeMinutes > 0)
_buildDesktopInfoRow('游玩时长', '${game.playTimeHours}小时${game.playTimeMinutes}分钟', colors),
if (game.purchasePlatforms.isNotEmpty)
_buildDesktopInfoRow('购买平台', game.purchasePlatforms.join(''), colors),
if (game.purchaseDate != null)
_buildDesktopInfoRow('购买时间', _formatDate(game.purchaseDate!), colors),
if (game.purchasePrice != null && game.purchasePrice!.isNotEmpty)
_buildDesktopInfoRow('购买价格', game.purchasePrice!, colors),
if (game.summary != null && game.summary!.isNotEmpty) ...[
Divider(height: 32, thickness: 0.5, color: colors.outline),
Row(children: [
Container(width: 4, height: 16, decoration: BoxDecoration(color: colors.onSurface, borderRadius: BorderRadius.circular(2))),
const SizedBox(width: 8),
Text('游戏简介', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: colors.onSurface)),
]),
const SizedBox(height: 12),
Text(game.summary!, style: TextStyle(fontSize: 15, color: colors.onSurface, height: 1.8)),
],
Divider(height: 32, thickness: 0.5, color: colors.outline),
Row(children: [
Container(width: 4, height: 16, decoration: BoxDecoration(color: colors.onSurface, borderRadius: BorderRadius.circular(2))),
const SizedBox(width: 8),
Text('更多', style: TextStyle(fontSize: 15, fontWeight: FontWeight.w600, color: colors.onSurface)),
]),
const SizedBox(height: 16),
_buildExtraSectionItem(
icon: Icons.rate_review_outlined,
title: '游戏评价',
subtitleFuture: context.read<AppProvider>().getGameReviewCount(game.id),
emptyText: '暂无评价',
unit: '条评价',
onTap: () => _navigateToReviews(game),
),
const SizedBox(height: 12),
_buildExtraSectionItem(
icon: Icons.photo_library_outlined,
title: '游戏截图',
subtitleFuture: context.read<AppProvider>().getGameScreenshotCount(game.id),
emptyText: '暂无截图',
unit: '张截图',
onTap: () => _navigateToScreenshots(game),
),
],
),
),
),
],
),
),
// 底部操作栏
Container(
height: 56,
decoration: BoxDecoration(
color: colors.surface,
border: Border(top: BorderSide(color: colors.outlineVariant, width: 0.5)),
),
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
OutlinedButton.icon(
onPressed: () => _showDeleteDialog(context),
icon: Icon(Icons.delete_outline, size: 16, color: colors.error),
label: Text('删除', style: TextStyle(color: colors.error)),
style: OutlinedButton.styleFrom(
side: BorderSide(color: colors.error.withValues(alpha: 0.3)),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
),
const SizedBox(width: 12),
FilledButton.icon(
onPressed: () => _navigateToEdit(context),
icon: const Icon(Icons.edit_outlined, size: 16),
label: const Text('编辑'),
style: FilledButton.styleFrom(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
),
],
),
),
],
),
);
}
Widget _buildDesktopInfoRow(String label, String value, ColorScheme colors) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(width: 56, child: Text(label, style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.4)))),
Expanded(child: Text(value, style: TextStyle(fontSize: 15, color: colors.onSurface, height: 1.5))),
],
),
);
}
Widget _buildStandardStyle(Game game, ColorScheme colors) {
final topSafe = MediaQuery.of(context).padding.top;
return Scaffold(
@@ -131,7 +333,7 @@ class _GameDetailPageState extends State<GameDetailPage> {
const SizedBox(width: 4),
IconButton(
icon: widget.embedded
? Icon(Icons.close, color: colors.onSurface, size: 18)
? Icon(Icons.arrow_back, color: colors.onSurface, size: 18)
: Icon(Icons.arrow_back_ios_new, color: colors.onSurface, size: 18),
onPressed: widget.embedded
? () => context.read<AppProvider>().selectGame(null)
@@ -204,7 +406,7 @@ class _GameDetailPageState extends State<GameDetailPage> {
const SizedBox(width: 4),
IconButton(
icon: widget.embedded
? const Icon(Icons.close, color: Colors.white, size: 18)
? const Icon(Icons.arrow_back, color: Colors.white, size: 18)
: const Icon(Icons.arrow_back_ios_new, color: Colors.white, size: 18),
onPressed: widget.embedded
? () => context.read<AppProvider>().selectGame(null)
@@ -466,14 +668,16 @@ class _GameDetailPageState extends State<GameDetailPage> {
backgroundColor: colors.error,
foregroundColor: colors.onError,
),
const SizedBox(height: 12),
_buildFloatingButton(
icon: Icons.share_outlined,
onPressed: () => _showSharePoster(game),
tooltip: '分享海报',
backgroundColor: const Color(0xFF4CAF50),
foregroundColor: Colors.white,
),
if (!Platform.isWindows) ...[
const SizedBox(height: 12),
_buildFloatingButton(
icon: Icons.share_outlined,
onPressed: () => _showSharePoster(game),
tooltip: '分享海报',
backgroundColor: const Color(0xFF4CAF50),
foregroundColor: Colors.white,
),
],
],
);
}

View File

@@ -1115,6 +1115,7 @@ class _GameFormPageState extends State<GameFormPage> {
);
await context.read<AppProvider>().addGame(newGame);
await context.read<AppProvider>().loadGames();
} else {
final updatedGame = widget.game!.copyWith(
title: _titleController.text.trim(),

View File

@@ -43,6 +43,7 @@ class _GameTabPageState extends State<GameTabPage> {
super.initState();
_scrollController = ScrollController()..addListener(_onScroll);
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
final provider = context.read<AppProvider>();
_provider = provider;
provider.addListener(_onDataChanged);