import 'dart:io'; import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:photo_view/photo_view.dart'; import 'package:share_plus/share_plus.dart'; import 'package:gallery_saver_plus/gallery_saver.dart'; import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart'; import 'package:get/get.dart'; import 'package:on_field_work/helpers/widgets/my_confirmation_dialog.dart'; class ImageViewerDialog extends StatefulWidget { final List imageSources; final int initialIndex; final List? captions; final String? title; const ImageViewerDialog({ Key? key, required this.imageSources, required this.initialIndex, this.captions, this.title, }) : super(key: key); @override State createState() => _ImageViewerDialogState(); } class _ImageViewerDialogState extends State { late final PageController _controller; late int currentIndex; bool isFile(dynamic item) => item is File; @override void initState() { super.initState(); currentIndex = widget.initialIndex; _controller = PageController(initialPage: widget.initialIndex); } Future shareImage(dynamic image) async { try { if (isFile(image)) { await Share.shareXFiles([XFile(image.path)], text: 'Check out this image!'); } else if (image is String) { await Share.share(image, subject: 'Check out this image!'); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Failed to share image: $e')), ); } } Future downloadImage(dynamic image) async { try { if (isFile(image)) { await GallerySaver.saveImage(image.path); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Image saved to gallery')), ); } else if (image is String) { final response = await http.get(Uri.parse(image)); final bytes = response.bodyBytes; final tempDir = await getTemporaryDirectory(); final filePath = '${tempDir.path}/${DateTime.now().millisecondsSinceEpoch}.png'; final file = await File(filePath).writeAsBytes(bytes); await GallerySaver.saveImage(file.path); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Image saved to gallery')), ); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Failed to save image: $e')), ); } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black, body: Stack( children: [ Container( decoration: const BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF101018), Color(0xFF050509)], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), ), // Add vertical padding to avoid overlap with header/footer Padding( padding: const EdgeInsets.only(top: 72, bottom: 110), // Adjust as needed child: PageView.builder( controller: _controller, itemCount: widget.imageSources.length, onPageChanged: (index) => setState(() => currentIndex = index), itemBuilder: (context, index) { final item = widget.imageSources[index]; final ImageProvider provider = isFile(item) ? FileImage(item) : CachedNetworkImageProvider(item); return Center( child: ClipRRect( borderRadius: BorderRadius.circular(20), child: PhotoView( imageProvider: provider, backgroundDecoration: const BoxDecoration(color: Colors.transparent), loadingBuilder: (context, event) => const Center(child: CircularProgressIndicator()), errorBuilder: (context, error, stackTrace) => const Center( child: Icon(Icons.broken_image, size: 64, color: Colors.white54), ), minScale: PhotoViewComputedScale.contained, maxScale: PhotoViewComputedScale.covered * 3, ), ), ); }, ), ), // Top blurred app bar with back button and title SafeArea( child: Padding( padding: const EdgeInsets.all(12), child: ClipRRect( borderRadius: BorderRadius.circular(24), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), child: Container( height: 50, decoration: BoxDecoration( color: Colors.black.withOpacity(0.4), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.4), blurRadius: 8, offset: Offset(0, 3), ), ], ), child: Row( children: [ Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(24), onTap: () => Navigator.of(context).pop(), child: const Padding( padding: EdgeInsets.all(8), child: Icon(Icons.arrow_back_ios_new_rounded, color: Colors.white, size: 24), ), ), ), const SizedBox(width: 8), Expanded( child: Text( widget.title ?? 'Preview', style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w600, letterSpacing: 0.5, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), ), ), ), ), ), // Bottom control panel with blur and rounded corners Align( alignment: Alignment.bottomCenter, child: SafeArea( top: false, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), child: ClipRRect( borderRadius: BorderRadius.circular(22), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12), child: Container( padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 14), decoration: BoxDecoration( color: Colors.black.withOpacity(0.4), boxShadow: [ BoxShadow( color: Colors.black54, blurRadius: 10, offset: Offset(0, 4), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Row( children: [ Text( '${currentIndex + 1}/${widget.imageSources.length}', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 15, ), ), const SizedBox(width: 16), Expanded( child: Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate( widget.imageSources.length, (index) { final bool active = index == currentIndex; return AnimatedContainer( duration: const Duration(milliseconds: 300), margin: const EdgeInsets.symmetric( horizontal: 4), height: 8, width: active ? 22 : 8, decoration: BoxDecoration( color: active ? Colors.white : Colors.white54, borderRadius: BorderRadius.circular(30), boxShadow: active ? [ BoxShadow( color: Colors.white70, blurRadius: 6, spreadRadius: 1, ) ] : [], ), ); }), ), ), const SizedBox(width: 16), Tooltip( message: 'Download Image', child: Material( color: Colors.transparent, child: InkWell( borderRadius: BorderRadius.circular(30), onTap: () async { final confirmed = await Get.dialog( ConfirmDialog( title: "Download Image", message: "Do you want to download this image to your device?", confirmText: "Download", cancelText: "Cancel", onConfirm: () async { // Call your existing download method here for the current image await downloadImage(widget .imageSources[currentIndex]); }, confirmColor: Colors.blueAccent, confirmIcon: Icons.download_rounded, icon: Icons.download_rounded, errorMessage: "Failed to download image. Please try again.", loadingText: "Downloading…", ), barrierDismissible: false, ); // Optionally handle if the dialog was cancelled (confirmed == false or null) if (confirmed != true) { // Download cancelled by user } }, child: const Padding( padding: EdgeInsets.all(6), child: Icon(Icons.download_rounded, color: Colors.white, size: 28), ), ), ), ), ], ), if (widget.captions != null && widget.captions!.isNotEmpty && currentIndex < widget.captions!.length) Padding( padding: const EdgeInsets.only(top: 8), child: AnimatedOpacity( opacity: 1.0, duration: const Duration(milliseconds: 400), child: Text( widget.captions![currentIndex], style: const TextStyle( color: Colors.white70, fontSize: 13, ), textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, ), ), ), ], ), ), ), ), ), ), ), ], ), ); } }