added categories, import export, settings

This commit is contained in:
davmar
2026-08-29 16:45:27 +02:00
parent 2a5a922aab
commit 732a0dc14a
30 changed files with 3700 additions and 167 deletions
+24 -21
View File
@@ -1,4 +1,4 @@
/// Single screen: capture or pick a receipt photo, then show raw OCR text.
/// Capture or pick a receipt photo, then review parsed line items.
library;
import 'dart:io';
@@ -8,16 +8,23 @@ import 'package:flutter/services.dart';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
import 'package:image_picker/image_picker.dart';
import 'products_screen.dart';
import 'review_screen.dart';
import 'services/app_settings.dart';
import 'services/product_repository.dart';
import 'services/receipt_parser.dart';
import 'utils/constants.dart';
import 'utils/supermarkets.dart';
import 'widgets/settings_button.dart';
class ScanScreen extends StatefulWidget {
const ScanScreen({super.key, required this.repository});
const ScanScreen({
super.key,
required this.repository,
this.onTripSaved,
});
final ProductRepository repository;
final VoidCallback? onTripSaved;
@override
State<ScanScreen> createState() => _ScanScreenState();
@@ -27,6 +34,7 @@ class _ScanScreenState extends State<ScanScreen> {
final ImagePicker _picker = ImagePicker();
String _extractedText = '';
String? _imagePath;
bool _busy = false;
String? _error;
@@ -81,6 +89,7 @@ class _ScanScreenState extends State<ScanScreen> {
_busy = true;
_error = null;
_extractedText = '';
_imagePath = imagePath;
});
final recognizer = TextRecognizer(script: TextRecognitionScript.latin);
@@ -114,23 +123,23 @@ class _ScanScreenState extends State<ScanScreen> {
Future<void> _openReview(String rawText) async {
final items = ReceiptParser.parse(rawText);
if (!mounted) return;
await Navigator.of(context).push(
MaterialPageRoute<void>(
final names =
SettingsScope.maybeOf(context)?.supermarkets.map((s) => s.name) ??
kSupermarkets;
final saved = await Navigator.of(context).push<bool>(
MaterialPageRoute(
builder: (_) => ReviewScreen(
items: items,
rawText: rawText,
repository: widget.repository,
imagePath: _imagePath,
supermarketNames: names.toList(),
),
),
);
}
void _openProducts() {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => ProductsScreen(repository: widget.repository),
),
);
if (saved == true && mounted) {
widget.onTripSaved?.call();
}
}
@override
@@ -140,13 +149,7 @@ class _ScanScreenState extends State<ScanScreen> {
return Scaffold(
appBar: AppBar(
title: const Text(kAppName),
actions: [
IconButton(
tooltip: 'Products',
onPressed: _openProducts,
icon: const Icon(Icons.inventory_2_outlined),
),
],
actions: const [SettingsButton()],
),
body: SafeArea(
child: Padding(
@@ -220,7 +223,7 @@ class _ResultPane extends StatelessWidget {
if (text.isEmpty) {
return Center(
child: Text(
'Take a photo of a shopping receipt, or pick one from the gallery.\n\nExtracted text will show up here.',
'Take a photo of a shopping receipt, or pick one from the gallery.\n\nConfirmed trips show up in Shop log.',
textAlign: TextAlign.center,
style: theme.textTheme.bodyLarge,
),