import 'dart:io'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:smtias_capture/providers/gallery_provider.dart'; import 'package:smtias_capture/widgets/image_grid.dart'; /// 一覧画面.撮影済み画像のサムネイルグリッドを表示する. class GalleryScreen extends StatefulWidget { const GalleryScreen({super.key}); @override State createState() => _GalleryScreenState(); } class _GalleryScreenState extends State { File? _selectedFile; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { context.read().loadImages(); }); } @override Widget build(BuildContext context) { final galleryProvider = context.watch(); if (galleryProvider.isLoading) { return const Center(child: CircularProgressIndicator()); } if (galleryProvider.images.isEmpty) { return const Center(child: Text('撮影した画像がありません')); } return Stack( children: [ ImageGrid( images: galleryProvider.images, onImageTap: (file) => setState(() => _selectedFile = file), ), // 画像詳細表示(この領域内に収まる) if (_selectedFile != null) Positioned.fill( child: Stack( children: [ // 画像(全面・上下クロップ) InteractiveViewer( minScale: 1.0, maxScale: 5.0, child: SizedBox.expand( child: Image.file(_selectedFile!, fit: BoxFit.cover), ), ), // ヘッダー(ファイル名 + 閉じるボタン) Positioned( top: 0, left: 0, right: 0, child: Container( color: Colors.black54, padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), child: Row( children: [ Expanded( child: Text( _displayName(_selectedFile!), style: const TextStyle( color: Colors.white, fontSize: 12, ), overflow: TextOverflow.ellipsis, ), ), IconButton( icon: const Icon(Icons.close, color: Colors.white), onPressed: () => setState(() => _selectedFile = null), ), ], ), ), ), // 削除ボタン Positioned( bottom: 0, left: 0, right: 0, child: Container( color: Colors.black54, padding: const EdgeInsets.all(8), child: Center( child: TextButton.icon( onPressed: () => _confirmDelete(galleryProvider), icon: const Icon(Icons.delete, color: Colors.red), label: const Text( '削除', style: TextStyle(color: Colors.red), ), ), ), ), ), ], ), ), ], ); } /// QM プレビューファイル(*.preview.jpg)かどうかを返す. bool _isQmPreview(File file) => file.path.toLowerCase().endsWith('.preview.jpg'); /// ファイルの表示名を返す. /// /// QM プレビュー (.preview.jpg) は対応する DNG ファイル名で表示する /// (DNG が「本体」であることをユーザーに伝えるため). String _displayName(File file) { final name = file.path.split('/').last; if (_isQmPreview(file)) { return '${name.substring(0, name.length - '.preview.jpg'.length)}.dng'; } return name; } void _confirmDelete(GalleryProvider galleryProvider) { final file = _selectedFile; if (file == null) return; final message = _isQmPreview(file) ? 'この画像を削除しますか?\n(DNG ファイルとメタデータも一緒に削除されます)' : 'この画像を削除しますか?'; showDialog( context: context, builder: (context) => AlertDialog( title: const Text('画像を削除'), content: Text(message), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('キャンセル'), ), TextButton( onPressed: () { Navigator.of(context).pop(); setState(() => _selectedFile = null); galleryProvider.deleteImage(file); }, child: const Text('削除', style: TextStyle(color: Colors.red)), ), ], ), ); } }