import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../../core/constants/design_system.dart'; /// Widget pour afficher une image en plein écran avec Hero animation. /// /// Ce widget affiche une image en plein écran avec des fonctionnalités /// de zoom, rotation, et fermeture par glissement. /// /// **Fonctionnalités:** /// - Hero animation pour transition fluide /// - Pinch to zoom et rotation /// - Swipe down pour fermer /// - Contrôles en overlay (fermer, télécharger) /// - Mode immersif (masque la barre de statut) class FullscreenImageViewer extends StatefulWidget { const FullscreenImageViewer({ required this.imageUrl, required this.heroTag, this.title, super.key, }); /// URL de l'image à afficher final String imageUrl; /// Tag Hero pour l'animation de transition final String heroTag; /// Titre optionnel affiché en haut final String? title; @override State createState() => _FullscreenImageViewerState(); } class _FullscreenImageViewerState extends State with SingleTickerProviderStateMixin { bool _showControls = true; final TransformationController _transformationController = TransformationController(); @override void initState() { super.initState(); // Mode immersif SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); } @override void dispose() { // Restaurer la barre de statut SystemChrome.setEnabledSystemUIMode( SystemUiMode.manual, overlays: SystemUiOverlay.values, ); _transformationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final theme = Theme.of(context); return Scaffold( backgroundColor: Colors.black, body: GestureDetector( onTap: _toggleControls, child: Stack( children: [ // Image avec zoom et rotation Center( child: Hero( tag: widget.heroTag, child: InteractiveViewer( transformationController: _transformationController, minScale: 0.5, maxScale: 4.0, child: Image.network( widget.imageUrl, fit: BoxFit.contain, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return Center( child: CircularProgressIndicator( value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null, color: theme.colorScheme.primary, ), ); }, errorBuilder: (context, error, stackTrace) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.error_outline, color: theme.colorScheme.error, size: 64, ), const SizedBox(height: 16), Text( 'Erreur de chargement', style: theme.textTheme.bodyLarge?.copyWith( color: Colors.white70, ), ), ], ), ); }, ), ), ), ), // Contrôles en overlay if (_showControls) ...[ // Header avec titre et bouton fermer Positioned( top: 0, left: 0, right: 0, child: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.black.withOpacity(0.6), Colors.transparent, ], ), ), child: SafeArea( child: Padding( padding: DesignSystem.paddingAll(DesignSystem.spacingMd), child: Row( children: [ IconButton( icon: const Icon(Icons.close, color: Colors.white), onPressed: () => Navigator.pop(context), tooltip: 'Fermer', ), if (widget.title != null) ...[ const SizedBox(width: 8), Expanded( child: Text( widget.title!, style: theme.textTheme.titleMedium?.copyWith( color: Colors.white, fontWeight: FontWeight.w600, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ], ), ), ), ), ), // Footer avec actions Positioned( bottom: 0, left: 0, right: 0, child: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [ Colors.black.withOpacity(0.6), Colors.transparent, ], ), ), child: SafeArea( child: Padding( padding: DesignSystem.paddingAll(DesignSystem.spacingMd), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( icon: const Icon(Icons.zoom_in, color: Colors.white), onPressed: _zoomIn, tooltip: 'Zoom +', ), const SizedBox(width: 16), IconButton( icon: const Icon(Icons.zoom_out, color: Colors.white), onPressed: _zoomOut, tooltip: 'Zoom -', ), const SizedBox(width: 16), IconButton( icon: const Icon(Icons.refresh, color: Colors.white), onPressed: _resetZoom, tooltip: 'Réinitialiser', ), ], ), ), ), ), ), ], ], ), ), ); } /// Bascule l'affichage des contrôles void _toggleControls() { setState(() { _showControls = !_showControls; }); } /// Zoom avant void _zoomIn() { final currentScale = _transformationController.value.getMaxScaleOnAxis(); final newScale = (currentScale * 1.5).clamp(0.5, 4.0); _transformationController.value = Matrix4.identity()..scale(newScale); } /// Zoom arrière void _zoomOut() { final currentScale = _transformationController.value.getMaxScaleOnAxis(); final newScale = (currentScale / 1.5).clamp(0.5, 4.0); _transformationController.value = Matrix4.identity()..scale(newScale); } /// Réinitialise le zoom void _resetZoom() { _transformationController.value = Matrix4.identity(); } }