import 'package:flutter/material.dart'; /// Champ de sélection d'image avec aperçu et support du thème. /// /// Ce widget fournit un champ de sélection d'image cohérent avec le design system, /// avec support de l'aperçu de l'image sélectionnée. /// /// **Usage:** /// ```dart /// ImagePickerField( /// label: 'Image de l\'événement', /// imagePath: selectedImagePath, /// onImagePicked: () { /// // Ouvrir le sélecteur d'image /// }, /// ) /// ``` class ImagePickerField extends StatelessWidget { /// Crée un nouveau [ImagePickerField]. /// /// [onImagePicked] La fonction appelée pour ouvrir le sélecteur d'image /// [imagePath] Le chemin de l'image sélectionnée (optionnel) /// [label] Le texte du label (par défaut: 'Sélectionnez une image') /// [imageUrl] L'URL de l'image (optionnel, prioritaire sur imagePath) const ImagePickerField({ required this.onImagePicked, super.key, this.imagePath, this.imageUrl, this.label = 'Sélectionnez une image', }); /// Le chemin de l'image sélectionnée final String? imagePath; /// L'URL de l'image (prioritaire sur imagePath) final String? imageUrl; /// La fonction appelée pour ouvrir le sélecteur d'image final VoidCallback onImagePicked; /// Le texte du label final String label; /// Retourne true si une image est sélectionnée bool get hasImage => (imageUrl != null && imageUrl!.isNotEmpty) || (imagePath != null && imagePath!.isNotEmpty); @override Widget build(BuildContext context) { final theme = Theme.of(context); return InkWell( onTap: onImagePicked, borderRadius: BorderRadius.circular(16), child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: theme.colorScheme.surface, borderRadius: BorderRadius.circular(16), border: Border.all( color: hasImage ? theme.colorScheme.primary : theme.colorScheme.outline.withOpacity(0.5), width: hasImage ? 2 : 1, ), ), child: Row( children: [ // Aperçu de l'image si disponible if (hasImage) Container( width: 60, height: 60, decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), image: imageUrl != null && imageUrl!.isNotEmpty ? DecorationImage( image: NetworkImage(imageUrl!), fit: BoxFit.cover, onError: (exception, stackTrace) { // En cas d'erreur de chargement, afficher une icône }, ) : imagePath != null && imagePath!.isNotEmpty ? DecorationImage( image: AssetImage(imagePath!), fit: BoxFit.cover, ) : null, ), child: imageUrl == null && imagePath == null ? Icon( Icons.image, color: theme.colorScheme.primary, size: 30, ) : null, ) else Container( width: 60, height: 60, decoration: BoxDecoration( color: theme.colorScheme.primary.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Icon( Icons.add_photo_alternate, color: theme.colorScheme.primary, size: 30, ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (hasImage) Text( 'Image sélectionnée', style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withOpacity(0.6), ), ), const SizedBox(height: 4), Text( hasImage ? (imageUrl ?? imagePath ?? 'Image') : label, style: theme.textTheme.bodyLarge?.copyWith( color: hasImage ? theme.colorScheme.onSurface : theme.colorScheme.onSurface.withOpacity(0.6), fontWeight: hasImage ? FontWeight.w600 : FontWeight.normal, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), const SizedBox(width: 12), Icon( hasImage ? Icons.edit : Icons.photo_camera, color: hasImage ? theme.colorScheme.primary : theme.colorScheme.onSurface.withOpacity(0.6), size: 24, ), ], ), ), ); } }