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.

387 lines
10KB

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