import 'dart:io'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:provider/provider.dart'; import '../../core/constants/app_constants.dart'; import '../../core/theme/app_theme.dart'; import '../../data/models/target_type.dart'; import '../crop/crop_screen.dart'; import '../session/session_provider.dart'; import 'widgets/image_source_button.dart'; import '../../services/image_crop_service.dart'; import '../../services/document_scanner_service.dart'; // Scanner ML Kit : redresse la cible de face class CaptureScreen extends StatefulWidget { const CaptureScreen({super.key}); @override State createState() => _CaptureScreenState(); } class _CaptureScreenState extends State { final ImagePicker _picker = ImagePicker(); final ImageCropService _cropService = ImageCropService(); final TargetType _selectedType = TargetType.concentric; // Scanner de documents Google ML Kit (redresse la cible de face automatiquement) final DocumentScannerService _docScanner = DocumentScannerService(); String? _selectedImagePath; bool _isLoading = false; // ───────────────────────────────────────────────────────────────────────── // Build principal // ───────────────────────────────────────────────────────────────────────── @override Widget build(BuildContext context) { final sessionProvider = context.watch(); final title = sessionProvider.isSessionActive ? 'Cible ${sessionProvider.targetCount + 1}' : 'Source'; return Scaffold( appBar: AppBar(title: Text(title)), body: SingleChildScrollView( padding: const EdgeInsets.all(AppConstants.defaultPadding), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const SizedBox(height: AppConstants.largePadding), _buildSectionTitle('Source de l\'Image'), const SizedBox(height: 12), Row( children: [ Expanded( child: ImageSourceButton( icon: Icons.document_scanner, label: 'Scanner', onPressed: _isLoading ? null : _scanWithDocumentScanner, ), ), const SizedBox(width: 12), Expanded( child: ImageSourceButton( icon: Icons.photo_library, label: 'Galerie', onPressed: _isLoading ? null : _handleGallerySelection, ), ), ], ), const SizedBox(height: AppConstants.largePadding), if (_isLoading) const Center( child: Padding( padding: EdgeInsets.all(32), child: CircularProgressIndicator(), ), ) else _buildGuide(), ], ), ), ); } // ───────────────────────────────────────────────────────────────────────── // Déclic → lance le scanner de documents Google ML Kit qui redresse // automatiquement la cible (feuille rectangulaire) de face. // ───────────────────────────────────────────────────────────────────────── Future _scanWithDocumentScanner() async { setState(() => _isLoading = true); try { final result = await _docScanner.scanTarget(); if (!result.success) { // L'utilisateur a annulé le scan : on ne fait rien de plus. if (mounted) setState(() => _isLoading = false); return; } setState(() { _selectedImagePath = result.imagePath; }); _analyzeImage(); } catch (e) { debugPrint('Erreur scanner document: $e'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Le scan a échoué. Réessayez.'), ), ); } } finally { if (mounted) setState(() => _isLoading = false); } } // ───────────────────────────────────────────────────────────────────────── // Galerie (inchangée) // ───────────────────────────────────────────────────────────────────────── Future _handleGallerySelection() async { PermissionStatus status; if (Platform.isAndroid) { final deviceInfo = DeviceInfoPlugin(); final androidInfo = await deviceInfo.androidInfo; status = androidInfo.version.sdkInt >= 33 ? await Permission.photos.request() : await Permission.storage.request(); } else { status = await Permission.photos.request(); } if (status.isGranted) { _captureImageFromGallery(); } else if (status.isPermanentlyDenied) { _showSettingsDialog(); } else { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Accès à la galerie requis pour continuer'), ), ); } } } void _showSettingsDialog() { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Permission requise'), content: const Text( 'L\'accès aux photos est nécessaire. Veuillez l\'activer dans les paramètres.', ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Annuler'), ), TextButton( onPressed: () => openAppSettings(), child: const Text('Paramètres'), ), ], ), ); } Future _captureImageFromGallery() async { setState(() => _isLoading = true); try { final XFile? image = await _picker.pickImage( source: ImageSource.gallery, maxWidth: 2048, maxHeight: 2048, imageQuality: 90, ); if (image != null) { setState(() { _selectedImagePath = image.path; }); _analyzeImage(); } } catch (e) { debugPrint('Erreur galerie: $e'); } finally { if (mounted) setState(() => _isLoading = false); } } void _analyzeImage() { if (_selectedImagePath == null) return; Navigator.push( context, MaterialPageRoute( builder: (_) => CropScreen( imagePath: _selectedImagePath!, targetType: _selectedType, ), ), ); } // ───────────────────────────────────────────────────────────────────────── // Helpers UI // ───────────────────────────────────────────────────────────────────────── Widget _buildSectionTitle(String title) { return Text( title, style: Theme.of(context) .textTheme .titleMedium ?.copyWith(fontWeight: FontWeight.bold), ); } Widget _buildGuide() { return Card( child: Padding( padding: const EdgeInsets.all(AppConstants.defaultPadding), child: Column( children: [ Icon(Icons.help_outline, size: 48, color: Colors.grey[400]), const SizedBox(height: 12), Text( 'Conseils pour une bonne analyse', style: Theme.of(context) .textTheme .titleSmall ?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 12), _buildGuideItem( Icons.crop_free, 'Cadrez la cible entière dans l\'image', ), _buildGuideItem(Icons.wb_sunny, 'Utilisez un bon éclairage'), _buildGuideItem(Icons.straighten, 'Prenez la photo de face'), _buildGuideItem(Icons.blur_off, 'Évitez les images floues'), _buildGuideItem( Icons.cleaning_services, 'Nettoyer votre objectif avec une chiffonnette', ), ], ), ), ); } Widget _buildGuideItem(IconData icon, String text) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( children: [ Icon(icon, size: 20, color: AppTheme.primaryColor), const SizedBox(width: 12), Expanded(child: Text(text)), ], ), ); } }