marco.pms.mobileapp/lib/helpers/widgets/image_viewer_dialog.dart

122 lines
4.1 KiB
Dart

import 'dart:io';
import 'package:flutter/material.dart';
class ImageViewerDialog extends StatefulWidget {
final List<dynamic> imageSources;
final int initialIndex;
const ImageViewerDialog({
Key? key,
required this.imageSources,
required this.initialIndex,
}) : super(key: key);
@override
State<ImageViewerDialog> createState() => _ImageViewerDialogState();
}
class _ImageViewerDialogState extends State<ImageViewerDialog> {
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,
),
),
),
],
),
),
),
);
}
}