camera_test.dart 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011
  1. import 'dart:async';
  2. import 'package:camera/camera.dart';
  3. import 'package:camera_platform_interface/camera_platform_interface.dart';
  4. import 'package:flutter/foundation.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:flutter/scheduler.dart';
  7. // import 'package:video_player/video_player.dart';
  8. /// Camera example home widget.
  9. class CameraTest extends StatefulWidget {
  10. /// Default Constructor
  11. const CameraTest({super.key});
  12. @override
  13. State<CameraTest> createState() {
  14. return _CameraTestState();
  15. }
  16. }
  17. /// Returns a suitable camera icon for [direction].
  18. IconData getCameraLensIcon(CameraLensDirection direction) {
  19. switch (direction) {
  20. case CameraLensDirection.back:
  21. return Icons.camera_rear;
  22. case CameraLensDirection.front:
  23. return Icons.camera_front;
  24. case CameraLensDirection.external:
  25. return Icons.camera;
  26. }
  27. // This enum is from a different package, so a new value could be added at
  28. // any time. The example should keep working if that happens.
  29. // ignore: dead_code
  30. return Icons.camera;
  31. }
  32. void _logError(String code, String? message) {
  33. // ignore: avoid_print
  34. print('Error: $code${message == null ? '' : '\nError Message: $message'}');
  35. }
  36. class _CameraTestState extends State<CameraTest>
  37. with WidgetsBindingObserver, TickerProviderStateMixin {
  38. CameraController? controller;
  39. XFile? imageFile;
  40. XFile? videoFile;
  41. // VideoPlayerController? videoController;
  42. VoidCallback? videoPlayerListener;
  43. bool enableAudio = true;
  44. double _minAvailableExposureOffset = 0.0;
  45. double _maxAvailableExposureOffset = 0.0;
  46. double _currentExposureOffset = 0.0;
  47. late AnimationController _flashModeControlRowAnimationController;
  48. late Animation<double> _flashModeControlRowAnimation;
  49. late AnimationController _exposureModeControlRowAnimationController;
  50. late Animation<double> _exposureModeControlRowAnimation;
  51. late AnimationController _focusModeControlRowAnimationController;
  52. late Animation<double> _focusModeControlRowAnimation;
  53. double _minAvailableZoom = 1.0;
  54. double _maxAvailableZoom = 1.0;
  55. double _currentScale = 1.0;
  56. double _baseScale = 1.0;
  57. // Counting pointers (number of user fingers on screen)
  58. int _pointers = 0;
  59. List<CameraDescription> _cameras = <CameraDescription>[];
  60. void findCameras() async {
  61. try {
  62. _cameras = await availableCameras();
  63. setState(() {});
  64. // if (_cameras.isEmpty) {
  65. // showInSnackBar('No camera found');
  66. // } else {
  67. // onNewCameraSelected(_cameras.first);
  68. // }
  69. } on CameraException catch (e) {
  70. _logError(e.code, e.description);
  71. }
  72. }
  73. @override
  74. void initState() {
  75. super.initState();
  76. WidgetsBinding.instance.addObserver(this);
  77. findCameras();
  78. _flashModeControlRowAnimationController = AnimationController(
  79. duration: const Duration(milliseconds: 300),
  80. vsync: this,
  81. );
  82. _flashModeControlRowAnimation = CurvedAnimation(
  83. parent: _flashModeControlRowAnimationController,
  84. curve: Curves.easeInCubic,
  85. );
  86. _exposureModeControlRowAnimationController = AnimationController(
  87. duration: const Duration(milliseconds: 300),
  88. vsync: this,
  89. );
  90. _exposureModeControlRowAnimation = CurvedAnimation(
  91. parent: _exposureModeControlRowAnimationController,
  92. curve: Curves.easeInCubic,
  93. );
  94. _focusModeControlRowAnimationController = AnimationController(
  95. duration: const Duration(milliseconds: 300),
  96. vsync: this,
  97. );
  98. _focusModeControlRowAnimation = CurvedAnimation(
  99. parent: _focusModeControlRowAnimationController,
  100. curve: Curves.easeInCubic,
  101. );
  102. }
  103. @override
  104. void dispose() {
  105. WidgetsBinding.instance.removeObserver(this);
  106. _flashModeControlRowAnimationController.dispose();
  107. _exposureModeControlRowAnimationController.dispose();
  108. super.dispose();
  109. }
  110. // #docregion AppLifecycle
  111. @override
  112. void didChangeAppLifecycleState(AppLifecycleState state) {
  113. final CameraController? cameraController = controller;
  114. // App state changed before we got the chance to initialize.
  115. if (cameraController == null || !cameraController.value.isInitialized) {
  116. return;
  117. }
  118. if (state == AppLifecycleState.inactive) {
  119. cameraController.dispose();
  120. } else if (state == AppLifecycleState.resumed) {
  121. onNewCameraSelected(cameraController.description);
  122. }
  123. }
  124. // #enddocregion AppLifecycle
  125. @override
  126. Widget build(BuildContext context) {
  127. return Scaffold(
  128. appBar: AppBar(
  129. title: const Text('Camera example'),
  130. ),
  131. body: Column(
  132. children: <Widget>[
  133. Expanded(
  134. child: Container(
  135. decoration: BoxDecoration(
  136. color: Colors.black,
  137. border: Border.all(
  138. color:
  139. controller != null && controller!.value.isRecordingVideo
  140. ? Colors.redAccent
  141. : Colors.grey,
  142. width: 3.0,
  143. ),
  144. ),
  145. child: Padding(
  146. padding: const EdgeInsets.all(1.0),
  147. child: Center(
  148. child: _cameraPreviewWidget(),
  149. ),
  150. ),
  151. ),
  152. ),
  153. _captureControlRowWidget(),
  154. _modeControlRowWidget(),
  155. Padding(
  156. padding: const EdgeInsets.all(5.0),
  157. child: Row(
  158. children: <Widget>[
  159. _cameraTogglesRowWidget(),
  160. // _thumbnailWidget(),
  161. ],
  162. ),
  163. ),
  164. ],
  165. ),
  166. );
  167. }
  168. /// Display the preview from the camera (or a message if the preview is not available).
  169. Widget _cameraPreviewWidget() {
  170. final CameraController? cameraController = controller;
  171. if (cameraController == null || !cameraController.value.isInitialized) {
  172. return GestureDetector(
  173. onTap: () {
  174. findCameras();
  175. },
  176. child: const Text(
  177. 'Find cameras',
  178. style: TextStyle(
  179. color: Colors.white,
  180. fontSize: 24.0,
  181. fontWeight: FontWeight.w900,
  182. ),
  183. ),
  184. );
  185. } else {
  186. return Listener(
  187. onPointerDown: (_) => _pointers++,
  188. onPointerUp: (_) => _pointers--,
  189. child: CameraPreview(
  190. controller!,
  191. child: LayoutBuilder(
  192. builder: (BuildContext context, BoxConstraints constraints) {
  193. return GestureDetector(
  194. behavior: HitTestBehavior.opaque,
  195. onScaleStart: _handleScaleStart,
  196. onScaleUpdate: _handleScaleUpdate,
  197. onTapDown: (TapDownDetails details) =>
  198. onViewFinderTap(details, constraints),
  199. );
  200. }),
  201. ),
  202. );
  203. }
  204. }
  205. void _handleScaleStart(ScaleStartDetails details) {
  206. _baseScale = _currentScale;
  207. }
  208. Future<void> _handleScaleUpdate(ScaleUpdateDetails details) async {
  209. // When there are not exactly two fingers on screen don't scale
  210. if (controller == null || _pointers != 2) {
  211. return;
  212. }
  213. _currentScale = (_baseScale * details.scale)
  214. .clamp(_minAvailableZoom, _maxAvailableZoom);
  215. await controller!.setZoomLevel(_currentScale);
  216. }
  217. /// Display a bar with buttons to change the flash and exposure modes
  218. Widget _modeControlRowWidget() {
  219. return Column(
  220. children: <Widget>[
  221. Row(
  222. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  223. children: <Widget>[
  224. IconButton(
  225. icon: const Icon(Icons.flash_on),
  226. color: Colors.blue,
  227. onPressed: controller != null ? onFlashModeButtonPressed : null,
  228. ),
  229. // The exposure and focus mode are currently not supported on the web.
  230. ...!kIsWeb
  231. ? <Widget>[
  232. IconButton(
  233. icon: const Icon(Icons.exposure),
  234. color: Colors.blue,
  235. onPressed: controller != null
  236. ? onExposureModeButtonPressed
  237. : null,
  238. ),
  239. IconButton(
  240. icon: const Icon(Icons.filter_center_focus),
  241. color: Colors.blue,
  242. onPressed:
  243. controller != null ? onFocusModeButtonPressed : null,
  244. )
  245. ]
  246. : <Widget>[],
  247. IconButton(
  248. icon: Icon(enableAudio ? Icons.volume_up : Icons.volume_mute),
  249. color: Colors.blue,
  250. onPressed: controller != null ? onAudioModeButtonPressed : null,
  251. ),
  252. IconButton(
  253. icon: Icon(controller?.value.isCaptureOrientationLocked ?? false
  254. ? Icons.screen_lock_rotation
  255. : Icons.screen_rotation),
  256. color: Colors.blue,
  257. onPressed: controller != null
  258. ? onCaptureOrientationLockButtonPressed
  259. : null,
  260. ),
  261. ],
  262. ),
  263. _flashModeControlRowWidget(),
  264. _exposureModeControlRowWidget(),
  265. _focusModeControlRowWidget(),
  266. ],
  267. );
  268. }
  269. Widget _flashModeControlRowWidget() {
  270. return SizeTransition(
  271. sizeFactor: _flashModeControlRowAnimation,
  272. child: ClipRect(
  273. child: Row(
  274. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  275. children: <Widget>[
  276. IconButton(
  277. icon: const Icon(Icons.flash_off),
  278. color: controller?.value.flashMode == FlashMode.off
  279. ? Colors.orange
  280. : Colors.blue,
  281. onPressed: controller != null
  282. ? () => onSetFlashModeButtonPressed(FlashMode.off)
  283. : null,
  284. ),
  285. IconButton(
  286. icon: const Icon(Icons.flash_auto),
  287. color: controller?.value.flashMode == FlashMode.auto
  288. ? Colors.orange
  289. : Colors.blue,
  290. onPressed: controller != null
  291. ? () => onSetFlashModeButtonPressed(FlashMode.auto)
  292. : null,
  293. ),
  294. IconButton(
  295. icon: const Icon(Icons.flash_on),
  296. color: controller?.value.flashMode == FlashMode.always
  297. ? Colors.orange
  298. : Colors.blue,
  299. onPressed: controller != null
  300. ? () => onSetFlashModeButtonPressed(FlashMode.always)
  301. : null,
  302. ),
  303. IconButton(
  304. icon: const Icon(Icons.highlight),
  305. color: controller?.value.flashMode == FlashMode.torch
  306. ? Colors.orange
  307. : Colors.blue,
  308. onPressed: controller != null
  309. ? () => onSetFlashModeButtonPressed(FlashMode.torch)
  310. : null,
  311. ),
  312. ],
  313. ),
  314. ),
  315. );
  316. }
  317. Widget _exposureModeControlRowWidget() {
  318. final ButtonStyle styleAuto = TextButton.styleFrom(
  319. foregroundColor: controller?.value.exposureMode == ExposureMode.auto
  320. ? Colors.orange
  321. : Colors.blue,
  322. );
  323. final ButtonStyle styleLocked = TextButton.styleFrom(
  324. foregroundColor: controller?.value.exposureMode == ExposureMode.locked
  325. ? Colors.orange
  326. : Colors.blue,
  327. );
  328. return SizeTransition(
  329. sizeFactor: _exposureModeControlRowAnimation,
  330. child: ClipRect(
  331. child: ColoredBox(
  332. color: Colors.grey.shade50,
  333. child: Column(
  334. children: <Widget>[
  335. const Center(
  336. child: Text('Exposure Mode'),
  337. ),
  338. Row(
  339. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  340. children: <Widget>[
  341. TextButton(
  342. style: styleAuto,
  343. onPressed: controller != null
  344. ? () =>
  345. onSetExposureModeButtonPressed(ExposureMode.auto)
  346. : null,
  347. onLongPress: () {
  348. if (controller != null) {
  349. CameraPlatform.instance
  350. .setExposurePoint(controller!.cameraId, null);
  351. showInSnackBar('Resetting exposure point');
  352. }
  353. },
  354. child: const Text('AUTO'),
  355. ),
  356. TextButton(
  357. style: styleLocked,
  358. onPressed: controller != null
  359. ? () =>
  360. onSetExposureModeButtonPressed(ExposureMode.locked)
  361. : null,
  362. child: const Text('LOCKED'),
  363. ),
  364. TextButton(
  365. style: styleLocked,
  366. onPressed: controller != null
  367. ? () => controller!.setExposureOffset(0.0)
  368. : null,
  369. child: const Text('RESET OFFSET'),
  370. ),
  371. ],
  372. ),
  373. const Center(
  374. child: Text('Exposure Offset'),
  375. ),
  376. Row(
  377. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  378. children: <Widget>[
  379. Text(_minAvailableExposureOffset.toString()),
  380. Slider(
  381. value: _currentExposureOffset,
  382. min: _minAvailableExposureOffset,
  383. max: _maxAvailableExposureOffset,
  384. label: _currentExposureOffset.toString(),
  385. onChanged: _minAvailableExposureOffset ==
  386. _maxAvailableExposureOffset
  387. ? null
  388. : setExposureOffset,
  389. ),
  390. Text(_maxAvailableExposureOffset.toString()),
  391. ],
  392. ),
  393. ],
  394. ),
  395. ),
  396. ),
  397. );
  398. }
  399. Widget _focusModeControlRowWidget() {
  400. final ButtonStyle styleAuto = TextButton.styleFrom(
  401. foregroundColor: controller?.value.focusMode == FocusMode.auto
  402. ? Colors.orange
  403. : Colors.blue,
  404. );
  405. final ButtonStyle styleLocked = TextButton.styleFrom(
  406. foregroundColor: controller?.value.focusMode == FocusMode.locked
  407. ? Colors.orange
  408. : Colors.blue,
  409. );
  410. return SizeTransition(
  411. sizeFactor: _focusModeControlRowAnimation,
  412. child: ClipRect(
  413. child: ColoredBox(
  414. color: Colors.grey.shade50,
  415. child: Column(
  416. children: <Widget>[
  417. const Center(
  418. child: Text('Focus Mode'),
  419. ),
  420. Row(
  421. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  422. children: <Widget>[
  423. TextButton(
  424. style: styleAuto,
  425. onPressed: controller != null
  426. ? () => onSetFocusModeButtonPressed(FocusMode.auto)
  427. : null,
  428. onLongPress: () {
  429. if (controller != null) {
  430. CameraPlatform.instance
  431. .setFocusPoint(controller!.cameraId, null);
  432. }
  433. showInSnackBar('Resetting focus point');
  434. },
  435. child: const Text('AUTO'),
  436. ),
  437. TextButton(
  438. style: styleLocked,
  439. onPressed: controller != null
  440. ? () => onSetFocusModeButtonPressed(FocusMode.locked)
  441. : null,
  442. child: const Text('LOCKED'),
  443. ),
  444. ],
  445. ),
  446. ],
  447. ),
  448. ),
  449. ),
  450. );
  451. }
  452. /// Display the control bar with buttons to take pictures and record videos.
  453. Widget _captureControlRowWidget() {
  454. final CameraController? cameraController = controller;
  455. return Row(
  456. mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  457. children: <Widget>[
  458. IconButton(
  459. icon: const Icon(Icons.camera_alt),
  460. color: Colors.blue,
  461. onPressed: cameraController != null &&
  462. cameraController.value.isInitialized &&
  463. !cameraController.value.isRecordingVideo
  464. ? onTakePictureButtonPressed
  465. : null,
  466. ),
  467. IconButton(
  468. icon: const Icon(Icons.videocam),
  469. color: Colors.blue,
  470. onPressed:
  471. cameraController == null ? null : onVideoRecordButtonPressed,
  472. ),
  473. IconButton(
  474. icon: cameraController != null &&
  475. cameraController.value.isRecordingPaused
  476. ? const Icon(Icons.play_arrow)
  477. : const Icon(Icons.pause),
  478. color: Colors.blue,
  479. onPressed: () {
  480. if (cameraController == null) {
  481. return;
  482. } else if (cameraController.value.isRecordingPaused) {
  483. return onResumeButtonPressed();
  484. } else {
  485. return onPauseButtonPressed();
  486. }
  487. },
  488. ),
  489. IconButton(
  490. icon: const Icon(Icons.stop),
  491. color: Colors.red,
  492. onPressed: cameraController == null ? null : onStopButtonPressed,
  493. ),
  494. IconButton(
  495. icon: const Icon(Icons.pause_presentation),
  496. color:
  497. cameraController != null && cameraController.value.isPreviewPaused
  498. ? Colors.red
  499. : Colors.blue,
  500. onPressed:
  501. cameraController == null ? null : onPausePreviewButtonPressed,
  502. ),
  503. ],
  504. );
  505. }
  506. /// Display a row of toggle to select the camera (or a message if no camera is available).
  507. Widget _cameraTogglesRowWidget() {
  508. final List<Widget> toggles = <Widget>[];
  509. void onChanged(CameraDescription? description) {
  510. if (description == null) {
  511. return;
  512. }
  513. onNewCameraSelected(description);
  514. }
  515. if (_cameras.isEmpty) {
  516. SchedulerBinding.instance.addPostFrameCallback((_) async {
  517. showInSnackBar('No camera found.');
  518. });
  519. return const Text('None');
  520. } else {
  521. for (final CameraDescription cameraDescription in _cameras) {
  522. toggles.add(
  523. SizedBox(
  524. width: 90.0,
  525. child: RadioListTile<CameraDescription>(
  526. title: Icon(getCameraLensIcon(cameraDescription.lensDirection)),
  527. groupValue: controller?.description,
  528. value: cameraDescription,
  529. onChanged:
  530. controller != null && controller!.value.isRecordingVideo
  531. ? null
  532. : onChanged,
  533. ),
  534. ),
  535. );
  536. }
  537. }
  538. return Row(children: toggles);
  539. }
  540. String timestamp() => DateTime.now().millisecondsSinceEpoch.toString();
  541. void showInSnackBar(String message) {
  542. ScaffoldMessenger.of(context)
  543. .showSnackBar(SnackBar(content: Text(message)));
  544. }
  545. void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) {
  546. if (controller == null) {
  547. return;
  548. }
  549. final CameraController cameraController = controller!;
  550. final Offset offset = Offset(
  551. details.localPosition.dx / constraints.maxWidth,
  552. details.localPosition.dy / constraints.maxHeight,
  553. );
  554. cameraController.setExposurePoint(offset);
  555. cameraController.setFocusPoint(offset);
  556. }
  557. Future<void> onNewCameraSelected(CameraDescription cameraDescription) async {
  558. final CameraController? oldController = controller;
  559. if (oldController != null) {
  560. // `controller` needs to be set to null before getting disposed,
  561. // to avoid a race condition when we use the controller that is being
  562. // disposed. This happens when camera permission dialog shows up,
  563. // which triggers `didChangeAppLifecycleState`, which disposes and
  564. // re-creates the controller.
  565. controller = null;
  566. await oldController.dispose();
  567. }
  568. final CameraController cameraController = CameraController(
  569. cameraDescription,
  570. kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium,
  571. enableAudio: enableAudio,
  572. imageFormatGroup: ImageFormatGroup.jpeg,
  573. );
  574. controller = cameraController;
  575. // If the controller is updated then update the UI.
  576. cameraController.addListener(() {
  577. if (mounted) {
  578. setState(() {});
  579. }
  580. if (cameraController.value.hasError) {
  581. showInSnackBar(
  582. 'Camera error ${cameraController.value.errorDescription}');
  583. }
  584. });
  585. try {
  586. await cameraController.initialize();
  587. await Future.wait(<Future<Object?>>[
  588. // The exposure mode is currently not supported on the web.
  589. ...!kIsWeb
  590. ? <Future<Object?>>[
  591. cameraController.getMinExposureOffset().then(
  592. (double value) => _minAvailableExposureOffset = value),
  593. cameraController
  594. .getMaxExposureOffset()
  595. .then((double value) => _maxAvailableExposureOffset = value)
  596. ]
  597. : <Future<Object?>>[],
  598. cameraController
  599. .getMaxZoomLevel()
  600. .then((double value) => _maxAvailableZoom = value),
  601. cameraController
  602. .getMinZoomLevel()
  603. .then((double value) => _minAvailableZoom = value),
  604. ]);
  605. } on CameraException catch (e) {
  606. // switch (e.code) {
  607. // case 'CameraAccessDenied':
  608. // showInSnackBar('You have denied camera access.');
  609. // case 'CameraAccessDeniedWithoutPrompt':
  610. // // iOS only
  611. // showInSnackBar('Please go to Settings app to enable camera access.');
  612. // case 'CameraAccessRestricted':
  613. // // iOS only
  614. // showInSnackBar('Camera access is restricted.');
  615. // case 'AudioAccessDenied':
  616. // showInSnackBar('You have denied audio access.');
  617. // case 'AudioAccessDeniedWithoutPrompt':
  618. // // iOS only
  619. // showInSnackBar('Please go to Settings app to enable audio access.');
  620. // case 'AudioAccessRestricted':
  621. // // iOS only
  622. // showInSnackBar('Audio access is restricted.');
  623. // default:
  624. // _showCameraException(e);
  625. // break;
  626. // }
  627. }
  628. if (mounted) {
  629. setState(() {});
  630. }
  631. }
  632. void onTakePictureButtonPressed() {
  633. takePicture().then((XFile? file) {
  634. if (mounted) {
  635. setState(() {
  636. imageFile = file;
  637. // videoController?.dispose();
  638. // videoController = null;
  639. });
  640. if (file != null) {
  641. showInSnackBar('Picture saved to ${file.path}');
  642. }
  643. }
  644. });
  645. }
  646. void onFlashModeButtonPressed() {
  647. if (_flashModeControlRowAnimationController.value == 1) {
  648. _flashModeControlRowAnimationController.reverse();
  649. } else {
  650. _flashModeControlRowAnimationController.forward();
  651. _exposureModeControlRowAnimationController.reverse();
  652. _focusModeControlRowAnimationController.reverse();
  653. }
  654. }
  655. void onExposureModeButtonPressed() {
  656. if (_exposureModeControlRowAnimationController.value == 1) {
  657. _exposureModeControlRowAnimationController.reverse();
  658. } else {
  659. _exposureModeControlRowAnimationController.forward();
  660. _flashModeControlRowAnimationController.reverse();
  661. _focusModeControlRowAnimationController.reverse();
  662. }
  663. }
  664. void onFocusModeButtonPressed() {
  665. if (_focusModeControlRowAnimationController.value == 1) {
  666. _focusModeControlRowAnimationController.reverse();
  667. } else {
  668. _focusModeControlRowAnimationController.forward();
  669. _flashModeControlRowAnimationController.reverse();
  670. _exposureModeControlRowAnimationController.reverse();
  671. }
  672. }
  673. void onAudioModeButtonPressed() {
  674. enableAudio = !enableAudio;
  675. if (controller != null) {
  676. onNewCameraSelected(controller!.description);
  677. }
  678. }
  679. Future<void> onCaptureOrientationLockButtonPressed() async {
  680. try {
  681. if (controller != null) {
  682. final CameraController cameraController = controller!;
  683. if (cameraController.value.isCaptureOrientationLocked) {
  684. await cameraController.unlockCaptureOrientation();
  685. showInSnackBar('Capture orientation unlocked');
  686. } else {
  687. await cameraController.lockCaptureOrientation();
  688. showInSnackBar(
  689. 'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}');
  690. }
  691. }
  692. } on CameraException catch (e) {
  693. _showCameraException(e);
  694. }
  695. }
  696. void onSetFlashModeButtonPressed(FlashMode mode) {
  697. setFlashMode(mode).then((_) {
  698. if (mounted) {
  699. setState(() {});
  700. }
  701. showInSnackBar('Flash mode set to ${mode.toString().split('.').last}');
  702. });
  703. }
  704. void onSetExposureModeButtonPressed(ExposureMode mode) {
  705. setExposureMode(mode).then((_) {
  706. if (mounted) {
  707. setState(() {});
  708. }
  709. showInSnackBar('Exposure mode set to ${mode.toString().split('.').last}');
  710. });
  711. }
  712. void onSetFocusModeButtonPressed(FocusMode mode) {
  713. setFocusMode(mode).then((_) {
  714. if (mounted) {
  715. setState(() {});
  716. }
  717. showInSnackBar('Focus mode set to ${mode.toString().split('.').last}');
  718. });
  719. }
  720. void onVideoRecordButtonPressed() {
  721. startVideoRecording().then((_) {
  722. if (mounted) {
  723. setState(() {});
  724. }
  725. });
  726. }
  727. void onStopButtonPressed() {
  728. stopVideoRecording().then((XFile? file) {
  729. if (mounted) {
  730. setState(() {});
  731. }
  732. if (file != null) {
  733. showInSnackBar('Video recorded to ${file.path}');
  734. videoFile = file;
  735. // _startVideoPlayer();
  736. }
  737. });
  738. }
  739. Future<void> onPausePreviewButtonPressed() async {
  740. final CameraController? cameraController = controller;
  741. if (cameraController == null || !cameraController.value.isInitialized) {
  742. showInSnackBar('Error: select a camera first.');
  743. return;
  744. }
  745. if (cameraController.value.isPreviewPaused) {
  746. await cameraController.resumePreview();
  747. } else {
  748. await cameraController.pausePreview();
  749. }
  750. if (mounted) {
  751. setState(() {});
  752. }
  753. }
  754. void onPauseButtonPressed() {
  755. pauseVideoRecording().then((_) {
  756. if (mounted) {
  757. setState(() {});
  758. }
  759. showInSnackBar('Video recording paused');
  760. });
  761. }
  762. void onResumeButtonPressed() {
  763. resumeVideoRecording().then((_) {
  764. if (mounted) {
  765. setState(() {});
  766. }
  767. showInSnackBar('Video recording resumed');
  768. });
  769. }
  770. Future<void> startVideoRecording() async {
  771. final CameraController? cameraController = controller;
  772. if (cameraController == null || !cameraController.value.isInitialized) {
  773. showInSnackBar('Error: select a camera first.');
  774. return;
  775. }
  776. if (cameraController.value.isRecordingVideo) {
  777. // A recording is already started, do nothing.
  778. return;
  779. }
  780. try {
  781. await cameraController.startVideoRecording();
  782. } on CameraException catch (e) {
  783. _showCameraException(e);
  784. return;
  785. }
  786. }
  787. Future<XFile?> stopVideoRecording() async {
  788. final CameraController? cameraController = controller;
  789. if (cameraController == null || !cameraController.value.isRecordingVideo) {
  790. return null;
  791. }
  792. try {
  793. return cameraController.stopVideoRecording();
  794. } on CameraException catch (e) {
  795. _showCameraException(e);
  796. return null;
  797. }
  798. }
  799. Future<void> pauseVideoRecording() async {
  800. final CameraController? cameraController = controller;
  801. if (cameraController == null || !cameraController.value.isRecordingVideo) {
  802. return;
  803. }
  804. try {
  805. await cameraController.pauseVideoRecording();
  806. } on CameraException catch (e) {
  807. _showCameraException(e);
  808. rethrow;
  809. }
  810. }
  811. Future<void> resumeVideoRecording() async {
  812. final CameraController? cameraController = controller;
  813. if (cameraController == null || !cameraController.value.isRecordingVideo) {
  814. return;
  815. }
  816. try {
  817. await cameraController.resumeVideoRecording();
  818. } on CameraException catch (e) {
  819. _showCameraException(e);
  820. rethrow;
  821. }
  822. }
  823. Future<void> setFlashMode(FlashMode mode) async {
  824. if (controller == null) {
  825. return;
  826. }
  827. try {
  828. await controller!.setFlashMode(mode);
  829. } on CameraException catch (e) {
  830. _showCameraException(e);
  831. rethrow;
  832. }
  833. }
  834. Future<void> setExposureMode(ExposureMode mode) async {
  835. if (controller == null) {
  836. return;
  837. }
  838. try {
  839. await controller!.setExposureMode(mode);
  840. } on CameraException catch (e) {
  841. _showCameraException(e);
  842. rethrow;
  843. }
  844. }
  845. Future<void> setExposureOffset(double offset) async {
  846. if (controller == null) {
  847. return;
  848. }
  849. setState(() {
  850. _currentExposureOffset = offset;
  851. });
  852. try {
  853. offset = await controller!.setExposureOffset(offset);
  854. } on CameraException catch (e) {
  855. _showCameraException(e);
  856. rethrow;
  857. }
  858. }
  859. Future<void> setFocusMode(FocusMode mode) async {
  860. if (controller == null) {
  861. return;
  862. }
  863. try {
  864. await controller!.setFocusMode(mode);
  865. } on CameraException catch (e) {
  866. _showCameraException(e);
  867. rethrow;
  868. }
  869. }
  870. // Future<void> _startVideoPlayer() async {
  871. // if (videoFile == null) {
  872. // return;
  873. // }
  874. // final VideoPlayerController vController = kIsWeb
  875. // ? VideoPlayerController.networkUrl(Uri.parse(videoFile!.path))
  876. // : VideoPlayerController.file(File(videoFile!.path));
  877. // videoPlayerListener = () {
  878. // if (videoController != null) {
  879. // // Refreshing the state to update video player with the correct ratio.
  880. // if (mounted) {
  881. // setState(() {});
  882. // }
  883. // videoController!.removeListener(videoPlayerListener!);
  884. // }
  885. // };
  886. // vController.addListener(videoPlayerListener!);
  887. // await vController.setLooping(true);
  888. // await vController.initialize();
  889. // await videoController?.dispose();
  890. // if (mounted) {
  891. // setState(() {
  892. // imageFile = null;
  893. // videoController = vController;
  894. // });
  895. // }
  896. // await vController.play();
  897. // }
  898. Future<XFile?> takePicture() async {
  899. final CameraController? cameraController = controller;
  900. if (cameraController == null || !cameraController.value.isInitialized) {
  901. showInSnackBar('Error: select a camera first.');
  902. return null;
  903. }
  904. if (cameraController.value.isTakingPicture) {
  905. // A capture is already pending, do nothing.
  906. return null;
  907. }
  908. try {
  909. final XFile file = await cameraController.takePicture();
  910. return file;
  911. } on CameraException catch (e) {
  912. _showCameraException(e);
  913. return null;
  914. }
  915. }
  916. void _showCameraException(CameraException e) {
  917. _logError(e.code, e.description);
  918. showInSnackBar('Error: ${e.code}\n${e.description}');
  919. }
  920. }