import 'dart:io'; import 'package:flutter/material.dart'; class ImageViewerDialog extends StatefulWidget { final List imageSources; final int initialIndex; const ImageViewerDialog({ Key? key, required this.imageSources, required this.initialIndex, }) : 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); } @override Widget build(BuildContext context) { final double dialogHeight = MediaQuery.of(context).size.height * 0.55; return Dialog( backgroundColor: Colors.transparent, insetPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 100), child: ClipRRect( borderRadius: BorderRadius.circular(16), child: Container( height: dialogHeight, decoration: BoxDecoration( color: Colors.white, boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.1), blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: Column( children: [ // Top Close Button Align( alignment: Alignment.topRight, child: IconButton( icon: const Icon(Icons.close, size: 26), onPressed: () => Navigator.of(context).pop(), splashRadius: 22, tooltip: 'Close', ), ), // Image Viewer Expanded( child: PageView.builder( controller: _controller, itemCount: widget.imageSources.length, onPageChanged: (index) { setState(() => currentIndex = index); }, itemBuilder: (context, index) { final item = widget.imageSources[index]; return Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: isFile(item) ? Image.file(item, fit: BoxFit.contain) : Image.network( item, fit: BoxFit.contain, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return Center( child: CircularProgressIndicator( value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / (loadingProgress.expectedTotalBytes ?? 1) : null, ), ); }, errorBuilder: (context, error, stackTrace) => const Center( child: Icon(Icons.broken_image, size: 48, color: Colors.grey), ), ), ); }, ), ), // Index Indicator Padding( padding: const EdgeInsets.only(top: 8, bottom: 12), child: Text( '${currentIndex + 1} / ${widget.imageSources.length}', style: const TextStyle( color: Colors.black87, fontSize: 14, fontWeight: FontWeight.w500, ), ), ), ], ), ), ), ); } }