import 'dart:io'; import 'package:camera/camera.dart'; import 'package:farm_tpf/main.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:video_player/video_player.dart'; import 'package:path_provider/path_provider.dart'; class CameraHelper extends StatefulWidget { @override _CameraHelperState createState() => _CameraHelperState(); } /// Returns a suitable camera icon for [direction]. IconData getCameraLensIcon(CameraLensDirection direction) { switch (direction) { case CameraLensDirection.back: return Icons.camera_rear; case CameraLensDirection.front: return Icons.camera_front; case CameraLensDirection.external: return Icons.camera; } throw ArgumentError('Unknown lens direction'); } void logError(String code, String message) => print('Error: $code\nError Message: $message'); class _CameraHelperState extends State with WidgetsBindingObserver { CameraController controller; String imagePath; String videoPath; VideoPlayerController videoController; VoidCallback videoPlayerListener; bool enableAudio = true; int indexCamera = 0; @override void initState() { super.initState(); controller = CameraController(cameras[indexCamera], ResolutionPreset.medium); controller.initialize().then((_) { if (!mounted) { return; } setState(() {}); }); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { // App state changed before we got the chance to initialize. if (controller == null || !controller.value.isInitialized) { return; } if (state == AppLifecycleState.inactive) { controller?.dispose(); } else if (state == AppLifecycleState.resumed) { if (controller != null) { onNewCameraSelected(controller.description); } } } final GlobalKey _scaffoldKey = GlobalKey(); @override Widget build(BuildContext context) { return Scaffold( key: _scaffoldKey, body: SafeArea( top: false, bottom: false, child: Column( children: [ Expanded( child: Container( child: Padding( padding: const EdgeInsets.all(1.0), child: Center( child: _cameraPreviewWidget(), ), ), decoration: BoxDecoration( color: Colors.black, border: Border.all( color: controller != null && controller.value.isRecordingVideo ? Colors.redAccent : Colors.grey, width: controller != null && controller.value.isRecordingVideo ? 1.0 : 0.0, ), ), ), ), _captureControlRowWidget(), ], ), ), ); } /// Display the preview from the camera (or a message if the preview is not available). Widget _cameraPreviewWidget() { if (controller == null || !controller.value.isInitialized) { return const Text( 'Đang mở camera ....', style: TextStyle( color: Colors.white, fontSize: 20.0, fontWeight: FontWeight.w900, ), ); } else { return AspectRatio( aspectRatio: controller.value.aspectRatio, child: CameraPreview(controller), ); } } /// Display the control bar with buttons to take pictures and record videos. Widget _captureControlRowWidget() { return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisSize: MainAxisSize.max, children: [ IconButton( icon: Icon(Icons.arrow_back), onPressed: () { Get.back(); }), _cameraTogglesRowWidget(), IconButton( icon: const Icon(Icons.camera_alt), color: Colors.blue, onPressed: controller != null && controller.value.isInitialized && !controller.value.isRecordingVideo ? onTakePictureButtonPressed : null, ), IconButton( icon: const Icon(Icons.videocam), color: Colors.blue, onPressed: controller != null && controller.value.isInitialized && !controller.value.isRecordingVideo ? onVideoRecordButtonPressed : null, ), IconButton( icon: controller != null && controller.value.isRecordingPaused ? Icon(Icons.play_arrow) : Icon(Icons.pause), color: Colors.blue, onPressed: controller != null && controller.value.isInitialized && controller.value.isRecordingVideo ? (controller != null && controller.value.isRecordingPaused ? onResumeButtonPressed : onPauseButtonPressed) : null, ), IconButton( icon: const Icon(Icons.stop), color: Colors.red, onPressed: controller != null && controller.value.isInitialized && controller.value.isRecordingVideo ? onStopButtonPressed : null, ) ], ); } /// Display a row of toggle to select the camera (or a message if no camera is available). Widget _cameraTogglesRowWidget() { if (cameras.isEmpty) { return const Text('Không có camera'); } else { bool disableSwitch = controller != null && controller.value.isRecordingVideo; if (indexCamera == cameras.length - 1) { indexCamera = 0; } else { indexCamera++; } return IconButton( icon: Icon( Icons.switch_camera, color: Colors.green, ), onPressed: disableSwitch == true ? null : () { onNewCameraSelected(cameras[indexCamera]); }); } } String timestamp() => DateTime.now().millisecondsSinceEpoch.toString(); void showInSnackBar(String message) { _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(message))); } void onNewCameraSelected(CameraDescription cameraDescription) async { if (controller != null) { await controller.dispose(); } controller = CameraController( cameraDescription, ResolutionPreset.medium, enableAudio: enableAudio, ); // If the controller is updated then update the UI. controller.addListener(() { if (mounted) setState(() {}); if (controller.value.hasError) { showInSnackBar('Lỗi camera: ${controller.value.errorDescription}'); } }); try { await controller.initialize(); } on CameraException catch (e) { _showCameraException(e); } if (mounted) { setState(() {}); } } void onTakePictureButtonPressed() { takePicture().then((String filePath) { if (mounted) { setState(() { imagePath = filePath; videoController?.dispose(); videoController = null; }); if (filePath != null) print('Picture saved to $filePath'); } }); } void onVideoRecordButtonPressed() { startVideoRecording().then((String filePath) { if (mounted) setState(() {}); if (filePath != null) print('Saving video to $filePath'); }); } void onStopButtonPressed() { stopVideoRecording().then((_) { if (mounted) setState(() {}); print('Video recorded to: $videoPath'); }); } void onPauseButtonPressed() { pauseVideoRecording().then((_) { if (mounted) setState(() {}); print('Video recording paused'); }); } void onResumeButtonPressed() { resumeVideoRecording().then((_) { if (mounted) setState(() {}); print('Video recording resumed'); }); } Future startVideoRecording() async { if (!controller.value.isInitialized) { showInSnackBar('Vui lòng chọn camera'); return null; } final Directory extDir = await getApplicationDocumentsDirectory(); final String dirPath = '${extDir.path}/Movies/tpf'; await Directory(dirPath).create(recursive: true); final String filePath = '$dirPath/${timestamp()}.mp4'; if (controller.value.isRecordingVideo) { // A recording is already started, do nothing. return null; } try { videoPath = filePath; await controller.startVideoRecording(filePath); } on CameraException catch (e) { _showCameraException(e); return null; } return filePath; } Future stopVideoRecording() async { if (!controller.value.isRecordingVideo) { return null; } try { await controller.stopVideoRecording(); } on CameraException catch (e) { _showCameraException(e); return null; } } Future pauseVideoRecording() async { if (!controller.value.isRecordingVideo) { return null; } try { await controller.pauseVideoRecording(); } on CameraException catch (e) { _showCameraException(e); rethrow; } } Future resumeVideoRecording() async { if (!controller.value.isRecordingVideo) { return null; } try { await controller.resumeVideoRecording(); } on CameraException catch (e) { _showCameraException(e); rethrow; } } Future takePicture() async { if (!controller.value.isInitialized) { showInSnackBar('Vui lòng chọn camera'); return null; } final Directory extDir = await getApplicationDocumentsDirectory(); final String dirPath = '${extDir.path}/Pictures/tpf'; await Directory(dirPath).create(recursive: true); final String filePath = '$dirPath/${timestamp()}.jpg'; if (controller.value.isTakingPicture) { // A capture is already pending, do nothing. return null; } try { await controller.takePicture(filePath); } on CameraException catch (e) { _showCameraException(e); return null; } return filePath; } void _showCameraException(CameraException e) { logError(e.code, e.description); showInSnackBar('Lỗi: ${e.code}\n${e.description}'); } }