import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:mini_tias/providers/gallery_provider.dart';
import 'package:mini_tias/widgets/image_grid.dart';
/// 一覧画面.撮影済み画像のサムネイルグリッドを表示する.
class GalleryScreen extends StatefulWidget {
const GalleryScreen({super.key});
@override
State<GalleryScreen> createState() => _GalleryScreenState();
}
class _GalleryScreenState extends State<GalleryScreen> {
File? _selectedFile;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<GalleryProvider>().loadImages();
});
}
@override
Widget build(BuildContext context) {
final galleryProvider = context.watch<GalleryProvider>();
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)),
),
],
),
);
}
}