import 'dart:io'; 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 '../../core/auth/auth_provider.dart'; import 'recognition_service.dart'; // Provider wired to the shared ApiClient. final _recognitionServiceProvider = Provider((ref) { return RecognitionService(ref.read(apiClientProvider)); }); /// Entry screen — lets the user choose how to add products. class ScanScreen extends ConsumerWidget { const ScanScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { 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, ref, _Mode.receipt), ), const SizedBox(height: 16), _ModeCard( emoji: '🥦', title: 'Сфотографировать продукты', subtitle: 'Холодильник, стол, полка — до 3 фото', onTap: () => _pickAndRecognize(context, ref, _Mode.products), ), const SizedBox(height: 16), _ModeCard( emoji: '🍽️', title: 'Определить блюдо', subtitle: 'КБЖУ≈ по фото готового блюда', onTap: () => _pickAndRecognize(context, ref, _Mode.dish), ), const SizedBox(height: 16), _ModeCard( emoji: '✏️', title: 'Добавить вручную', subtitle: 'Ввести название, количество и срок', onTap: () => context.push('/products/add'), ), ], ), ); } Future _pickAndRecognize( BuildContext context, WidgetRef ref, _Mode mode, ) async { final picker = ImagePicker(); List files = []; if (mode == _Mode.products) { // Allow up to 3 images. final picked = await picker.pickMultiImage(imageQuality: 70); if (picked.isEmpty) return; files = picked.take(3).map((x) => File(x.path)).toList(); } else { final source = await _chooseSource(context); if (source == null) return; final picked = await picker.pickImage(source: source, imageQuality: 70); if (picked == null) return; files = [File(picked.path)]; } 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); } case _Mode.dish: final dish = await service.recognizeDish(files.first); if (context.mounted) { Navigator.pop(context); context.push('/scan/dish', extra: dish); } } } catch (e) { 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, dish } // --------------------------------------------------------------------------- // 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('Распознаём...'), ], ), ); } }