new plus button
This commit is contained in:
+95
-41
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user