feat: Add location display and category chips in pantry and inventory screens
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
This commit is contained in:
@@ -218,6 +218,13 @@ class _InventoryEditScreenState extends ConsumerState<InventoryEditScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _save() async {
|
Future<void> _save() async {
|
||||||
|
if (_selectedProductId == null) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text(context.l10n.inventorySelectProduct)),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!_formKey.currentState!.validate()) return;
|
if (!_formKey.currentState!.validate()) return;
|
||||||
setState(() => _saving = true);
|
setState(() => _saving = true);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -286,10 +286,12 @@ class _ForegroundTile extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
|
final location = item.location?.trim();
|
||||||
|
final hasLocation = location != null && location.isNotEmpty;
|
||||||
|
|
||||||
final subtitleText = [
|
final subtitleText = [
|
||||||
'${_fmtQty(item.quantity)} ${item.unit}',
|
'${_fmtQty(item.quantity)} ${item.unit}',
|
||||||
if (item.location != null && item.location!.isNotEmpty) item.location!,
|
if (hasLocation) location,
|
||||||
if (item.bestBeforeDate != null)
|
if (item.bestBeforeDate != null)
|
||||||
'Bäst före: ${_formatDate(item.bestBeforeDate!)}',
|
'Bäst före: ${_formatDate(item.bestBeforeDate!)}',
|
||||||
].join(' · ');
|
].join(' · ');
|
||||||
@@ -305,8 +307,12 @@ class _ForegroundTile extends ConsumerWidget {
|
|||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
title: Text(item.displayName),
|
title: Text(item.displayName),
|
||||||
// Subtitle: small swipe-hint icon + text on the same row.
|
// Subtitle: swipe hint + details on first line, category chip below.
|
||||||
subtitle: Row(
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
children: [
|
children: [
|
||||||
if (!selectableMode) ...[
|
if (!selectableMode) ...[
|
||||||
Icon(
|
Icon(
|
||||||
@@ -324,6 +330,30 @@ class _ForegroundTile extends ConsumerWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Chip(
|
||||||
|
label: Text(
|
||||||
|
'L1: ${item.l1Category}',
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
side: BorderSide(color: theme.colorScheme.outlineVariant),
|
||||||
|
backgroundColor: theme.colorScheme.surface,
|
||||||
|
labelStyle: theme.textTheme.bodySmall?.copyWith(
|
||||||
|
color: theme.colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
trailing: acting
|
trailing: acting
|
||||||
? const SizedBox(
|
? const SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
|
|||||||
@@ -219,6 +219,9 @@ class _PantryScreenState extends ConsumerState<PantryScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final colorScheme = theme.colorScheme;
|
||||||
|
final textTheme = theme.textTheme;
|
||||||
final pantryAsync = ref.watch(pantryProvider);
|
final pantryAsync = ref.watch(pantryProvider);
|
||||||
|
|
||||||
if (pantryAsync.isLoading) {
|
if (pantryAsync.isLoading) {
|
||||||
@@ -370,15 +373,43 @@ class _PantryScreenState extends ConsumerState<PantryScreen> {
|
|||||||
if (index == 1) return headerSection;
|
if (index == 1) return headerSection;
|
||||||
final item = filteredItems[index - 2];
|
final item = filteredItems[index - 2];
|
||||||
final l1Category = _resolveL1Category(item);
|
final l1Category = _resolveL1Category(item);
|
||||||
|
final location = item.location?.trim();
|
||||||
|
final hasLocation = location != null && location.isNotEmpty;
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(item.displayName),
|
title: Text(item.displayName),
|
||||||
subtitle: Text(
|
subtitle: Column(
|
||||||
[
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (hasLocation)
|
||||||
|
Text(
|
||||||
|
'Plats: $location',
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Chip(
|
||||||
|
label: Text(
|
||||||
'L1: $l1Category',
|
'L1: $l1Category',
|
||||||
if (item.location != null && item.location!.trim().isNotEmpty)
|
maxLines: 1,
|
||||||
'Plats: ${item.location}',
|
overflow: TextOverflow.ellipsis,
|
||||||
].join(' • '),
|
),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
side: BorderSide(color: colorScheme.outlineVariant),
|
||||||
|
backgroundColor: colorScheme.surface,
|
||||||
|
labelStyle: textTheme.bodySmall?.copyWith(
|
||||||
|
color: colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
|||||||
Reference in New Issue
Block a user