new plus button

This commit is contained in:
davmar
2026-08-31 09:40:38 +02:00
parent 3b698a3667
commit 92581e6d55
17 changed files with 1336 additions and 152 deletions
+95 -41
View File
@@ -11,6 +11,7 @@ import 'utils/money.dart';
import 'utils/supermarkets.dart';
import 'widgets/item_edit_sheet.dart';
import 'widgets/store_tag.dart';
import 'widgets/tag_editor.dart';
class ReviewScreen extends StatefulWidget {
const ReviewScreen({
@@ -42,6 +43,9 @@ class _ReviewScreenState extends State<ReviewScreen> {
String? _supermarket;
String? _customStore;
bool _saving = false;
final _tripName = TextEditingController();
List<String> _tags = [];
List<String> _tagSuggestions = [];
@override
void initState() {
@@ -57,6 +61,18 @@ class _ReviewScreenState extends State<ReviewScreen> {
} else if (widget.detectedStore != null) {
_supermarket = widget.detectedStore;
}
widget.repository
.getUsedShopTags()
.then((tags) {
if (mounted) setState(() => _tagSuggestions = tags);
})
.catchError((_) {});
}
@override
void dispose() {
_tripName.dispose();
super.dispose();
}
String? get _resolvedSupermarket {
@@ -117,6 +133,8 @@ class _ReviewScreenState extends State<ReviewScreen> {
shoppedAt: _shoppedAt,
items: _items,
receiptImagePath: widget.imagePath,
name: _tripName.text,
tags: _tags,
);
if (!mounted) return;
Navigator.of(context).pop(true);
@@ -214,52 +232,88 @@ class _ReviewScreenState extends State<ReviewScreen> {
),
),
Expanded(
child: _items.isEmpty
? Center(
child: Text(
'No line items yet.\nAdd products for this trip.',
textAlign: TextAlign.center,
style: theme.textTheme.bodyLarge,
child: CustomScrollView(
slivers: [
SliverPadding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
sliver: SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: _tripName,
enabled: !_saving,
textCapitalization: TextCapitalization.sentences,
decoration: const InputDecoration(
labelText: 'Trip name',
hintText: 'Optional, e.g. Weekly shop',
),
),
const SizedBox(height: 12),
TagEditor(
tags: _tags,
suggestions: _tagSuggestions,
enabled: !_saving,
onChanged: (tags) => setState(() => _tags = tags),
),
],
),
),
),
if (_items.isEmpty)
SliverFillRemaining(
hasScrollBody: false,
child: Center(
child: Text(
'No line items yet.\nAdd products for this trip.',
textAlign: TextAlign.center,
style: theme.textTheme.bodyLarge,
),
),
)
: ListView.separated(
padding: const EdgeInsets.fromLTRB(8, 8, 8, 8),
itemCount: _items.length,
separatorBuilder: (_, _) => const SizedBox(height: 4),
itemBuilder: (context, index) {
final item = _items[index];
return Dismissible(
key: ValueKey(item.id),
direction: DismissDirection.endToStart,
background: Container(
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
decoration: BoxDecoration(
color: theme.colorScheme.errorContainer,
borderRadius: BorderRadius.circular(12),
else
SliverPadding(
padding: const EdgeInsets.fromLTRB(8, 0, 8, 8),
sliver: SliverList.separated(
itemCount: _items.length,
separatorBuilder: (_, _) => const SizedBox(height: 4),
itemBuilder: (context, index) {
final item = _items[index];
return Dismissible(
key: ValueKey(item.id),
direction: DismissDirection.endToStart,
background: Container(
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
decoration: BoxDecoration(
color: theme.colorScheme.errorContainer,
borderRadius: BorderRadius.circular(12),
),
child: Icon(
Icons.delete_outline,
color: theme.colorScheme.onErrorContainer,
),
),
child: Icon(
Icons.delete_outline,
color: theme.colorScheme.onErrorContainer,
onDismissed: (_) {
setState(() {
_items.removeWhere((row) => row.id == item.id);
});
},
child: ListTile(
title: Text(item.name),
subtitle: _itemSubtitle(item),
trailing: Text(
formatEuro(item.price),
style: theme.textTheme.titleMedium,
),
onTap: _saving ? null : () => _edit(existing: item),
),
),
onDismissed: (_) {
setState(() {
_items.removeWhere((row) => row.id == item.id);
});
},
child: ListTile(
title: Text(item.name),
subtitle: _itemSubtitle(item),
trailing: Text(
formatEuro(item.price),
style: theme.textTheme.titleMedium,
),
onTap: _saving ? null : () => _edit(existing: item),
),
);
},
);
},
),
),
],
),
),
if (widget.rawText.isNotEmpty)
ExpansionTile(