Files
receipity/lib/widgets/item_card.dart
T
2026-08-28 18:23:56 +02:00

128 lines
3.7 KiB
Dart

/// Tappable card used inside [ListView.builder] on Home and Search.
library;
import 'package:flutter/material.dart';
import '../models/item.dart';
import '../screens/item_detail_screen.dart';
import '../utils/helpers.dart';
import 'item_hero_badge.dart';
/// Displays one [Item] as a rounded, shadowed card that opens the detail route.
class ItemCard extends StatelessWidget {
const ItemCard({
super.key,
required this.item,
this.heroScope = 'home',
});
final Item item;
/// Prefix so Home and Search can show the same item without colliding Heroes.
final String heroScope;
String get heroTag => '$heroScope-${item.heroTag}';
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colors = theme.colorScheme;
return DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: theme.shadowColor.withValues(alpha: 0.08),
blurRadius: 18,
offset: const Offset(0, 8),
),
],
),
child: Material(
color: colors.surfaceContainerLow,
borderRadius: BorderRadius.circular(16),
child: InkWell(
borderRadius: BorderRadius.circular(16),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => ItemDetailScreen(
item: item,
heroTag: heroTag,
),
),
);
},
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ItemHeroBadge(item: item, heroTag: heroTag),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(item.title, style: theme.textTheme.titleMedium),
const SizedBox(height: 4),
Text(
item.subtitle,
style: theme.textTheme.bodyMedium,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 10),
Wrap(
spacing: 8,
runSpacing: 4,
children: [
_MetaChip(
label: item.category.label,
color: item.category.accent,
),
_MetaChip(
label: formatDate(item.createdAt),
color: colors.outline,
),
],
),
],
),
),
Icon(
Icons.chevron_right,
color: colors.outline,
),
],
),
),
),
),
);
}
}
class _MetaChip extends StatelessWidget {
const _MetaChip({required this.label, required this.color});
final String label;
final Color color;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: color.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(8),
),
child: Text(
label,
style: Theme.of(context).textTheme.labelMedium?.copyWith(color: color),
),
);
}
}