import 'dart:io'; import 'package:flutter/material.dart'; import 'package:food_hub_app/config/app_config.dart'; import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view_gallery.dart'; class ImagePreviewPage extends StatefulWidget { final List images; final int initialIndex; const ImagePreviewPage({ super.key, required this.images, required this.initialIndex, }) : assert(initialIndex >= 0 && initialIndex < images.length); @override State createState() => _ImagePreviewPageState(); } class _ImagePreviewPageState extends State { // 声明 PageController 并初始化初始索引 late PageController _pageController; // 记录当前显示的图片索引(用于更新页码) int _currentIndex = 0; @override void initState() { super.initState(); // 初始化控制器,设置初始页面 _pageController = PageController(initialPage: widget.initialIndex); // 初始化当前索引为初始索引 _currentIndex = widget.initialIndex; // 监听页面切换事件 _pageController.addListener(() { // 取当前页面的整数索引(避免滑动过程中的小数) final currentPage = _pageController.page?.round() ?? 0; // 只有当索引变化时才更新状态 if (currentPage != _currentIndex) { setState(() { _currentIndex = currentPage; }); } }); } @override void dispose() { _pageController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.black54, elevation: 0, leading: IconButton( icon: const Icon(Icons.close, color: Colors.white), onPressed: () => Navigator.pop(context), ), // 显示实时更新的页码(当前索引+1 / 总数量) title: Text( '${_currentIndex + 1}/${widget.images.length}', style: const TextStyle(color: Colors.white), ), centerTitle: true, ), body: PhotoViewGallery( pageOptions: widget.images.map((url) { return PhotoViewGalleryPageOptions( imageProvider: NetworkImage('${AppConfig.imageBaseUrl}$url'), minScale: PhotoViewComputedScale.contained, maxScale: PhotoViewComputedScale.covered * 2, // 点击空白处关闭预览 onTapDown: (context, details, controllerValue) { Navigator.pop(context); }, ); }).toList(), pageController: _pageController, scrollDirection: Axis.horizontal, ), ); } } Widget buildNetworkImage( BuildContext context, List imageUrls, int index, ) { return AspectRatio( aspectRatio: 4 / 3, child: ClipRRect( borderRadius: BorderRadius.circular(8), child: GestureDetector( onTap: () { showFullScreenImage(context, imageUrls, index); }, child: Image.network( '${AppConfig.imageBaseUrl}${imageUrls[index]}', fit: BoxFit.cover, loadingBuilder: (context, child, loadingProgress) { if (loadingProgress == null) return child; return buildImageLoadingIndicator(loadingProgress); }, errorBuilder: (context, error, stackTrace) => buildErrorImage(), ), ), ), ); } Widget buildImagePreviewItem({ required BuildContext context, required List imageUrls, required int index, required VoidCallback onRemoveImage, }) { return Container( decoration: BoxDecoration(borderRadius: BorderRadius.circular(8)), child: ClipRRect( borderRadius: BorderRadius.circular(8), child: Stack( children: [ buildNetworkImage(context, imageUrls, index), buildDeleteImage(onRemoveImage: onRemoveImage), ], ), ), ); } Widget buildImageLoadingIndicator(ImageChunkEvent? loadingProgress) { return Center( child: SizedBox( width: 30, height: 30, child: CircularProgressIndicator( value: loadingProgress?.expectedTotalBytes != null ? loadingProgress!.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null, ), ), ); } Widget buildErrorImage() { return Container( color: Colors.grey[200], child: const Icon(Icons.image, color: Colors.grey, size: 30), ); } Widget _buildPhotoView(ImageProvider imageProvider) { return PhotoView( imageProvider: imageProvider, backgroundDecoration: const BoxDecoration(color: Colors.transparent), minScale: PhotoViewComputedScale.contained, maxScale: PhotoViewComputedScale.covered * 2, initialScale: PhotoViewComputedScale.contained, loadingBuilder: (context, event) => buildImageLoadingIndicator(event), errorBuilder: (context, error, stackTrace) => buildErrorImage(), ); } Widget _buildCloseImage(BuildContext context) { return Positioned( top: MediaQuery.of(context).padding.top + 10, right: 20, child: IconButton( icon: Icon(Icons.close, color: Colors.white, size: 30), onPressed: () => Navigator.of(context).pop(), ), ); } void showFullScreenImage( BuildContext context, List imageUrls, int index, ) { Navigator.push( context, MaterialPageRoute( builder: (context) => ImagePreviewPage(images: imageUrls, initialIndex: index), ), ); // Navigator.of(context).push( // PageRouteBuilder( // opaque: false, // pageBuilder: ( // BuildContext context, // Animation animation, // Animation secondaryAnimation, // ) { // return Scaffold( // backgroundColor: Colors.black.withAlpha(200), // body: Stack( // children: [ // // 可缩放图片 // Positioned.fill(child: _buildPhotoView(imageProvider)), // // 关闭按钮 // _buildCloseImage(context), // ], // ), // ); // }, // ), // ); } Widget buildImageUploadButton({required VoidCallback onPickImage}) { return InkWell( onTap: onPickImage, borderRadius: BorderRadius.circular(8), child: Container( width: 96, height: 96, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.black), ), child: const Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.add, color: Color(0xFF86909C)), SizedBox(height: 6), Text('添加图片', style: TextStyle(color: Color(0xFF86909C))), ], ), ), ); } Widget buildDeleteImage({required VoidCallback onRemoveImage}) { return Positioned( top: 0, right: 0, child: GestureDetector( onTap: onRemoveImage, child: Container( width: 24, height: 24, decoration: const BoxDecoration( color: Colors.red, shape: BoxShape.circle, ), child: const Icon(Icons.close, color: Colors.white, size: 16), ), ), ); }