You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

372 lines
10KB

  1. import 'dart:io';
  2. import 'package:camera/camera.dart';
  3. import 'package:farm_tpf/main.dart';
  4. import 'package:farm_tpf/presentation/custom_widgets/widget_utils.dart';
  5. import 'package:file_picker/file_picker.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:get/get.dart';
  8. import 'package:path_provider/path_provider.dart';
  9. class CameraHelper extends StatefulWidget {
  10. @override
  11. _CameraHelperState createState() => _CameraHelperState();
  12. }
  13. /// Returns a suitable camera icon for [direction].
  14. IconData getCameraLensIcon(CameraLensDirection direction) {
  15. switch (direction) {
  16. case CameraLensDirection.back:
  17. return Icons.camera_rear;
  18. case CameraLensDirection.front:
  19. return Icons.camera_front;
  20. case CameraLensDirection.external:
  21. return Icons.camera;
  22. }
  23. throw ArgumentError('Unknown lens direction');
  24. }
  25. void logError(String code, String message) => print('Error: $code\nError Message: $message');
  26. class _CameraHelperState extends State<CameraHelper> with WidgetsBindingObserver {
  27. CameraController? controller;
  28. String? imagePath;
  29. String? videoPath;
  30. VoidCallback? videoPlayerListener;
  31. bool enableAudio = true;
  32. int indexCamera = 0;
  33. @override
  34. void initState() {
  35. super.initState();
  36. // controller = CameraController(cameras[indexCamera], ResolutionPreset.medium);
  37. controller?.initialize().then((_) {
  38. if (!mounted) {
  39. return;
  40. }
  41. setState(() {});
  42. });
  43. WidgetsBinding.instance.addObserver(this);
  44. }
  45. @override
  46. void dispose() {
  47. WidgetsBinding.instance.removeObserver(this);
  48. super.dispose();
  49. }
  50. @override
  51. void didChangeAppLifecycleState(AppLifecycleState state) {
  52. // App state changed before we got the chance to initialize.
  53. if (controller == null || !controller!.value.isInitialized) {
  54. return;
  55. }
  56. if (state == AppLifecycleState.inactive) {
  57. controller?.dispose();
  58. } else if (state == AppLifecycleState.resumed) {
  59. if (controller != null) {
  60. onNewCameraSelected(controller!.description);
  61. }
  62. }
  63. }
  64. final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  65. @override
  66. Widget build(BuildContext context) {
  67. return Scaffold(
  68. key: _scaffoldKey,
  69. body: SafeArea(
  70. top: false,
  71. bottom: false,
  72. child: Column(
  73. children: <Widget>[
  74. Expanded(
  75. child: Container(
  76. child: Padding(
  77. padding: const EdgeInsets.all(1.0),
  78. child: Center(
  79. child: _cameraPreviewWidget(),
  80. ),
  81. ),
  82. decoration: BoxDecoration(
  83. color: Colors.black,
  84. border: Border.all(
  85. color: controller != null && controller!.value.isRecordingVideo ? Colors.redAccent : Colors.grey,
  86. width: controller != null && controller!.value.isRecordingVideo ? 1.0 : 0.0,
  87. ),
  88. ),
  89. ),
  90. ),
  91. _captureControlRowWidget(),
  92. ],
  93. ),
  94. ),
  95. );
  96. }
  97. /// Display the preview from the camera (or a message if the preview is not available).
  98. Widget _cameraPreviewWidget() {
  99. if (controller == null || !controller!.value.isInitialized) {
  100. return const Text(
  101. 'Đang mở camera ....',
  102. style: TextStyle(
  103. color: Colors.white,
  104. fontSize: 18.0,
  105. fontWeight: FontWeight.w900,
  106. ),
  107. );
  108. } else {
  109. return AspectRatio(
  110. aspectRatio: controller!.value.aspectRatio,
  111. child: CameraPreview(controller!),
  112. );
  113. }
  114. }
  115. /// Display the control bar with buttons to take pictures and record videos.
  116. Widget _captureControlRowWidget() {
  117. return Row(
  118. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  119. mainAxisSize: MainAxisSize.max,
  120. children: <Widget>[
  121. IconButton(
  122. icon: Icon(Icons.arrow_back),
  123. onPressed: () {
  124. Get.back();
  125. }),
  126. _cameraTogglesRowWidget(),
  127. IconButton(
  128. icon: const Icon(
  129. Icons.camera_alt,
  130. size: 30,
  131. ),
  132. color: Colors.blue,
  133. onPressed: controller != null && controller!.value.isInitialized && !controller!.value.isRecordingVideo ? onTakePictureButtonPressed : null,
  134. ),
  135. // IconButton(
  136. // icon: const Icon(Icons.videocam, size: 35),
  137. // color: Colors.blue,
  138. // onPressed: controller != null && controller.value.isInitialized && !controller.value.isRecordingVideo ? onVideoRecordButtonPressed : null,
  139. // ),
  140. // IconButton(
  141. // icon: controller != null && controller.value.isRecordingPaused ? Icon(Icons.play_arrow) : Icon(Icons.pause),
  142. // color: Colors.blue,
  143. // onPressed: controller != null && controller.value.isInitialized && controller.value.isRecordingVideo
  144. // ? (controller != null && controller.value.isRecordingPaused ? onResumeButtonPressed : onPauseButtonPressed)
  145. // : null,
  146. // ),
  147. // IconButton(
  148. // icon: const Icon(Icons.stop),
  149. // color: Colors.red,
  150. // onPressed: controller != null && controller.value.isInitialized && controller.value.isRecordingVideo ? onStopButtonPressed : null,
  151. // )
  152. ],
  153. );
  154. }
  155. /// Display a row of toggle to select the camera (or a message if no camera is available).
  156. Widget _cameraTogglesRowWidget() {
  157. // if (cameras.isEmpty) {
  158. // return const Text('Không có camera');
  159. // } else {
  160. // bool disableSwitch = controller != null && controller!.value.isRecordingVideo;
  161. // if (indexCamera == cameras.length - 1) {
  162. // indexCamera = 0;
  163. // } else {
  164. // indexCamera++;
  165. // }
  166. // return IconButton(
  167. // icon: Icon(
  168. // Icons.switch_camera,
  169. // color: Colors.green,
  170. // ),
  171. // onPressed: disableSwitch == true
  172. // ? null
  173. // : () {
  174. // onNewCameraSelected(cameras[indexCamera]);
  175. // });
  176. // }
  177. return Container();
  178. }
  179. String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();
  180. void showInSnackBar(String message) {
  181. Utils.showSnackBarError(message: message);
  182. }
  183. void onNewCameraSelected(CameraDescription cameraDescription) async {
  184. if (controller != null) {
  185. await controller?.dispose();
  186. }
  187. controller = CameraController(
  188. cameraDescription,
  189. ResolutionPreset.medium,
  190. enableAudio: enableAudio,
  191. );
  192. // If the controller is updated then update the UI.
  193. controller?.addListener(() {
  194. if (mounted) setState(() {});
  195. if (controller!.value.hasError) {
  196. showInSnackBar('Lỗi camera: ${controller?.value.errorDescription}');
  197. }
  198. });
  199. try {
  200. await controller?.initialize();
  201. } on CameraException catch (e) {
  202. _showCameraException(e);
  203. }
  204. if (mounted) {
  205. setState(() {});
  206. }
  207. }
  208. void onTakePictureButtonPressed() {
  209. takePicture().then((String filePath) {
  210. if (mounted) {
  211. setState(() {
  212. imagePath = filePath;
  213. // videoController?.dispose();
  214. // videoController = null;
  215. });
  216. if (filePath != null) {
  217. print('Picture saved to $filePath');
  218. Get.back(result: [filePath, false]);
  219. }
  220. }
  221. });
  222. }
  223. void onVideoRecordButtonPressed() {
  224. startVideoRecording().then((String filePath) {
  225. if (mounted) setState(() {});
  226. if (filePath != null) {
  227. print('Saving video to $filePath');
  228. }
  229. });
  230. }
  231. void onStopButtonPressed() {
  232. stopVideoRecording().then((_) {
  233. if (mounted) setState(() {});
  234. {
  235. print('Video recorded to: $videoPath');
  236. Get.back(result: [videoPath, true]);
  237. }
  238. });
  239. }
  240. void onPauseButtonPressed() {
  241. pauseVideoRecording().then((_) {
  242. if (mounted) setState(() {});
  243. print('Video recording paused');
  244. });
  245. }
  246. void onResumeButtonPressed() {
  247. resumeVideoRecording().then((_) {
  248. if (mounted) setState(() {});
  249. print('Video recording resumed');
  250. });
  251. }
  252. Future<String> startVideoRecording() async {
  253. if (!controller!.value.isInitialized) {
  254. showInSnackBar('Vui lòng chọn camera');
  255. return '';
  256. }
  257. final Directory extDir = await getApplicationDocumentsDirectory();
  258. final String dirPath = '${extDir.path}/Movies/tpf';
  259. await Directory(dirPath).create(recursive: true);
  260. final String filePath = '$dirPath/${timestamp()}.mp4';
  261. if (controller!.value.isRecordingVideo) {
  262. // A recording is already started, do nothing.
  263. return '';
  264. }
  265. try {
  266. videoPath = filePath;
  267. // await controller?.startVideoRecording(filePath ?? '');
  268. } on CameraException catch (e) {
  269. _showCameraException(e);
  270. return '';
  271. }
  272. return filePath;
  273. }
  274. Future<void> stopVideoRecording() async {
  275. if (!controller!.value.isRecordingVideo) {
  276. return null;
  277. }
  278. try {
  279. await controller?.stopVideoRecording();
  280. } on CameraException catch (e) {
  281. _showCameraException(e);
  282. return null;
  283. }
  284. }
  285. Future<void> pauseVideoRecording() async {
  286. if (!controller!.value.isRecordingVideo) {
  287. return null;
  288. }
  289. try {
  290. await controller?.pauseVideoRecording();
  291. } on CameraException catch (e) {
  292. _showCameraException(e);
  293. rethrow;
  294. }
  295. }
  296. Future<void> resumeVideoRecording() async {
  297. if (!controller!.value.isRecordingVideo) {
  298. return null;
  299. }
  300. try {
  301. await controller?.resumeVideoRecording();
  302. } on CameraException catch (e) {
  303. _showCameraException(e);
  304. rethrow;
  305. }
  306. }
  307. Future<String> takePicture() async {
  308. if (!controller!.value.isInitialized) {
  309. showInSnackBar('Vui lòng chọn camera');
  310. return '';
  311. }
  312. final Directory extDir = await getApplicationDocumentsDirectory();
  313. final String dirPath = '${extDir.path}/Pictures/tpf';
  314. await Directory(dirPath).create(recursive: true);
  315. String filePath = '$dirPath/${timestamp()}.jpg';
  316. if (controller!.value.isTakingPicture) {
  317. // A capture is already pending, do nothing.
  318. return '';
  319. }
  320. try {
  321. var xFile = await controller?.takePicture(); //.takePicture(filePath);
  322. filePath = xFile?.path ?? '';
  323. } on CameraException catch (e) {
  324. _showCameraException(e);
  325. return '';
  326. }
  327. return filePath;
  328. }
  329. void _showCameraException(CameraException e) {
  330. logError(e.code, e.description ?? '');
  331. showInSnackBar('Lỗi: ${e.code}\n${e.description}');
  332. }
  333. }