ponshu-room-lite/lib/widgets/home/sake_grid_item.dart

195 lines
7.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:io';
import '../../models/sake_item.dart';
import '../../providers/menu_providers.dart';
import '../../screens/sake_detail_screen.dart';
import '../../theme/app_theme.dart';
import 'package:lucide_icons/lucide_icons.dart';
class SakeGridItem extends ConsumerWidget {
final SakeItem sake;
final bool isMenuMode;
const SakeGridItem({
super.key,
required this.sake,
required this.isMenuMode,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isSelected = ref.watch(selectedMenuSakeIdsProvider).contains(sake.id);
return Card(
clipBehavior: Clip.antiAlias,
// Highlight selected
shape: isMenuMode && isSelected
? RoundedRectangleBorder(side: const BorderSide(color: Colors.orange, width: 3), borderRadius: BorderRadius.circular(12))
: null,
child: InkWell(
onTap: () {
if (isMenuMode) {
ref.read(selectedMenuSakeIdsProvider.notifier).toggle(sake.id);
return;
}
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SakeDetailScreen(sake: sake),
),
);
},
child: Stack(
fit: StackFit.expand,
children: [
Hero(
tag: sake.id,
child: sake.displayData.imagePaths.isNotEmpty
? Image.file(
File(sake.displayData.imagePaths.first),
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
final isDark = Theme.of(context).brightness == Brightness.dark;
return Container(
color: isDark ? Colors.grey[800] : Colors.grey[300],
child: Center(
child: Icon(
LucideIcons.imageOff,
color: isDark ? Colors.grey[600] : Colors.grey[500],
),
),
);
},
)
: (sake.itemType == ItemType.set
? Image.asset(
'assets/images/set_placeholder.png',
fit: BoxFit.cover,
)
: Container(
color: Theme.of(context).brightness == Brightness.dark
? Colors.grey[800]
: Colors.grey[300],
child: Center(
child: Icon(
LucideIcons.image,
size: 50,
color: Theme.of(context).brightness == Brightness.dark
? Colors.grey[600]
: Colors.grey[500],
),
),
)),
),
// Gradient Overlay for Text Visibility
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.all(AppTheme.spacingSmall),
color: Colors.black54, // Changed from gradient to solid for "Transparent Black" underlay request
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// セット商品バッジ
if (sake.itemType == ItemType.set)
Container(
margin: const EdgeInsets.only(bottom: 4),
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'セット',
style: TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
),
Text(
sake.displayData.name,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 14,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
// 通常銘柄のみ酒蔵/都道府県を表示
if (sake.itemType != ItemType.set &&
(sake.displayData.brewery.isNotEmpty || sake.displayData.prefecture.isNotEmpty))
Text(
'${sake.displayData.brewery} / ${sake.displayData.prefecture}',
style: const TextStyle(
color: Colors.white70,
fontSize: 10,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
// セット商品の説明文
if (sake.itemType == ItemType.set && sake.displayData.catchCopy != null)
Text(
sake.displayData.catchCopy!,
style: const TextStyle(
color: Colors.white70,
fontSize: 10,
fontStyle: FontStyle.italic,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
),
// Selection Checkbox Overlay
if (isMenuMode)
Positioned(
top: AppTheme.spacingSmall,
left: AppTheme.spacingSmall,
child: Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.2),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
child: Icon(
isSelected ? Icons.check_circle : Icons.check_circle_outline,
color: isSelected ? AppTheme.posimaiBlue : Colors.grey[400],
size: 32,
),
),
),
// Favorite Icon
if (sake.userData.isFavorite && !isMenuMode)
const Positioned(
top: AppTheme.spacingSmall,
right: AppTheme.spacingSmall,
child: Icon(
Icons.favorite,
color: Colors.pink,
size: 20,
),
),
],
),
),
);
}
}