import 'dart:io'; import 'package:flutter/material.dart'; import 'package:camera/camera.dart'; import 'package:audioplayers/audioplayers.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:mobdr/service/shared_prefs.dart'; class CustomCameraPreview extends StatefulWidget { final List photoFiles; final CameraController cameraController; CustomCameraPreview( {Key? key, required this.photoFiles, required this.cameraController}) : super(key: key); @override _CustomCameraPreviewState createState() => _CustomCameraPreviewState(); } class _CustomCameraPreviewState extends State { final AudioPlayer _audioPlayer = AudioPlayer(); bool isTakingPhoto = false; @override Widget build(BuildContext context) { return Stack( alignment: FractionalOffset.center, children: [ Positioned.fill( child: AspectRatio( aspectRatio: widget.cameraController.value.aspectRatio, child: CameraPreview(widget.cameraController), ), ), Positioned( bottom: 5, child: FloatingActionButton( heroTag: "camera", backgroundColor: Colors.white, onPressed: () async { if (widget.photoFiles.length != 10 && !isTakingPhoto) { setState(() { isTakingPhoto = true; }); try { var dirCacheXFile = await widget.cameraController.takePicture(); /// move jpg file to photo directory final dirPhotoFile = await moveFromCacheToPhotosDir(File(dirCacheXFile.path)); widget.photoFiles.add(dirPhotoFile); if (SharedPrefs().photoSound) { await _audioPlayer.setVolume((0.5)); await _audioPlayer.play( AssetSource('sounds/camera-shutter-click.mp3'), ); } } finally { setState(() { isTakingPhoto = false; }); } } }, child: const Icon( Icons.camera_alt, color: Colors.black, ), ), ), _confirmButton(), _rejectButton(), Positioned( bottom: 80, child: SizedBox( height: 80, width: MediaQuery.of(context).size.width, child: ListView.builder( scrollDirection: Axis.horizontal, itemBuilder: (context, index) { return GestureDetector( onVerticalDragEnd: (details) { if (details.primaryVelocity! < 0) { setState(() { widget.photoFiles.removeAt(index); }); } }, child: Container( margin: const EdgeInsets.symmetric(horizontal: 2.0), decoration: BoxDecoration( border: Border.all(width: 2.0, color: Colors.white), ), child: index >= widget.photoFiles.length ? Container(color: Colors.white, width: 100) : Image.file( widget.photoFiles[index], fit: BoxFit.cover, height: 100, width: 100, ), ), ); }, itemCount: widget.photoFiles.length, ), ), ), ], ); } Positioned _rejectButton() { return Positioned( bottom: 5, left: 5, child: FloatingActionButton( heroTag: "close", backgroundColor: Colors.black, onPressed: !isTakingPhoto ? () { Navigator.pop(context, false); } : null, child: const Icon( Icons.close, color: Colors.white, ), ), ); } Positioned _confirmButton() { return Positioned( bottom: 5, right: 5, child: FloatingActionButton( heroTag: "confirm", backgroundColor: Colors.black, onPressed: !isTakingPhoto ? () { Navigator.pop(context, true); } : null, child: const Icon( Icons.check, color: Colors.white, ), ), ); } // Moves a temporary file to the photos directory in the app's document directory. /// /// Returns a `File` object for the new file in the photos directory. /// /// Parameters: /// * `tempFile`: The temporary `File` object to move to the photos directory. /// /// Throws a `FileSystemException` if there is an error renaming the file. Future moveFromCacheToPhotosDir(File tempFile) async { // Set the new file path with the original file name final String newPath = '${SharedPrefs().photosDir}/${tempFile.path.split('/').last}'; // Create a new File object with the new path final newFile = File(newPath); final bool photoResizing = SharedPrefs().photoResizing; final int? minWidth = photoResizing ? 1024 : null; final int? minHeight = photoResizing ? 768 : null; final fixedImageBytes = await FlutterImageCompress.compressWithFile( tempFile.path, minWidth: minWidth!, minHeight: minHeight!, rotate: 0, quality: 100, keepExif: false, autoCorrectionAngle: true, format: CompressFormat.jpeg, ); // Write the compressed image bytes to the new file await newFile.writeAsBytes(fixedImageBytes!); // deleting the temporary file await tempFile.delete(); // Create and return a new File object for the new file return File(newPath); } }