import 'dart:io'; import 'package:flutter/material.dart'; /// Display mode for the book grid item. enum ViewMode { relaxed, compact } /// Book grid item widget – displays a single book in the grid. /// Appearance branches based on [ViewMode]. class BookGridItem extends StatelessWidget { final Map book; final ViewMode viewMode; final VoidCallback? onTap; final VoidCallback? onLongPress; const BookGridItem({ super.key, required this.book, required this.viewMode, this.onTap, this.onLongPress, }); // ─── public build ──────────────────────────────────────────────────────── @override Widget build(BuildContext context) { return GestureDetector( onTap: onTap, onLongPress: onLongPress, child: switch (viewMode) { ViewMode.relaxed => _buildRelaxed(context), ViewMode.compact => _buildCompact(context), }, ); } // ─── mode helpers ───────────────────────────────────────────────────────── /// Relaxed: cover + title + author + progress bar. Widget _buildRelaxed(BuildContext context) { final title = book['title'] as String? ?? ''; final author = book['author'] as String? ?? ''; final progress = _readingProgress; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded(child: _buildCoverStack(context, fit: StackFit.expand)), const SizedBox(height: 12), Text( title, maxLines: 2, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodyMedium?.copyWith( fontWeight: FontWeight.w500, ), ), const SizedBox(height: 4), if (author.isNotEmpty) Text( author, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 12, ), ), if (progress > 0) Padding( padding: const EdgeInsets.only(top: 8), child: ClipRRect( borderRadius: BorderRadius.circular(2), child: LinearProgressIndicator( value: progress, minHeight: 3, ), ), ), ], ); } /// Compact: cover only, title gradient overlay + progress badge. Widget _buildCompact(BuildContext context) { final title = book['title'] as String? ?? ''; return _buildCoverStack( context, fit: StackFit.expand, extras: [ // Bottom gradient + title Positioned( bottom: 0, left: 0, right: 0, child: ClipRRect( borderRadius: const BorderRadius.vertical( bottom: Radius.circular(6), ), child: Container( padding: const EdgeInsets.fromLTRB(6, 32, 6, 6), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Colors.transparent, Colors.black87], ), ), child: Text( title, maxLines: 2, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.labelSmall?.copyWith( color: Colors.white, fontWeight: FontWeight.w500, shadows: [ const Shadow( color: Colors.black54, blurRadius: 2.0, offset: Offset(0, 1.0), ), ], ), ), ), ), ), // Progress badge (top-right) _buildProgressBadge(context), ], ); } // ─── shared cover stack ─────────────────────────────────────────────────── Widget _buildCoverStack( BuildContext context, { List extras = const [], StackFit fit = StackFit.loose, }) { final coverPath = book['cover_path'] as String?; final hasCover = coverPath != null && coverPath.isNotEmpty && File(coverPath).existsSync(); return Stack( fit: fit, children: [ Container( decoration: BoxDecoration(borderRadius: BorderRadius.circular(6)), clipBehavior: Clip.antiAlias, child: hasCover ? Image.file( File(coverPath), fit: BoxFit.cover, width: double.infinity, height: double.infinity, errorBuilder: (_, __, ___) => _buildPlaceholder(context), ) : _buildPlaceholder(context), ), ...extras, ], ); } Widget _buildPlaceholder(BuildContext context) { return Container( color: Theme.of(context).colorScheme.surfaceContainerHighest, child: Center( child: Icon( Icons.auto_stories_outlined, size: 36, color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.2), ), ), ); } // ─── badge helpers ──────────────────────────────────────────────────────── /// Progress badge (compact mode). Widget _buildProgressBadge(BuildContext context) { final progress = _readingProgress; if (progress <= 0) return const SizedBox.shrink(); return Positioned( top: 8, right: 8, child: ClipRRect( borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3), color: Theme.of(context).colorScheme.shadow.withValues(alpha: 0.8), child: Text( '${(progress * 100).toStringAsFixed(0)}%', style: const TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.w600, ), ), ), ), ); } // ─── helpers ────────────────────────────────────────────────────────────── double get _readingProgress => (book['reading_percentage'] as num?)?.toDouble() ?? 0.0; }