This commit is contained in:
DelLevin-Home
2026-07-16 03:13:30 +08:00
parent a8842fe91d
commit 1aab6858a7
12 changed files with 1217 additions and 1306 deletions

View File

@@ -24,13 +24,15 @@ class BookGridItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
return RepaintBoundary(
child: GestureDetector(
onTap: onTap,
onLongPress: onLongPress,
child: switch (viewMode) {
ViewMode.relaxed => _buildRelaxed(context),
ViewMode.compact => _buildCompact(context),
},
),
);
}
@@ -41,6 +43,7 @@ class BookGridItem extends StatelessWidget {
final colors = Theme.of(context).colorScheme;
final title = book['title'] as String? ?? '';
final author = book['author'] as String? ?? '';
final progress = _readingProgress;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -59,8 +62,16 @@ class BookGridItem extends StatelessWidget {
),
clipBehavior: Clip.antiAlias,
child: _buildCoverStack(context, fit: StackFit.expand, extras: [
// 阅读状态角标
_buildStatusBadge(context),
// 阅读状态圆点
_buildStatusDot(context),
// 底部进度条
if (progress > 0 && progress < 1.0)
Positioned(
bottom: 0,
left: 0,
right: 0,
child: _buildProgressBar(context, progress, height: 3),
),
]),
),
),
@@ -95,13 +106,22 @@ class BookGridItem extends StatelessWidget {
Widget _buildCompact(BuildContext context) {
final title = book['title'] as String? ?? '';
final author = book['author'] as String? ?? '';
final progress = _readingProgress;
return _buildCoverStack(
context,
fit: StackFit.expand,
extras: [
// 阅读状态角标
_buildStatusBadge(context),
// 阅读状态圆点
_buildStatusDot(context),
// 底部进度条
if (progress > 0 && progress < 1.0)
Positioned(
bottom: 0,
left: 0,
right: 0,
child: _buildProgressBar(context, progress, height: 3),
),
Positioned(
bottom: 0,
left: 0,
@@ -193,23 +213,30 @@ class BookGridItem extends StatelessWidget {
}
Widget _buildPlaceholder(BuildContext context) {
final colors = Theme.of(context).colorScheme;
final title = book['title'] as String? ?? '';
final initial = title.isNotEmpty ? title.substring(0, 1) : '';
// 根据书名首字生成渐变色
final gradientColors = _generateGradientColors(initial);
return Container(
color: colors.surfaceContainerHighest,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: gradientColors,
),
),
child: Center(
child: initial.isNotEmpty
? Text(initial,
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.w600,
color: colors.onSurface.withValues(alpha: 0.2),
color: Colors.white.withValues(alpha: 0.85),
))
: Icon(
Icons.auto_stories_outlined,
size: 36,
color: colors.onSurface.withValues(alpha: 0.2),
color: Colors.white.withValues(alpha: 0.5),
),
),
);
@@ -217,44 +244,76 @@ class BookGridItem extends StatelessWidget {
// ─── badge helpers ────────────────────────────────────────────────────────
/// 阅读状态角标(左上角,含百分比
Widget _buildStatusBadge(BuildContext context) {
/// 阅读状态圆点(左上角,三色
Widget _buildStatusDot(BuildContext context) {
final colors = Theme.of(context).colorScheme;
final progress = _readingProgress;
final String label;
final Color color;
final Color dotColor;
if (progress >= 1.0) {
label = '已读';
color = const Color(0xFF16A34A);
dotColor = const Color(0xFF16A34A);
} else if (progress > 0.0) {
label = '在读 ${(progress * 100).toInt()}%';
color = colors.primary;
dotColor = colors.primary;
} else {
label = '未读';
color = const Color(0xFFDC2626);
dotColor = const Color(0xFFDC2626);
}
return Positioned(
top: 6,
left: 6,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),
width: 8,
height: 8,
decoration: BoxDecoration(
color: color.withValues(alpha: 0.85),
borderRadius: BorderRadius.circular(4),
color: dotColor,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: dotColor.withValues(alpha: 0.4),
blurRadius: 3,
offset: const Offset(0, 1),
),
],
),
child: Text(label,
style: const TextStyle(
color: Colors.white,
fontSize: 9,
fontWeight: FontWeight.w600,
)),
),
);
}
/// 底部进度条
Widget _buildProgressBar(BuildContext context, double progress, {double height = 3}) {
final colors = Theme.of(context).colorScheme;
return Column(
mainAxisSize: MainAxisSize.min,
children: [
LinearProgressIndicator(
value: progress,
minHeight: height,
backgroundColor: Colors.black26,
valueColor: AlwaysStoppedAnimation<Color>(colors.primary),
borderRadius: BorderRadius.zero,
),
],
);
}
// ─── helpers ──────────────────────────────────────────────────────────────
double get _readingProgress =>
(book['reading_percentage'] as num?)?.toDouble() ?? 0.0;
/// 根据首字符生成渐变色
List<Color> _generateGradientColors(String initial) {
if (initial.isEmpty) return [const Color(0xFF6B7280), const Color(0xFF9CA3AF)];
final code = initial.codeUnitAt(0);
final palettes = [
[const Color(0xFF6366F1), const Color(0xFF8B5CF6)], // 靛蓝-紫
[const Color(0xFF3B82F6), const Color(0xFF06B6D4)], // 蓝-青
[const Color(0xFF10B981), const Color(0xFF34D399)], // 绿
[const Color(0xFFF59E0B), const Color(0xFFF97316)], // 琥珀-橙
[const Color(0xFFEF4444), const Color(0xFFF472B6)], // 红-粉
[const Color(0xFF8B5CF6), const Color(0xFFEC4899)], // 紫-粉
[const Color(0xFF14B8A6), const Color(0xFF3B82F6)], // 青-蓝
[const Color(0xFFF97316), const Color(0xFFEF4444)], // 橙-红
];
return palettes[code % palettes.length];
}
}