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

201 lines
8.5 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';
// Haptic via InkWell? No, explicit HapticFeedback used generally.
class SakeListItem extends ConsumerWidget {
final SakeItem sake;
final bool isMenuMode;
final int? index; // For ReorderableDragStartListener
const SakeListItem({
super.key,
required this.sake,
required this.isMenuMode,
this.index,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final isSelected = ref.watch(selectedMenuSakeIdsProvider).contains(sake.id);
final isDark = Theme.of(context).brightness == Brightness.dark;
// Adaptive selection color
final selectedColor = isDark ? Colors.orange.withOpacity(0.3) : Colors.orange.shade50;
return Card(
clipBehavior: Clip.antiAlias,
elevation: 1, // Slight elevation
color: isMenuMode && isSelected ? selectedColor : null,
shape: isMenuMode && isSelected
? RoundedRectangleBorder(side: const BorderSide(color: Colors.orange, width: 2), borderRadius: BorderRadius.circular(12))
: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: InkWell(
onTap: () {
if (isMenuMode) {
ref.read(selectedMenuSakeIdsProvider.notifier).toggle(sake.id);
return;
}
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SakeDetailScreen(sake: sake),
),
);
},
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Selection Checkbox for ListView
if (isMenuMode)
Padding(
padding: const EdgeInsets.symmetric(vertical: AppTheme.spacingXLarge, horizontal: AppTheme.spacingMedium),
child: Icon(
isSelected ? Icons.check_circle : Icons.check_circle_outline,
color: isSelected ? AppTheme.posimaiBlue : Colors.grey[300],
size: 28,
),
),
SizedBox(
width: 100,
height: 100,
child: Hero(
tag: sake.id,
child: sake.displayData.imagePaths.isNotEmpty
? Image.file(
File(sake.displayData.imagePaths.first),
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
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: isDark ? Colors.grey[800] : Colors.grey[300],
child: Center(
child: Icon(
LucideIcons.image,
size: 40,
color: isDark ? Colors.grey[600] : Colors.grey[500],
),
),
)),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(AppTheme.spacingMedium),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
children: [
// セット商品バッジ
if (sake.itemType == ItemType.set)
Container(
margin: const EdgeInsets.only(right: 6),
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,
),
),
),
Expanded(
child: Text(
sake.displayData.name,
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
color: (isMenuMode && isSelected && !isDark) ? Colors.brown[900] : null,
),
),
),
],
),
),
if (sake.userData.isFavorite && !isMenuMode)
const Icon(Icons.favorite, color: Colors.pink, size: 16),
],
),
const SizedBox(height: AppTheme.spacingTiny),
// Brand / Prefecture (セット商品では非表示)
if (sake.itemType != ItemType.set &&
(sake.displayData.brewery.isNotEmpty || sake.displayData.prefecture.isNotEmpty))
Row(
children: [
Expanded(
child: Text(
'${sake.displayData.brewery} / ${sake.displayData.prefecture}',
style: Theme.of(context).textTheme.bodySmall,
overflow: TextOverflow.ellipsis,
),
),
],
),
// セット商品の説明文表示
if (sake.itemType == ItemType.set && sake.displayData.catchCopy != null)
Text(
sake.displayData.catchCopy!,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontStyle: FontStyle.italic,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
if (!isMenuMode && sake.hiddenSpecs.flavorTags.isNotEmpty) ...[
const SizedBox(height: AppTheme.spacingSmall),
Wrap(
spacing: 4,
runSpacing: 4,
children: sake.hiddenSpecs.flavorTags.take(3).map((tag) => Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: isDark ? Colors.grey[800] : Colors.grey[200],
borderRadius: BorderRadius.circular(4),
),
child: Text(
tag,
style: TextStyle(fontSize: 10, color: isDark ? Colors.grey[300] : Colors.grey[800]),
),
)).toList(),
)
]
],
),
),
),
],
),
),
);
}
}