import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:image_picker/image_picker.dart'; import '../../l10n/app_localizations.dart'; import 'recognition_service.dart'; /// Entry screen — lets the user choose how to add products. class ScanScreen extends ConsumerStatefulWidget { const ScanScreen({super.key}); @override ConsumerState createState() => _ScanScreenState(); } class _ScanScreenState extends ConsumerState { @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return Scaffold( appBar: AppBar(title: Text(l10n.addFromReceiptOrPhoto)), body: ListView( padding: const EdgeInsets.all(20), children: [ const SizedBox(height: 16), Text( l10n.chooseMethod, style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center, ), const SizedBox(height: 32), _ModeCard( emoji: '🧾', title: l10n.photoReceipt, subtitle: l10n.photoReceiptSubtitle, onTap: () => _pickAndRecognize(context, _Mode.receipt), ), const SizedBox(height: 16), _ModeCard( emoji: '🥦', title: l10n.photoProducts, subtitle: l10n.photoProductsSubtitle, onTap: () => _pickAndRecognize(context, _Mode.products), ), ], ), ); } Future _pickAndRecognize( BuildContext context, _Mode mode, ) async { final picker = ImagePicker(); List files = []; if (mode == _Mode.products) { // Allow up to 3 images. final picked = await picker.pickMultiImage( imageQuality: 70, maxWidth: 1024, maxHeight: 1024, ); if (picked.isEmpty) return; files = picked.take(3).toList(); } else { final source = await _chooseSource(context); if (source == null) return; final picked = await picker.pickImage( source: source, imageQuality: 70, maxWidth: 1024, maxHeight: 1024, ); if (picked == null) return; files = [picked]; } if (!context.mounted) return; final service = ref.read(recognitionServiceProvider); final l10n = AppLocalizations.of(context)!; // Show loading overlay while the AI processes. showDialog( context: context, barrierDismissible: false, builder: (dialogContext) => _LoadingDialog(label: l10n.recognizing), ); try { switch (mode) { case _Mode.receipt: final result = await service.recognizeReceipt(files.first); if (context.mounted) { Navigator.pop(context); // close loading context.push('/scan/confirm', extra: result.items); } case _Mode.products: final items = await service.recognizeProducts(files); if (context.mounted) { Navigator.pop(context); context.push('/scan/confirm', extra: items); } } } catch (recognitionError) { debugPrint('Recognition error: $recognitionError'); if (context.mounted) { Navigator.pop(context); // close loading ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(l10n.recognitionFailed)), ); } } } Future _chooseSource(BuildContext context) async { final l10n = AppLocalizations.of(context)!; return showModalBottomSheet( context: context, builder: (_) => SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Icons.camera_alt), title: Text(l10n.camera), onTap: () => Navigator.pop(context, ImageSource.camera), ), ListTile( leading: const Icon(Icons.photo_library), title: Text(l10n.gallery), onTap: () => Navigator.pop(context, ImageSource.gallery), ), ], ), ), ); } } // --------------------------------------------------------------------------- // Mode enum // --------------------------------------------------------------------------- enum _Mode { receipt, products } // --------------------------------------------------------------------------- // Widgets // --------------------------------------------------------------------------- class _ModeCard extends StatelessWidget { const _ModeCard({ required this.emoji, required this.title, required this.subtitle, required this.onTap, }); final String emoji; final String title; final String subtitle; final VoidCallback onTap; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Card( child: ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), leading: Text(emoji, style: const TextStyle(fontSize: 32)), title: Text(title, style: theme.textTheme.titleMedium), subtitle: Text(subtitle, style: theme.textTheme.bodySmall), trailing: const Icon(Icons.chevron_right), onTap: onTap, ), ); } } class _LoadingDialog extends StatelessWidget { const _LoadingDialog({required this.label}); final String label; @override Widget build(BuildContext context) { return AlertDialog( content: Column( mainAxisSize: MainAxisSize.min, children: [ const CircularProgressIndicator(), const SizedBox(height: 16), Text(label), ], ), ); } }