import 'dart:io'; import 'package:flutter/material.dart'; import 'package:lucide_icons/lucide_icons.dart'; import 'package:image_picker/image_picker.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as path; import 'package:hive_flutter/hive_flutter.dart'; import '../../../models/sake_item.dart'; import '../../../theme/app_colors.dart'; import '../../camera_screen.dart'; /// 写真編集モーダルウィジェット class SakePhotoEditModal extends StatefulWidget { final SakeItem sake; final Function(SakeItem) onUpdated; const SakePhotoEditModal({ super.key, required this.sake, required this.onUpdated, }); @override State createState() => _SakePhotoEditModalState(); } class _SakePhotoEditModalState extends State { late List _imagePaths; @override void initState() { super.initState(); _imagePaths = List.from(widget.sake.displayData.imagePaths); } @override Widget build(BuildContext context) { return Container( height: MediaQuery.of(context).size.height * 0.7, decoration: BoxDecoration( color: Theme.of(context).scaffoldBackgroundColor, borderRadius: const BorderRadius.vertical(top: Radius.circular(20)), ), child: Column( children: [ // Handle bar Container( margin: const EdgeInsets.only(top: 12, bottom: 8), width: 40, height: 4, decoration: BoxDecoration( color: Theme.of(context).extension()!.divider, borderRadius: BorderRadius.circular(2), ), ), // Header Padding( padding: const EdgeInsets.all(16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( '写真を編集', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), IconButton( icon: const Icon(LucideIcons.x), onPressed: () => Navigator.pop(context), ), ], ), ), const Divider(height: 1), // Photo grid Expanded( child: _imagePaths.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(LucideIcons.image, size: 64, color: Theme.of(context).extension()!.iconSubtle), const SizedBox(height: 16), Text( '写真を追加してください', style: TextStyle(color: Theme.of(context).extension()!.textSecondary), ), ], ), ) : ReorderableListView.builder( padding: const EdgeInsets.all(16), itemCount: _imagePaths.length, onReorder: (oldIndex, newIndex) { setState(() { if (oldIndex < newIndex) { newIndex -= 1; } final item = _imagePaths.removeAt(oldIndex); _imagePaths.insert(newIndex, item); }); }, itemBuilder: (context, index) { final photoPath = _imagePaths[index]; return Card( key: ValueKey(photoPath), margin: const EdgeInsets.only(bottom: 12), child: ListTile( leading: ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.file( File(photoPath), width: 60, height: 60, fit: BoxFit.cover, ), ), title: Text( index == 0 ? 'メイン写真' : '写真 ${index + 1}', style: const TextStyle(fontWeight: FontWeight.w500), ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(LucideIcons.gripVertical, color: Theme.of(context).extension()!.iconSubtle, size: 32), const SizedBox(width: 8), IconButton( icon: Icon(LucideIcons.trash2, color: Theme.of(context).extension()!.error), onPressed: () => _deletePhoto(index), ), ], ), ), ); }, ), ), // Bottom buttons Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).cardColor, boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 8, offset: const Offset(0, -2), ), ], ), child: SafeArea( child: Row( children: [ Expanded( child: OutlinedButton.icon( icon: const Icon(LucideIcons.camera), label: const Text('写真を追加'), onPressed: _addPhoto, style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 14), ), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton( onPressed: _saveChanges, style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).extension()!.brandPrimary, foregroundColor: Theme.of(context).extension()!.surfaceSubtle, padding: const EdgeInsets.symmetric(vertical: 14), ), child: const Text('保存'), ), ), ], ), ), ), ], ), ); } Future _addPhoto() async { final picker = ImagePicker(); // Show bottom sheet with camera/gallery options final source = await showModalBottomSheet( context: context, builder: (context) => SafeArea( child: Wrap( children: [ ListTile( leading: const Icon(LucideIcons.camera), title: const Text('カメラで撮影'), onTap: () async { Navigator.pop(context); // Close sheet // Navigate to CameraScreen in returnPath mode final result = await Navigator.push( context, MaterialPageRoute(builder: (_) => CameraScreen(mode: CameraMode.returnPath)), ); if (result is String && context.mounted) { // Add the path await _saveNewPhoto(result); } }, ), ListTile( leading: const Icon(LucideIcons.image), title: const Text('ギャラリーから選択'), onTap: () => Navigator.pop(context, ImageSource.gallery), ), ], ), ), ); if (source == null) return; // Handle Gallery (Camera is handled in ListTile callback) if (source == ImageSource.gallery) { try { final XFile? pickedFile = await picker.pickImage(source: source); if (pickedFile == null) return; // Save to app directory final appDir = await getApplicationDocumentsDirectory(); final fileName = '${DateTime.now().millisecondsSinceEpoch}.jpg'; final savedPath = path.join(appDir.path, fileName); await File(pickedFile.path).copy(savedPath); if (!mounted) return; await _saveNewPhoto(savedPath); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('エラー: $e')), ); } } } } Future _saveNewPhoto(String imagePath) async { setState(() { _imagePaths.add(imagePath); }); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('写真を追加しました')), ); } } void _deletePhoto(int index) { setState(() { _imagePaths.removeAt(index); }); } Future _saveChanges() async { final box = Hive.box('sake_items'); final updatedSake = widget.sake.copyWith( imagePaths: _imagePaths, isUserEdited: true, ); await box.put(widget.sake.key, updatedSake); widget.onUpdated(updatedSake); if (mounted) { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('写真を更新しました')), ); } } }