import 'dart:io'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import '../../../core/constants/design_system.dart'; /// Widget de sélection de médias pour la création de posts. /// /// Permet de sélectionner des images et vidéos depuis la galerie ou l'appareil photo. class MediaPicker extends StatefulWidget { const MediaPicker({ required this.onMediasChanged, this.maxMedias = 10, this.initialMedias = const [], super.key, }); final ValueChanged> onMediasChanged; final int maxMedias; final List initialMedias; @override State createState() => _MediaPickerState(); } class _MediaPickerState extends State { final ImagePicker _picker = ImagePicker(); List _selectedMedias = []; @override void initState() { super.initState(); _selectedMedias = List.from(widget.initialMedias); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // Titre avec compteur Row( children: [ Text( 'Médias', style: theme.textTheme.titleSmall?.copyWith( fontWeight: FontWeight.w600, fontSize: 14, ), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2, ), decoration: BoxDecoration( color: theme.colorScheme.primaryContainer, borderRadius: BorderRadius.circular(10), ), child: Text( '${_selectedMedias.length}/${widget.maxMedias}', style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: theme.colorScheme.onPrimaryContainer, ), ), ), ], ), const SizedBox(height: DesignSystem.spacingMd), // Boutons d'action Row( children: [ _buildActionButton( context, icon: Icons.photo_library_outlined, label: 'Galerie', onTap: _pickFromGallery, ), const SizedBox(width: DesignSystem.spacingSm), _buildActionButton( context, icon: Icons.camera_alt_outlined, label: 'Caméra', onTap: _pickFromCamera, ), const SizedBox(width: DesignSystem.spacingSm), _buildActionButton( context, icon: Icons.videocam_outlined, label: 'Vidéo', onTap: _pickVideo, ), ], ), // Grille de médias sélectionnés if (_selectedMedias.isNotEmpty) ...[ const SizedBox(height: DesignSystem.spacingMd), _buildMediasGrid(theme), ], ], ); } Widget _buildActionButton( BuildContext context, { required IconData icon, required String label, required VoidCallback onTap, }) { final theme = Theme.of(context); final canAddMore = _selectedMedias.length < widget.maxMedias; return Expanded( child: Material( color: canAddMore ? theme.colorScheme.primaryContainer : theme.colorScheme.surfaceVariant, borderRadius: BorderRadius.circular(DesignSystem.radiusMd), child: InkWell( onTap: canAddMore ? onTap : null, borderRadius: BorderRadius.circular(DesignSystem.radiusMd), child: Padding( padding: const EdgeInsets.symmetric( horizontal: DesignSystem.spacingSm, vertical: DesignSystem.spacingMd, ), child: Column( children: [ Icon( icon, size: 24, color: canAddMore ? theme.colorScheme.onPrimaryContainer : theme.colorScheme.onSurfaceVariant.withOpacity(0.5), ), const SizedBox(height: 4), Text( label, style: theme.textTheme.bodySmall?.copyWith( fontSize: 11, fontWeight: FontWeight.w600, color: canAddMore ? theme.colorScheme.onPrimaryContainer : theme.colorScheme.onSurfaceVariant.withOpacity(0.5), ), ), ], ), ), ), ), ); } Widget _buildMediasGrid(ThemeData theme) { return SizedBox( height: 100, child: ListView.builder( scrollDirection: Axis.horizontal, itemCount: _selectedMedias.length, itemBuilder: (context, index) { return _buildMediaItem(theme, index); }, ), ); } Widget _buildMediaItem(ThemeData theme, int index) { final media = _selectedMedias[index]; final isVideo = media.path.toLowerCase().endsWith('.mp4') || media.path.toLowerCase().endsWith('.mov'); return Padding( padding: const EdgeInsets.only(right: DesignSystem.spacingSm), child: Stack( children: [ // Image/Vidéo ClipRRect( borderRadius: BorderRadius.circular(DesignSystem.radiusMd), child: Container( width: 100, height: 100, color: theme.colorScheme.surfaceVariant, child: isVideo ? Center( child: Icon( Icons.videocam_rounded, size: 32, color: theme.colorScheme.onSurfaceVariant, ), ) : Image.file( File(media.path), fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Center( child: Icon( Icons.broken_image_rounded, size: 32, color: theme.colorScheme.onSurfaceVariant, ), ); }, ), ), ), // Bouton de suppression Positioned( top: 4, right: 4, child: GestureDetector( onTap: () => _removeMedia(index), child: Container( padding: const EdgeInsets.all(4), decoration: BoxDecoration( color: Colors.black.withOpacity(0.7), shape: BoxShape.circle, ), child: const Icon( Icons.close_rounded, size: 16, color: Colors.white, ), ), ), ), // Indicateur vidéo if (isVideo) Positioned( bottom: 4, left: 4, child: Container( padding: const EdgeInsets.symmetric( horizontal: 4, vertical: 2, ), decoration: BoxDecoration( color: Colors.black.withOpacity(0.7), borderRadius: BorderRadius.circular(DesignSystem.radiusSm), ), child: const Row( mainAxisSize: MainAxisSize.min, children: [ Icon( Icons.play_circle_outline, size: 12, color: Colors.white, ), ], ), ), ), ], ), ); } Future _pickFromGallery() async { try { final images = await _picker.pickMultiImage(); if (images.isNotEmpty) { _addMedias(images); } } catch (e) { debugPrint('[MediaPicker] Erreur lors de la sélection: $e'); } } Future _pickFromCamera() async { try { final image = await _picker.pickImage(source: ImageSource.camera); if (image != null) { _addMedias([image]); } } catch (e) { debugPrint('[MediaPicker] Erreur lors de la capture: $e'); } } Future _pickVideo() async { try { final video = await _picker.pickVideo(source: ImageSource.gallery); if (video != null) { _addMedias([video]); } } catch (e) { debugPrint('[MediaPicker] Erreur lors de la sélection vidéo: $e'); } } void _addMedias(List medias) { final availableSlots = widget.maxMedias - _selectedMedias.length; final mediasToAdd = medias.take(availableSlots).toList(); setState(() { _selectedMedias.addAll(mediasToAdd); }); widget.onMediasChanged(_selectedMedias); } void _removeMedia(int index) { setState(() { _selectedMedias.removeAt(index); }); widget.onMediasChanged(_selectedMedias); } }