import 'dart:io'; import 'dart:typed_data'; import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; import 'package:gifunavi/model/destination.dart'; class CustomCameraView extends StatefulWidget { final Function(String) onImageCaptured; final Destination? destination; final Function(bool) onCameraStatusChanged; //const CustomCameraView({super.key, required this.onImageCaptured, required this.destination}); const CustomCameraView({ Key? key, required this.onImageCaptured, required this.destination, required this.onCameraStatusChanged, // 新しいコールバック }) : super(key: key); @override _CustomCameraViewState createState() => _CustomCameraViewState(); } class _CustomCameraViewState extends State { CameraController? _controller; bool _isCameraAvailable = true; late List _cameras; int _selectedCameraIndex = 0; double _currentScale = 1.0; FlashMode _currentFlashMode = FlashMode.off; Destination? destination; @override void initState() { super.initState(); _initializeCamera(); destination = widget.destination; } Future _initializeCamera() async { try { _cameras = await availableCameras(); if (_cameras.isNotEmpty) { _controller = CameraController( _cameras[_selectedCameraIndex], ResolutionPreset.medium); await _controller!.initialize(); setState(() { _isCameraAvailable = true; }); } else { throw Exception('Camera is not available'); } }catch(err){ print("Error initializing camera: $err"); setState(() { _isCameraAvailable = false; }); } widget.onCameraStatusChanged(_isCameraAvailable); } @override void dispose() { _controller?.dispose(); super.dispose(); } Future _toggleCameraLens() async { final newIndex = (_selectedCameraIndex + 1) % _cameras.length; await _controller!.dispose(); setState(() { _controller = null; _selectedCameraIndex = newIndex; }); _controller = CameraController(_cameras[_selectedCameraIndex], ResolutionPreset.medium); await _controller!.initialize(); setState(() {}); } void _toggleFlashMode() { setState(() { _currentFlashMode = (_currentFlashMode == FlashMode.off) ? FlashMode.torch : FlashMode.off; }); _controller!.setFlashMode(_currentFlashMode); } void _zoomIn() { setState(() { _currentScale += 0.1; if (_currentScale > 5.0) _currentScale = 5.0; }); _controller!.setZoomLevel(_currentScale); } void _zoomOut() { setState(() { _currentScale -= 0.1; if (_currentScale < 1.0) _currentScale = 1.0; }); _controller!.setZoomLevel(_currentScale); } void _captureImage() async { if (_isCameraAvailable) { if (_controller!.value.isInitialized) { final Directory appDirectory = await getApplicationDocumentsDirectory(); final String imagePath = path.join( appDirectory.path, '${DateTime.now()}.jpg'); final XFile imageFile = await _controller!.takePicture(); await imageFile.saveTo(imagePath); widget.onImageCaptured(imagePath); Navigator.pop(context); } }else{ // ダミー画像を使用 final String imagePath = await _saveDummyImage(); widget.onImageCaptured(imagePath); Navigator.pop(context); } } Future _saveDummyImage() async { final Directory appDirectory = await getApplicationDocumentsDirectory(); final String imagePath = path.join(appDirectory.path, 'dummy_${DateTime.now()}.png'); // アセットからダミー画像を読み込む ByteData data = await rootBundle.load('assets/images/dummy_camera_image.png'); List bytes = data.buffer.asUint8List(); // ダミー画像をファイルとして保存 await File(imagePath).writeAsBytes(bytes); return imagePath; } @override Widget build(BuildContext context) { if (!_isCameraAvailable) { return _buildDummyCameraView(); } if (_controller == null || !_controller!.value.isInitialized) { return Container(); } return Stack( children: [ Padding( padding: const EdgeInsets.only(top: 60.0), // 上部に60ピクセルのパディングを追加 child: CameraPreview(_controller!), ), Positioned( bottom: 120.0, left: 16.0, right: 16.0, child: Center( child: Text( destination?.tags ?? '', style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), ), ), Positioned( bottom: 16.0, left: 16.0, right: 16.0, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( onPressed: _toggleFlashMode, icon: Icon( (_currentFlashMode == FlashMode.off) ? Icons.flash_off : Icons.flash_on, color: Colors.white, ), iconSize: 32, color: Colors.orange, ), GestureDetector( onTap: _captureImage, child: Container( height: 80, width: 80, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.white, border: Border.all(color: Colors.red, width: 4), ), child: const Icon(Icons.camera_alt, color: Colors.red, size: 40), ), ), IconButton( onPressed: _toggleCameraLens, icon: const Icon(Icons.flip_camera_ios, color: Colors.white), iconSize: 32, color: Colors.blue, ), ], ), ), Positioned( top: 16.0, right: 16.0, child: Column( children: [ IconButton( onPressed: _zoomIn, icon: const Icon(Icons.zoom_in, color: Colors.white), iconSize: 32, color: Colors.green, ), IconButton( onPressed: _zoomOut, icon: const Icon(Icons.zoom_out, color: Colors.white), iconSize: 32, color: Colors.green, ), ], ), ), ], ); } Widget _buildDummyCameraView() { return Stack( children: [ Container( color: Colors.black, child: const Center( child: Text( 'カメラを利用できません', style: TextStyle(color: Colors.white, fontSize: 18), ), ), ), Positioned( bottom: 16.0, left: 16.0, right: 16.0, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( onPressed: () {}, icon: const Icon(Icons.flash_off, color: Colors.white), iconSize: 32, ), GestureDetector( onTap: _captureEmulatedImage, child: Container( height: 80, width: 80, decoration: BoxDecoration( shape: BoxShape.circle, color: Colors.white, border: Border.all(color: Colors.red, width: 4), ), child: const Icon(Icons.camera_alt, color: Colors.red, size: 40), ), ), IconButton( onPressed: () {}, icon: const Icon(Icons.flip_camera_ios, color: Colors.white), iconSize: 32, ), ], ), ), ], ); } void _captureEmulatedImage() async { final Directory appDirectory = await getApplicationDocumentsDirectory(); final String imagePath = path.join(appDirectory.path, '${DateTime.now()}.jpg'); // ダミーの画像ファイルを作成 await File(imagePath).writeAsBytes(Uint8List(0)); widget.onImageCaptured(imagePath); Navigator.pop(context); } }