Files
impact/lib/features/capture/capture_screen.dart

241 lines
7.0 KiB
Dart

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';
class CaptureScreen extends StatefulWidget {
const CaptureScreen({super.key});
@override
State<CaptureScreen> createState() => _CaptureScreenState();
}
class _CaptureScreenState extends State<CaptureScreen> {
final ImagePicker _picker = ImagePicker();
final TargetType _selectedType = TargetType.concentric;
String? _selectedImagePath;
bool _isLoading = false;
Future<void> _handleGallerySelection() async {
PermissionStatus status;
if (Platform.isAndroid) {
final deviceInfo = DeviceInfoPlugin();
final androidInfo = await deviceInfo.androidInfo;
if (androidInfo.version.sdkInt >= 33) {
status = await Permission.photos.request();
} else {
status = await Permission.storage.request();
}
} else {
status = await Permission.photos.request();
}
if (status.isGranted) {
_captureImage(ImageSource.gallery);
} 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'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
final sessionProvider = context.watch<SessionProvider>();
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.camera_alt,
label: 'Scanner',
// Appelle maintenant l'appareil photo classique
onPressed: _isLoading ? null : _openStandardCamera,
),
),
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(),
],
),
),
);
}
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)),
],
),
);
}
// NOUVELLE FONCTION : Ouvre l'appareil photo classique 100% Manuel
Future<void> _openStandardCamera() async {
final status = await Permission.camera.request();
if (status.isGranted) {
_captureImage(ImageSource.camera);
} else {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Accès à l\'appareil photo requis')),
);
}
}
}
Future<void> _captureImage(ImageSource source) async {
setState(() => _isLoading = true);
try {
final XFile? image = await _picker.pickImage(
source: source,
maxWidth: 2048,
maxHeight: 2048,
imageQuality: 90,
);
if (image != null) {
setState(() {
_selectedImagePath = image.path;
});
// On appelle directement l'analyse avec le bon chemin de fichier
_analyzeImage();
}
} catch (e) {
debugPrint('Erreur capture: $e');
} finally {
if (mounted) setState(() => _isLoading = false);
}
}
void _analyzeImage() {
if (_selectedImagePath == null) return;
// Correction de la redirection pour ouvrir proprement le CropScreen
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => CropScreen(
imagePath: _selectedImagePath!,
targetType: _selectedType,
),
),
);
}
}