generated from dellevin/template
新增详情页显示样式
This commit is contained in:
@@ -148,10 +148,11 @@ class _BookDetailPageState extends State<BookDetailPage> {
|
|||||||
if (Breakpoint.isDesktop(context)) {
|
if (Breakpoint.isDesktop(context)) {
|
||||||
return _buildDesktopStyle(book, colors);
|
return _buildDesktopStyle(book, colors);
|
||||||
}
|
}
|
||||||
if (_detailStyle == 1) {
|
return switch (_detailStyle) {
|
||||||
return _buildOverlayStyle(book, colors);
|
1 => _buildOverlayStyle(book, colors),
|
||||||
}
|
2 => _buildMinimalLayeredStyle(book, colors),
|
||||||
return _buildStandardStyle(book, colors);
|
_ => _buildStandardStyle(book, colors),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 桌面端左右分栏布局
|
/// 桌面端左右分栏布局
|
||||||
@@ -972,6 +973,264 @@ class _BookDetailPageState extends State<BookDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 浅色极简层叠样式:封面卡片 + 浅色信息卡片层叠(无毛玻璃)
|
||||||
|
Widget _buildMinimalLayeredStyle(Book book, ColorScheme colors) {
|
||||||
|
final safeTop = MediaQuery.of(context).padding.top;
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: colors.surface,
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
// 整体可滚动(封面卡片 + 内容一起滑动)
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: safeTop + 48),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.fromLTRB(20, 8, 20, 120),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Center(child: _buildLayeredCover(book)),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildLayeredHeader(book),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildLayeredInfoRow('作者', book.authors.join(','), colors),
|
||||||
|
if (book.translators.isNotEmpty)
|
||||||
|
_buildLayeredInfoRow('译者', book.translators.join(','), colors),
|
||||||
|
if (book.isbn != null && book.isbn!.isNotEmpty)
|
||||||
|
_buildLayeredInfoRow('ISBN', book.isbn!, colors),
|
||||||
|
if (book.publisher != null && book.publisher!.isNotEmpty)
|
||||||
|
_buildLayeredInfoRow('出版社', book.publisher!, colors),
|
||||||
|
if (book.publishDate != null)
|
||||||
|
_buildLayeredInfoRow('出版时间', '${book.publishDate!.year}年${book.publishDate!.month.toString().padLeft(2, '0')}月', colors),
|
||||||
|
if (book.startDate != null || book.finishDate != null || book.readCount > 0)
|
||||||
|
_buildLayeredReadingDates(book),
|
||||||
|
if (book.genres.isNotEmpty)
|
||||||
|
_buildLayeredGenres(book),
|
||||||
|
CharacterPreviewSection(
|
||||||
|
characters: _characters,
|
||||||
|
onTap: _openCharacterSheet,
|
||||||
|
),
|
||||||
|
WorkPeopleSection(workId: book.id, workType: 'book'),
|
||||||
|
if (book.summary != null && book.summary!.isNotEmpty)
|
||||||
|
_buildLayeredSummary(book),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildLayeredExtraSections(book),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildStandardTopBar(book.title, colors),
|
||||||
|
Positioned(right: 16, bottom: 24, child: _buildFloatingActionButtons(book)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:居中封面卡片
|
||||||
|
Widget _buildLayeredCover(Book book) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
final hasCover = book.coverPath != null && book.coverPath!.isNotEmpty;
|
||||||
|
return Container(
|
||||||
|
width: 160, height: 230,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.surfaceContainerHighest,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
boxShadow: hasCover
|
||||||
|
? [BoxShadow(color: Colors.black.withValues(alpha: 0.12), blurRadius: 20, offset: const Offset(0, 8))]
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
child: hasCover
|
||||||
|
? FadeInLocalImage(path: book.coverPath, fit: BoxFit.cover)
|
||||||
|
: Center(child: Icon(Icons.menu_book, size: 48, color: colors.onSurface.withValues(alpha: 0.25))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:居中标题 + 评分/状态
|
||||||
|
Widget _buildLayeredHeader(Book book) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Text(book.title,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(fontSize: 22, fontWeight: FontWeight.w700, color: colors.onSurface, height: 1.3)),
|
||||||
|
if (book.alternateTitles.isNotEmpty) ...[
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Text(book.alternateTitles.join(' / '),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.4), height: 1.4)),
|
||||||
|
],
|
||||||
|
_buildEpubProgressBar(book),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Wrap(spacing: 8, runSpacing: 8, alignment: WrapAlignment.center, crossAxisAlignment: WrapCrossAlignment.center, children: [
|
||||||
|
if (book.rating != null) ...[
|
||||||
|
Icon(Icons.star, size: 20, color: colors.onSurface),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(book.rating!.toStringAsFixed(1),
|
||||||
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: colors.onSurface)),
|
||||||
|
],
|
||||||
|
_buildStatusTag(book),
|
||||||
|
]),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text('添加于 ${_formatDate(book.createdAt)}',
|
||||||
|
style: TextStyle(fontSize: 12, color: colors.onSurface.withValues(alpha: 0.4))),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:信息卡片(作者/译者/ISBN/出版社/出版时间)
|
||||||
|
Widget _buildLayeredInfoRow(String label, String value, ColorScheme colors) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: colors.outlineVariant, width: 0.5),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(width: 64, 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 _buildLayeredReadingDates(Book book) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: colors.outlineVariant, width: 0.5),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(width: 64, child: Text('阅读日期', style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.4)))),
|
||||||
|
Expanded(
|
||||||
|
child: Wrap(spacing: 12, runSpacing: 8, children: [
|
||||||
|
if (book.startDate != null) _buildDateChip('开始', book.startDate!, false),
|
||||||
|
if (book.finishDate != null) _buildDateChip('读完', book.finishDate!, false),
|
||||||
|
if (book.readCount > 0)
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||||
|
decoration: BoxDecoration(color: colors.surfaceContainerHighest, borderRadius: BorderRadius.circular(6)),
|
||||||
|
child: Text('共 ${book.readCount} 次', style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.7))),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:类型卡片
|
||||||
|
Widget _buildLayeredGenres(Book book) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: colors.outlineVariant, width: 0.5),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(width: 64, child: Text('类型', style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.4)))),
|
||||||
|
Expanded(child: Wrap(spacing: 8, runSpacing: 8,
|
||||||
|
children: book.genres.map((g) => Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
|
decoration: BoxDecoration(color: colors.surface, borderRadius: BorderRadius.circular(16)),
|
||||||
|
child: Text(g, style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.6))),
|
||||||
|
)).toList(),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:简介卡片
|
||||||
|
Widget _buildLayeredSummary(Book book) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: colors.outlineVariant, width: 0.5),
|
||||||
|
),
|
||||||
|
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
|
Row(children: [
|
||||||
|
Container(width: 4, height: 14, 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(book.summary!, style: TextStyle(fontSize: 15, color: colors.onSurface, height: 1.8)),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:更多(书评/摘抄/句读/角色)
|
||||||
|
Widget _buildLayeredExtraSections(Book book) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
|
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),
|
||||||
|
_buildExtraSectionItem(
|
||||||
|
icon: Icons.rate_review_outlined,
|
||||||
|
title: '书评',
|
||||||
|
subtitleFuture: context.read<AppProvider>().getBookReviewCount(book.id),
|
||||||
|
emptyText: '暂无书评',
|
||||||
|
unit: '条书评',
|
||||||
|
onTap: () => _navigateToReviews(book),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildExtraSectionItem(
|
||||||
|
icon: Icons.format_quote_outlined,
|
||||||
|
title: '摘抄',
|
||||||
|
subtitleFuture: context.read<AppProvider>().getBookExcerptCount(book.id),
|
||||||
|
emptyText: '暂无摘抄',
|
||||||
|
unit: '条摘抄',
|
||||||
|
onTap: () => _navigateToExcerpts(book),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildExtraSectionItem(
|
||||||
|
icon: Icons.highlight_outlined,
|
||||||
|
title: '句读',
|
||||||
|
subtitleFuture: _getEpubHighlightCount(book.id),
|
||||||
|
emptyText: '暂无句读',
|
||||||
|
unit: '条句读',
|
||||||
|
onTap: () => _navigateToEpubHighlights(book),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildExtraSectionItem(
|
||||||
|
icon: Icons.people_outline,
|
||||||
|
title: '角色',
|
||||||
|
subtitleFuture: context.read<AppProvider>().getBookCharacterCount(book.id),
|
||||||
|
emptyText: '暂无角色',
|
||||||
|
unit: '个角色',
|
||||||
|
onTap: () => _navigateToCharacters(book),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/// 叠层模式顶部:封面小图 + 标题/评分
|
/// 叠层模式顶部:封面小图 + 标题/评分
|
||||||
Widget _buildOverlayHeader(Book book) {
|
Widget _buildOverlayHeader(Book book) {
|
||||||
final hasCover = book.coverPath != null && book.coverPath!.isNotEmpty;
|
final hasCover = book.coverPath != null && book.coverPath!.isNotEmpty;
|
||||||
@@ -1310,9 +1569,9 @@ class _BookDetailPageState extends State<BookDetailPage> {
|
|||||||
|
|
||||||
void _showStylePicker() {
|
void _showStylePicker() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
const names = ['默认样式', '毛玻璃层叠'];
|
const names = ['默认样式', '毛玻璃层叠', '浅色极简'];
|
||||||
const icons = [Icons.article_outlined, Icons.blur_on_outlined];
|
const icons = [Icons.article_outlined, Icons.blur_on_outlined, Icons.layers_outlined];
|
||||||
const subtitles = ['标准封面顶部布局', '封面背景 + 毛玻璃卡片'];
|
const subtitles = ['标准封面顶部布局', '封面背景 + 毛玻璃卡片', '封面卡片 + 浅色信息卡片层叠'];
|
||||||
appModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -170,9 +170,11 @@ class _GameDetailPageState extends State<GameDetailPage> {
|
|||||||
if (Breakpoint.isDesktop(context)) {
|
if (Breakpoint.isDesktop(context)) {
|
||||||
return _buildDesktopStyle(game, colors);
|
return _buildDesktopStyle(game, colors);
|
||||||
}
|
}
|
||||||
return _detailStyle == 1
|
return switch (_detailStyle) {
|
||||||
? _buildOverlayStyle(game, colors)
|
1 => _buildOverlayStyle(game, colors),
|
||||||
: _buildStandardStyle(game, colors);
|
2 => _buildMinimalLayeredStyle(game, colors),
|
||||||
|
_ => _buildStandardStyle(game, colors),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 桌面端左右分栏布局
|
/// 桌面端左右分栏布局
|
||||||
@@ -1206,6 +1208,257 @@ class _GameDetailPageState extends State<GameDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 浅色极简层叠样式:封面卡片 + 浅色信息卡片层叠(无毛玻璃)
|
||||||
|
Widget _buildMinimalLayeredStyle(Game game, ColorScheme colors) {
|
||||||
|
final safeTop = MediaQuery.of(context).padding.top;
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: colors.surface,
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
// 整体可滚动(封面卡片 + 内容一起滑动)
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: safeTop + 48),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.fromLTRB(20, 8, 20, 120),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Center(child: _buildLayeredCover(game)),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildLayeredHeader(game),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
if (game.platforms.isNotEmpty)
|
||||||
|
_buildLayeredInfoRow('平台', game.platforms.join('、'), colors),
|
||||||
|
if (game.versions.isNotEmpty)
|
||||||
|
_buildLayeredInfoRow('版本', game.versions.join('、'), colors),
|
||||||
|
if (game.genres.isNotEmpty)
|
||||||
|
_buildLayeredGenres(game),
|
||||||
|
if (game.developer.isNotEmpty)
|
||||||
|
_buildLayeredInfoRow('开发者', game.developer.join('、'), colors),
|
||||||
|
if (game.releaseDate != null)
|
||||||
|
_buildLayeredInfoRow('发售时间', _formatDate(game.releaseDate!), colors),
|
||||||
|
if (game.playTimeHours > 0 || game.playTimeMinutes > 0)
|
||||||
|
_buildLayeredInfoRow('游玩时长', '${game.playTimeHours}小时${game.playTimeMinutes}分钟', colors),
|
||||||
|
if (game.playCount > 0)
|
||||||
|
_buildLayeredInfoRow('游玩次数', '${game.playCount} 次', colors),
|
||||||
|
if (game.purchasePlatforms.isNotEmpty)
|
||||||
|
_buildLayeredInfoRow('购买平台', game.purchasePlatforms.join('、'), colors),
|
||||||
|
if (game.purchaseDate != null)
|
||||||
|
_buildLayeredInfoRow('购买时间', _formatDate(game.purchaseDate!), colors),
|
||||||
|
if (game.purchasePrice != null && game.purchasePrice!.isNotEmpty)
|
||||||
|
_buildLayeredInfoRow('购买价格', game.purchasePrice!, colors),
|
||||||
|
CharacterPreviewSection(
|
||||||
|
characters: _characters,
|
||||||
|
onTap: _openCharacterSheet,
|
||||||
|
),
|
||||||
|
WorkPeopleSection(workId: game.id, workType: 'game'),
|
||||||
|
if (game.summary != null && game.summary!.isNotEmpty)
|
||||||
|
_buildLayeredSummary(game),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildLayeredExtraSections(game),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: 0, left: 0, right: 0,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(top: safeTop),
|
||||||
|
color: colors.surface,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 48,
|
||||||
|
child: Row(children: [
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
IconButton(
|
||||||
|
icon: widget.embedded
|
||||||
|
? 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)
|
||||||
|
: () => Navigator.pop(context),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Expanded(
|
||||||
|
child: Text(game.title,
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: colors.onSurface),
|
||||||
|
maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.tune, color: colors.onSurface, size: 20),
|
||||||
|
tooltip: '切换样式',
|
||||||
|
onPressed: _showStylePicker,
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(right: 16, bottom: 24, child: _buildFloatingActionButtons(game)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:居中封面卡片
|
||||||
|
Widget _buildLayeredCover(Game game) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
final hasCover = game.coverPath != null && game.coverPath!.isNotEmpty;
|
||||||
|
return Container(
|
||||||
|
width: 160, height: 230,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.surfaceContainerHighest,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
boxShadow: hasCover
|
||||||
|
? [BoxShadow(color: Colors.black.withValues(alpha: 0.12), blurRadius: 20, offset: const Offset(0, 8))]
|
||||||
|
: 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))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:居中标题 + 评分/状态/分类
|
||||||
|
Widget _buildLayeredHeader(Game game) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Text(game.title,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(fontSize: 22, fontWeight: FontWeight.w700, color: colors.onSurface, height: 1.3)),
|
||||||
|
if (game.genres.isNotEmpty) ...[
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Text(game.genres.join(' / '),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.4), height: 1.4)),
|
||||||
|
],
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Wrap(spacing: 8, runSpacing: 8, alignment: WrapAlignment.center, crossAxisAlignment: WrapCrossAlignment.center, children: [
|
||||||
|
if (game.rating != null) ...[
|
||||||
|
const Icon(Icons.star, size: 20, color: Colors.amber),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(game.rating!.toStringAsFixed(1),
|
||||||
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: colors.onSurface)),
|
||||||
|
],
|
||||||
|
_buildStatusTag(game),
|
||||||
|
_buildCategoryTag(game),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:信息卡片(平台/版本/开发者/发售时间等)
|
||||||
|
Widget _buildLayeredInfoRow(String label, String value, ColorScheme colors) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: colors.outlineVariant, width: 0.5),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(width: 72, 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 _buildLayeredGenres(Game game) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: colors.outlineVariant, width: 0.5),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(width: 72, 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.surface, borderRadius: BorderRadius.circular(16)),
|
||||||
|
child: Text(g, style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.6))),
|
||||||
|
)).toList(),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:简介卡片
|
||||||
|
Widget _buildLayeredSummary(Game game) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: colors.outlineVariant, width: 0.5),
|
||||||
|
),
|
||||||
|
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
|
Row(children: [
|
||||||
|
Container(width: 4, height: 14, 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)),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:更多(评价/截图/角色)
|
||||||
|
Widget _buildLayeredExtraSections(Game game) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
|
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),
|
||||||
|
_buildExtraSectionItem(
|
||||||
|
icon: Icons.rate_review_outlined,
|
||||||
|
title: '游戏评价',
|
||||||
|
subtitleFuture: context.read<AppProvider>().getGameReviewCount(game.id),
|
||||||
|
emptyText: '暂无评价',
|
||||||
|
unit: '条评价',
|
||||||
|
onTap: () => _navigateToReviews(game),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildExtraSectionItem(
|
||||||
|
icon: Icons.photo_library_outlined,
|
||||||
|
title: '游戏截图',
|
||||||
|
subtitleFuture: context.read<AppProvider>().getGameScreenshotCount(game.id),
|
||||||
|
emptyText: '暂无截图',
|
||||||
|
unit: '张截图',
|
||||||
|
onTap: () => _navigateToScreenshots(game),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildExtraSectionItem(
|
||||||
|
icon: Icons.people_outline,
|
||||||
|
title: '角色',
|
||||||
|
subtitleFuture: context.read<AppProvider>().getGameCharacterCount(game.id),
|
||||||
|
emptyText: '暂无角色',
|
||||||
|
unit: '个角色',
|
||||||
|
onTap: () => _navigateToCharacters(game),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildOverlayHeader(Game game) {
|
Widget _buildOverlayHeader(Game game) {
|
||||||
final hasCover = game.coverPath != null && game.coverPath!.isNotEmpty;
|
final hasCover = game.coverPath != null && game.coverPath!.isNotEmpty;
|
||||||
return Row(
|
return Row(
|
||||||
@@ -1870,9 +2123,9 @@ class _GameDetailPageState extends State<GameDetailPage> {
|
|||||||
void _showStylePicker() {
|
void _showStylePicker() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
final currentStyle = UserPrefs().detailPageStyle;
|
final currentStyle = UserPrefs().detailPageStyle;
|
||||||
const names = ['默认样式', '毛玻璃层叠'];
|
const names = ['默认样式', '毛玻璃层叠', '浅色极简'];
|
||||||
const icons = [Icons.article_outlined, Icons.blur_on_outlined];
|
const icons = [Icons.article_outlined, Icons.blur_on_outlined, Icons.layers_outlined];
|
||||||
const subtitles = ['标准封面顶部布局', '封面背景 + 毛玻璃卡片'];
|
const subtitles = ['标准封面顶部布局', '封面背景 + 毛玻璃卡片', '封面卡片 + 浅色信息卡片层叠'];
|
||||||
appModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -148,9 +148,11 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
|
|||||||
if (Breakpoint.isDesktop(context)) {
|
if (Breakpoint.isDesktop(context)) {
|
||||||
return _buildDesktopStyle(movie, colors);
|
return _buildDesktopStyle(movie, colors);
|
||||||
}
|
}
|
||||||
return _detailStyle == 1
|
return switch (_detailStyle) {
|
||||||
? _buildOverlayStyle(movie, colors)
|
1 => _buildOverlayStyle(movie, colors),
|
||||||
: _buildStandardStyle(movie, colors);
|
2 => _buildMinimalLayeredStyle(movie, colors),
|
||||||
|
_ => _buildStandardStyle(movie, colors),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 桌面端左右分栏布局
|
/// 桌面端左右分栏布局
|
||||||
@@ -1128,6 +1130,274 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 浅色极简层叠样式:海报卡片 + 浅色信息卡片层叠(无毛玻璃)
|
||||||
|
Widget _buildMinimalLayeredStyle(Movie movie, ColorScheme colors) {
|
||||||
|
final safeTop = MediaQuery.of(context).padding.top;
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: colors.surface,
|
||||||
|
body: Stack(
|
||||||
|
children: [
|
||||||
|
// 整体可滚动(海报卡片 + 内容一起滑动)
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: safeTop + 48),
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.fromLTRB(20, 8, 20, 120),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// 居中海报卡片
|
||||||
|
Center(child: _buildLayeredPoster(movie)),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildLayeredHeader(movie),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
if (movie.directors.isNotEmpty)
|
||||||
|
_buildLayeredInfoRow('导演', movie.directors.join(','), colors),
|
||||||
|
if (movie.writers.isNotEmpty)
|
||||||
|
_buildLayeredInfoRow('编剧', movie.writers.join(','), colors),
|
||||||
|
if (movie.actors.isNotEmpty)
|
||||||
|
_buildLayeredInfoRow('主演', movie.actors.join(','), colors),
|
||||||
|
if (movie.genres.isNotEmpty)
|
||||||
|
_buildLayeredGenres(movie),
|
||||||
|
CharacterPreviewSection(
|
||||||
|
characters: _characters,
|
||||||
|
onTap: _openCharacterSheet,
|
||||||
|
),
|
||||||
|
WorkPeopleSection(workId: movie.id, workType: 'movie'),
|
||||||
|
if (movie.summary != null && movie.summary!.isNotEmpty)
|
||||||
|
_buildLayeredSummary(movie),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
_buildLayeredExtraSections(movie),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 顶部导航栏
|
||||||
|
Positioned(
|
||||||
|
top: 0, left: 0, right: 0,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(top: safeTop),
|
||||||
|
color: colors.surface,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 48,
|
||||||
|
child: Row(children: [
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
IconButton(
|
||||||
|
icon: widget.embedded
|
||||||
|
? 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>().selectMovie(null)
|
||||||
|
: () => Navigator.pop(context),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Expanded(
|
||||||
|
child: Text(movie.title,
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: colors.onSurface),
|
||||||
|
maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||||
|
),
|
||||||
|
_buildStyleButton(color: colors.onSurface),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned(right: 16, bottom: 24, child: _buildFloatingActionButtons(movie)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:居中海报卡片
|
||||||
|
Widget _buildLayeredPoster(Movie movie) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
final hasPoster = movie.posterPath != null && movie.posterPath!.isNotEmpty;
|
||||||
|
return Container(
|
||||||
|
width: 160, height: 230,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.surfaceContainerHighest,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
boxShadow: hasPoster
|
||||||
|
? [BoxShadow(color: Colors.black.withValues(alpha: 0.12), blurRadius: 20, offset: const Offset(0, 8))]
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
child: hasPoster
|
||||||
|
? FadeInLocalImage(path: movie.posterPath, fit: BoxFit.cover)
|
||||||
|
: Center(child: Icon(Icons.movie_outlined, size: 48, color: colors.onSurface.withValues(alpha: 0.25))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:居中标题 + 评分/状态/分类
|
||||||
|
Widget _buildLayeredHeader(Movie movie) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Text(movie.title,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(fontSize: 22, fontWeight: FontWeight.w700, color: colors.onSurface, height: 1.3)),
|
||||||
|
if (movie.alternateTitles.isNotEmpty) ...[
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Text(movie.alternateTitles.join(' / '),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.4), height: 1.4)),
|
||||||
|
],
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Wrap(spacing: 8, runSpacing: 8, alignment: WrapAlignment.center, crossAxisAlignment: WrapCrossAlignment.center, children: [
|
||||||
|
if (movie.rating != null) ...[
|
||||||
|
Icon(Icons.star, size: 20, color: colors.onSurface),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(movie.rating!.toStringAsFixed(1),
|
||||||
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: colors.onSurface)),
|
||||||
|
],
|
||||||
|
_buildStatusTag(movie),
|
||||||
|
_buildCategoryTag(movie),
|
||||||
|
]),
|
||||||
|
if (movie.releaseDate != null) ...[
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: _toggleDateDisplay,
|
||||||
|
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||||
|
Text(
|
||||||
|
_showExactDate
|
||||||
|
? '${movie.releaseDate!.year}年${movie.releaseDate!.month.toString().padLeft(2, '0')}月${movie.releaseDate!.day.toString().padLeft(2, '0')}日上映'
|
||||||
|
: '${movie.releaseDate!.year}年${movie.releaseDate!.month.toString().padLeft(2, '0')}月上映',
|
||||||
|
style: TextStyle(fontSize: 14, color: colors.onSurface.withValues(alpha: 0.4)),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Icon(Icons.tune, size: 14, color: colors.onSurface.withValues(alpha: 0.2)),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (movie.watchDate != null) ...[
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text('观看于 ${_formatDate(movie.watchDate!)}',
|
||||||
|
style: TextStyle(fontSize: 14, color: colors.onSurface.withValues(alpha: 0.4))),
|
||||||
|
],
|
||||||
|
if (movie.watchCount > 0) ...[
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text('已观看 ${movie.watchCount} 次',
|
||||||
|
style: TextStyle(fontSize: 14, color: colors.onSurface.withValues(alpha: 0.4))),
|
||||||
|
],
|
||||||
|
if (movie.duration > 0) ...[
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(_formatDuration(movie.duration),
|
||||||
|
style: TextStyle(fontSize: 14, color: colors.onSurface.withValues(alpha: 0.4))),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:信息卡片(导演/编剧/主演)
|
||||||
|
Widget _buildLayeredInfoRow(String label, String value, ColorScheme colors) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: colors.outlineVariant, width: 0.5),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(width: 48, 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 _buildLayeredGenres(Movie movie) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 10),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: colors.outlineVariant, width: 0.5),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SizedBox(width: 48, child: Text('类型', style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.4)))),
|
||||||
|
Expanded(child: Wrap(spacing: 8, runSpacing: 8,
|
||||||
|
children: movie.genres.map((g) => Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||||
|
decoration: BoxDecoration(color: colors.surface, borderRadius: BorderRadius.circular(16)),
|
||||||
|
child: Text(g, style: TextStyle(fontSize: 13, color: colors.onSurface.withValues(alpha: 0.6))),
|
||||||
|
)).toList(),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:简介卡片
|
||||||
|
Widget _buildLayeredSummary(Movie movie) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
margin: const EdgeInsets.only(bottom: 12),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colors.surfaceContainerHigh,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: colors.outlineVariant, width: 0.5),
|
||||||
|
),
|
||||||
|
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
|
Row(children: [
|
||||||
|
Container(width: 4, height: 14, 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(movie.summary!, style: TextStyle(fontSize: 15, color: colors.onSurface, height: 1.8)),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 浅色极简:更多(影评/海报墙/角色)卡片标题
|
||||||
|
Widget _buildLayeredExtraSections(Movie movie) {
|
||||||
|
final colors = Theme.of(context).colorScheme;
|
||||||
|
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
|
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),
|
||||||
|
_buildExtraSectionItem(
|
||||||
|
icon: Icons.rate_review_outlined,
|
||||||
|
title: '影评',
|
||||||
|
subtitleFuture: context.read<AppProvider>().getMovieReviewCount(movie.id),
|
||||||
|
emptyText: '暂无影评',
|
||||||
|
unit: '条影评',
|
||||||
|
onTap: () => _navigateToReviews(movie),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildExtraSectionItem(
|
||||||
|
icon: Icons.photo_library_outlined,
|
||||||
|
title: '海报墙',
|
||||||
|
subtitleFuture: context.read<AppProvider>().getMoviePosterCount(movie.id),
|
||||||
|
emptyText: '暂无海报',
|
||||||
|
unit: '张海报',
|
||||||
|
onTap: () => _navigateToPosters(movie),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
_buildExtraSectionItem(
|
||||||
|
icon: Icons.people_outline,
|
||||||
|
title: '角色',
|
||||||
|
subtitleFuture: context.read<AppProvider>().getMovieCharacterCount(movie.id),
|
||||||
|
emptyText: '暂无角色',
|
||||||
|
unit: '个角色',
|
||||||
|
onTap: () => _navigateToCharacters(movie),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/// 叠层模式:封面小图 + 标题/评分
|
/// 叠层模式:封面小图 + 标题/评分
|
||||||
Widget _buildOverlayHeader(Movie movie) {
|
Widget _buildOverlayHeader(Movie movie) {
|
||||||
final hasPoster = movie.posterPath != null && movie.posterPath!.isNotEmpty;
|
final hasPoster = movie.posterPath != null && movie.posterPath!.isNotEmpty;
|
||||||
@@ -1195,9 +1465,9 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
|
|||||||
|
|
||||||
void _showStylePicker() {
|
void _showStylePicker() {
|
||||||
final colors = Theme.of(context).colorScheme;
|
final colors = Theme.of(context).colorScheme;
|
||||||
const names = ['默认样式', '毛玻璃层叠'];
|
const names = ['默认样式', '毛玻璃层叠', '浅色极简'];
|
||||||
const icons = [Icons.article_outlined, Icons.blur_on_outlined];
|
const icons = [Icons.article_outlined, Icons.blur_on_outlined, Icons.layers_outlined];
|
||||||
const subtitles = ['标准封面顶部布局', '封面背景 + 毛玻璃卡片'];
|
const subtitles = ['标准封面顶部布局', '封面背景 + 毛玻璃卡片', '海报卡片 + 浅色信息卡片层叠'];
|
||||||
appModalBottomSheet(
|
appModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: colors.surface,
|
backgroundColor: colors.surface,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: mooknote
|
name: mooknote
|
||||||
description: "app for tracking movies, books, and notes"
|
description: "app for tracking movies, books, and notes"
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 0.3.1
|
version: 0.3.2
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.5.0
|
sdk: ^3.5.0
|
||||||
|
|||||||
Reference in New Issue
Block a user