import 'dart:io'; import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; import 'package:image_picker/image_picker.dart'; /// Widget réutilisable pour uploader un fichier (image ou PDF) /// avec prévisualisation et validation class FileUploadWidget extends StatefulWidget { final Function(File?) onFileSelected; final String? initialFileName; final bool showImagePreview; const FileUploadWidget({ super.key, required this.onFileSelected, this.initialFileName, this.showImagePreview = true, }); @override State createState() => _FileUploadWidgetState(); } class _FileUploadWidgetState extends State { File? _selectedFile; final ImagePicker _imagePicker = ImagePicker(); @override void initState() { super.initState(); if (widget.initialFileName != null) { _selectedFile = File(widget.initialFileName!); } } Future _pickImage(ImageSource source) async { try { final XFile? image = await _imagePicker.pickImage( source: source, maxWidth: 1920, maxHeight: 1920, imageQuality: 85, ); if (image != null) { final file = File(image.path); final fileSize = await file.length(); // Vérifier la taille (max 5 MB) if (fileSize > 5 * 1024 * 1024) { if (mounted) { _showError('Fichier trop volumineux. Taille max: 5 MB'); } return; } setState(() { _selectedFile = file; }); widget.onFileSelected(file); } } catch (e) { _showError('Erreur lors de la sélection de l\'image: $e'); } } Future _pickPdf() async { try { final result = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: ['pdf'], ); if (result != null && result.files.single.path != null) { final file = File(result.files.single.path!); final fileSize = await file.length(); // Vérifier la taille (max 5 MB) if (fileSize > 5 * 1024 * 1024) { if (mounted) { _showError('Fichier trop volumineux. Taille max: 5 MB'); } return; } setState(() { _selectedFile = file; }); widget.onFileSelected(file); } } catch (e) { _showError('Erreur lors de la sélection du PDF: $e'); } } void _removeFile() { setState(() { _selectedFile = null; }); widget.onFileSelected(null); } void _showError(String message) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: Colors.red, ), ); } } void _showPickerOptions() { showModalBottomSheet( context: context, builder: (context) => SafeArea( child: Wrap( children: [ ListTile( leading: const Icon(Icons.photo_camera), title: const Text('Prendre une photo'), onTap: () { Navigator.pop(context); _pickImage(ImageSource.camera); }, ), ListTile( leading: const Icon(Icons.photo_library), title: const Text('Choisir une image'), onTap: () { Navigator.pop(context); _pickImage(ImageSource.gallery); }, ), ListTile( leading: const Icon(Icons.picture_as_pdf), title: const Text('Choisir un PDF'), onTap: () { Navigator.pop(context); _pickPdf(); }, ), ], ), ), ); } bool _isImage() { if (_selectedFile == null) return false; final ext = _selectedFile!.path.split('.').last.toLowerCase(); return ['jpg', 'jpeg', 'png', 'gif'].contains(ext); } bool _isPdf() { if (_selectedFile == null) return false; return _selectedFile!.path.toLowerCase().endsWith('.pdf'); } @override Widget build(BuildContext context) { return Card( elevation: 2, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Pièce justificative', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16, ), ), if (_selectedFile != null) IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: _removeFile, ), ], ), const SizedBox(height: 8), if (_selectedFile == null) OutlinedButton.icon( onPressed: _showPickerOptions, icon: const Icon(Icons.attach_file), label: const Text('Joindre un fichier'), style: OutlinedButton.styleFrom( minimumSize: const Size(double.infinity, 48), ), ) else ...[ // Prévisualisation if (_isImage() && widget.showImagePreview) Container( height: 200, width: double.infinity, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey.shade300), ), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.file( _selectedFile!, fit: BoxFit.cover, ), ), ) else if (_isPdf()) Container( height: 100, width: double.infinity, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey.shade300), ), child: const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.picture_as_pdf, size: 48, color: Colors.red), SizedBox(height: 8), Text('Document PDF'), ], ), ), ), const SizedBox(height: 8), Text( _selectedFile!.path.split('/').last, style: TextStyle(color: Colors.grey.shade700), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], const SizedBox(height: 8), Text( 'Formats acceptés: JPEG, PNG, PDF (max 5 MB)', style: TextStyle( fontSize: 12, color: Colors.grey.shade600, ), ), ], ), ), ); } }