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 '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) { return Scaffold( appBar: AppBar(title: const Text('Добавить продукты')), body: ListView( padding: const EdgeInsets.all(20), children: [ const SizedBox(height: 16), Text( 'Выберите способ', style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center, ), const SizedBox(height: 32), _ModeCard( emoji: '🧾', title: 'Сфотографировать чек', subtitle: 'Распознаем все продукты из чека', onTap: () => _pickAndRecognize(context, _Mode.receipt), ), const SizedBox(height: 16), _ModeCard( emoji: '🥦', title: 'Сфотографировать продукты', subtitle: 'Холодильник, стол, полка — до 3 фото', onTap: () => _pickAndRecognize(context, _Mode.products), ), const SizedBox(height: 16), _ModeCard( emoji: '✏️', title: 'Добавить вручную', subtitle: 'Ввести название, количество и срок', onTap: () => context.push('/products/add'), ), ], ), ); } 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); // Show loading overlay while the AI processes. showDialog( context: context, barrierDismissible: false, builder: (_) => const _LoadingDialog(), ); 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( const SnackBar( content: Text('Не удалось распознать. Попробуйте ещё раз.'), ), ); } } } Future _chooseSource(BuildContext context) async { return showModalBottomSheet( context: context, builder: (_) => SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: [ ListTile( leading: const Icon(Icons.camera_alt), title: const Text('Камера'), onTap: () => Navigator.pop(context, ImageSource.camera), ), ListTile( leading: const Icon(Icons.photo_library), title: const Text('Галерея'), 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(); @override Widget build(BuildContext context) { return const AlertDialog( content: Column( mainAxisSize: MainAxisSize.min, children: [ CircularProgressIndicator(), SizedBox(height: 16), Text('Распознаём...'), ], ), ); } }