From 597d12115ad72d2adc2de145417c6465089caedf Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Fri, 26 Feb 2021 16:20:55 +0100 Subject: [PATCH 1/8] Stable release for null safety. --- packages/camera/camera/CHANGELOG.md | 15 +- .../example/integration_test/camera_test.dart | 6 +- packages/camera/camera/example/lib/main.dart | 303 +++++++++++------- packages/camera/camera/example/pubspec.yaml | 10 +- packages/camera/camera/pubspec.yaml | 19 +- 5 files changed, 202 insertions(+), 151 deletions(-) diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 079aa1685bd5..f1d771496dde 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,18 +1,9 @@ -## 0.8.0-nullsafety.3 - -* Updates the example code listed in the [README.md](README.md), so it runs without errors when you simply copy/ paste it into a Flutter App. - -## 0.8.0-nullsafety.2 +## 0.8.0 +* Stable null safety release. * Solved delay when using the zoom feature on iOS. - -## 0.8.0-nullsafety.1 - * Added a timeout to the pre-capture sequence on Android to prevent crashes when the camera cannot get a focus. - -## 0.8.0-nullsafety - -* Migrated to null safety. +* Updates the example code listed in the [README.md](README.md), so it runs without errors when you simply copy/ paste it into a Flutter App. ## 0.7.0+4 diff --git a/packages/camera/camera/example/integration_test/camera_test.dart b/packages/camera/camera/example/integration_test/camera_test.dart index c2e73e0f1563..2d29ecf093d2 100644 --- a/packages/camera/camera/example/integration_test/camera_test.dart +++ b/packages/camera/camera/example/integration_test/camera_test.dart @@ -10,7 +10,7 @@ import 'package:video_player/video_player.dart'; import 'package:integration_test/integration_test.dart'; void main() { - Directory testDir; + late Directory testDir; IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -50,7 +50,7 @@ void main() { // whether the image is exactly the desired resolution. Future testCaptureImageResolution( CameraController controller, ResolutionPreset preset) async { - final Size expectedSize = presetExpectedSizes[preset]; + final Size expectedSize = presetExpectedSizes[preset]!; print( 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); @@ -95,7 +95,7 @@ void main() { // whether the image is exactly the desired resolution. Future testCaptureVideoResolution( CameraController controller, ResolutionPreset preset) async { - final Size expectedSize = presetExpectedSizes[preset]; + final Size expectedSize = presetExpectedSizes[preset]!; print( 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index 6244aa5a8e37..b6b74c9f5972 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -27,32 +27,33 @@ IconData getCameraLensIcon(CameraLensDirection direction) { return Icons.camera_front; case CameraLensDirection.external: return Icons.camera; + default: + throw ArgumentError('Unknown lens direction'); } - throw ArgumentError('Unknown lens direction'); } -void logError(String code, String message) => +void logError(String code, String? message) => print('Error: $code\nError Message: $message'); class _CameraExampleHomeState extends State with WidgetsBindingObserver, TickerProviderStateMixin { - CameraController controller; - XFile imageFile; - XFile videoFile; - VideoPlayerController videoController; - VoidCallback videoPlayerListener; + CameraController? controller; + XFile? imageFile; + XFile? videoFile; + VideoPlayerController? videoController; + VoidCallback? videoPlayerListener; bool enableAudio = true; double _minAvailableExposureOffset = 0.0; double _maxAvailableExposureOffset = 0.0; double _currentExposureOffset = 0.0; - AnimationController _flashModeControlRowAnimationController; - Animation _flashModeControlRowAnimation; - AnimationController _exposureModeControlRowAnimationController; - Animation _exposureModeControlRowAnimation; - AnimationController _focusModeControlRowAnimationController; - Animation _focusModeControlRowAnimation; - double _minAvailableZoom; - double _maxAvailableZoom; + AnimationController? _flashModeControlRowAnimationController; + Animation? _flashModeControlRowAnimation; + AnimationController? _exposureModeControlRowAnimationController; + Animation? _exposureModeControlRowAnimation; + AnimationController? _focusModeControlRowAnimationController; + Animation? _focusModeControlRowAnimation; + double? _minAvailableZoom; + double? _maxAvailableZoom; double _currentScale = 1.0; double _baseScale = 1.0; @@ -62,13 +63,14 @@ class _CameraExampleHomeState extends State @override void initState() { super.initState(); - WidgetsBinding.instance.addObserver(this); + WidgetsBinding.instance?.addObserver(this); + _flashModeControlRowAnimationController = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); _flashModeControlRowAnimation = CurvedAnimation( - parent: _flashModeControlRowAnimationController, + parent: _flashModeControlRowAnimationController!, curve: Curves.easeInCubic, ); _exposureModeControlRowAnimationController = AnimationController( @@ -76,7 +78,7 @@ class _CameraExampleHomeState extends State vsync: this, ); _exposureModeControlRowAnimation = CurvedAnimation( - parent: _exposureModeControlRowAnimationController, + parent: _exposureModeControlRowAnimationController!, curve: Curves.easeInCubic, ); _focusModeControlRowAnimationController = AnimationController( @@ -84,31 +86,30 @@ class _CameraExampleHomeState extends State vsync: this, ); _focusModeControlRowAnimation = CurvedAnimation( - parent: _focusModeControlRowAnimationController, + parent: _focusModeControlRowAnimationController!, curve: Curves.easeInCubic, ); } @override void dispose() { - WidgetsBinding.instance.removeObserver(this); - _flashModeControlRowAnimationController.dispose(); - _exposureModeControlRowAnimationController.dispose(); + WidgetsBinding.instance?.removeObserver(this); + _flashModeControlRowAnimationController?.dispose(); + _exposureModeControlRowAnimationController?.dispose(); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { // App state changed before we got the chance to initialize. - if (controller == null || !controller.value.isInitialized) { + if (controller == null || !controller!.value.isInitialized) { return; } + if (state == AppLifecycleState.inactive) { controller?.dispose(); } else if (state == AppLifecycleState.resumed) { - if (controller != null) { - onNewCameraSelected(controller.description); - } + onNewCameraSelected(controller!.description); } } @@ -134,9 +135,10 @@ class _CameraExampleHomeState extends State decoration: BoxDecoration( color: Colors.black, border: Border.all( - color: controller != null && controller.value.isRecordingVideo - ? Colors.redAccent - : Colors.grey, + color: + controller != null && controller!.value.isRecordingVideo + ? Colors.redAccent + : Colors.grey, width: 3.0, ), ), @@ -161,7 +163,7 @@ class _CameraExampleHomeState extends State /// Display the preview from the camera (or a message if the preview is not available). Widget _cameraPreviewWidget() { - if (controller == null || !controller.value.isInitialized) { + if (controller == null || !controller!.value.isInitialized) { return const Text( 'Tap a camera', style: TextStyle( @@ -175,7 +177,7 @@ class _CameraExampleHomeState extends State onPointerDown: (_) => _pointers++, onPointerUp: (_) => _pointers--, child: CameraPreview( - controller, + controller!, child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return GestureDetector( @@ -196,14 +198,14 @@ class _CameraExampleHomeState extends State Future _handleScaleUpdate(ScaleUpdateDetails details) async { // When there are not exactly two fingers on screen don't scale - if (_pointers != 2) { + if (controller == null || _pointers != 2) { return; } _currentScale = (_baseScale * details.scale) - .clamp(_minAvailableZoom, _maxAvailableZoom); + .clamp(_minAvailableZoom ?? 1, _maxAvailableZoom ?? 1); - await controller.setZoomLevel(_currentScale); + await controller!.setZoomLevel(_currentScale); } /// Display the thumbnail of the captured image or video. @@ -218,15 +220,15 @@ class _CameraExampleHomeState extends State ? Container() : SizedBox( child: (videoController == null) - ? Image.file(File(imageFile.path)) + ? Image.file(File(imageFile!.path)) : Container( child: Center( child: AspectRatio( aspectRatio: - videoController.value.size != null - ? videoController.value.aspectRatio + videoController!.value.size != null + ? videoController!.value.aspectRatio : 1.0, - child: VideoPlayer(videoController)), + child: VideoPlayer(videoController!)), ), decoration: BoxDecoration( border: Border.all(color: Colors.pink)), @@ -270,7 +272,7 @@ class _CameraExampleHomeState extends State onPressed: controller != null ? onAudioModeButtonPressed : null, ), IconButton( - icon: Icon(controller?.value?.isCaptureOrientationLocked ?? false + icon: Icon(controller?.value.isCaptureOrientationLocked ?? false ? Icons.screen_lock_rotation : Icons.screen_rotation), color: Colors.blue, @@ -288,8 +290,8 @@ class _CameraExampleHomeState extends State } Widget _flashModeControlRowWidget() { - return SizeTransition( - sizeFactor: _flashModeControlRowAnimation, + return _wrapInSizeTransition( + animation: _flashModeControlRowAnimation!, child: ClipRect( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -297,7 +299,7 @@ class _CameraExampleHomeState extends State children: [ IconButton( icon: Icon(Icons.flash_off), - color: controller?.value?.flashMode == FlashMode.off + color: controller?.value.flashMode == FlashMode.off ? Colors.orange : Colors.blue, onPressed: controller != null @@ -306,7 +308,7 @@ class _CameraExampleHomeState extends State ), IconButton( icon: Icon(Icons.flash_auto), - color: controller?.value?.flashMode == FlashMode.auto + color: controller?.value.flashMode == FlashMode.auto ? Colors.orange : Colors.blue, onPressed: controller != null @@ -315,7 +317,7 @@ class _CameraExampleHomeState extends State ), IconButton( icon: Icon(Icons.flash_on), - color: controller?.value?.flashMode == FlashMode.always + color: controller?.value.flashMode == FlashMode.always ? Colors.orange : Colors.blue, onPressed: controller != null @@ -324,7 +326,7 @@ class _CameraExampleHomeState extends State ), IconButton( icon: Icon(Icons.highlight), - color: controller?.value?.flashMode == FlashMode.torch + color: controller?.value.flashMode == FlashMode.torch ? Colors.orange : Colors.blue, onPressed: controller != null @@ -339,18 +341,18 @@ class _CameraExampleHomeState extends State Widget _exposureModeControlRowWidget() { final ButtonStyle styleAuto = TextButton.styleFrom( - primary: controller?.value?.exposureMode == ExposureMode.auto + primary: controller?.value.exposureMode == ExposureMode.auto ? Colors.orange : Colors.blue, ); final ButtonStyle styleLocked = TextButton.styleFrom( - primary: controller?.value?.exposureMode == ExposureMode.locked + primary: controller?.value.exposureMode == ExposureMode.locked ? Colors.orange : Colors.blue, ); - return SizeTransition( - sizeFactor: _exposureModeControlRowAnimation, + return _wrapInSizeTransition( + animation: _exposureModeControlRowAnimation!, child: ClipRect( child: Container( color: Colors.grey.shade50, @@ -371,8 +373,10 @@ class _CameraExampleHomeState extends State onSetExposureModeButtonPressed(ExposureMode.auto) : null, onLongPress: () { - if (controller != null) controller.setExposurePoint(null); - showInSnackBar('Resetting exposure point'); + if (controller != null) { + controller!.setExposurePoint(null); + showInSnackBar('Resetting exposure point'); + } }, ), TextButton( @@ -415,18 +419,18 @@ class _CameraExampleHomeState extends State Widget _focusModeControlRowWidget() { final ButtonStyle styleAuto = TextButton.styleFrom( - primary: controller?.value?.focusMode == FocusMode.auto + primary: controller?.value.focusMode == FocusMode.auto ? Colors.orange : Colors.blue, ); final ButtonStyle styleLocked = TextButton.styleFrom( - primary: controller?.value?.focusMode == FocusMode.locked + primary: controller?.value.focusMode == FocusMode.locked ? Colors.orange : Colors.blue, ); - return SizeTransition( - sizeFactor: _focusModeControlRowAnimation, + return _wrapInSizeTransition( + animation: _focusModeControlRowAnimation, child: ClipRect( child: Container( color: Colors.grey.shade50, @@ -446,7 +450,7 @@ class _CameraExampleHomeState extends State ? () => onSetFocusModeButtonPressed(FocusMode.auto) : null, onLongPress: () { - if (controller != null) controller.setFocusPoint(null); + if (controller != null) controller!.setFocusPoint(null); showInSnackBar('Resetting focus point'); }, ), @@ -466,6 +470,20 @@ class _CameraExampleHomeState extends State ); } + Widget _wrapInSizeTransition({ + required Widget child, + Animation? animation, + }) { + if (animation == null) { + return child; + } + + return SizeTransition( + sizeFactor: animation, + child: child, + ); + } + /// Display the control bar with buttons to take pictures and record videos. Widget _captureControlRowWidget() { return Row( @@ -476,8 +494,8 @@ class _CameraExampleHomeState extends State icon: const Icon(Icons.camera_alt), color: Colors.blue, onPressed: controller != null && - controller.value.isInitialized && - !controller.value.isRecordingVideo + controller!.value.isInitialized && + !controller!.value.isRecordingVideo ? onTakePictureButtonPressed : null, ), @@ -485,20 +503,20 @@ class _CameraExampleHomeState extends State icon: const Icon(Icons.videocam), color: Colors.blue, onPressed: controller != null && - controller.value.isInitialized && - !controller.value.isRecordingVideo + controller!.value.isInitialized && + !controller!.value.isRecordingVideo ? onVideoRecordButtonPressed : null, ), IconButton( - icon: controller != null && controller.value.isRecordingPaused + 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 + controller!.value.isInitialized && + controller!.value.isRecordingVideo + ? (controller != null && controller!.value.isRecordingPaused ? onResumeButtonPressed : onPauseButtonPressed) : null, @@ -507,8 +525,8 @@ class _CameraExampleHomeState extends State icon: const Icon(Icons.stop), color: Colors.red, onPressed: controller != null && - controller.value.isInitialized && - controller.value.isRecordingVideo + controller!.value.isInitialized && + controller!.value.isRecordingVideo ? onStopButtonPressed : null, ) @@ -520,6 +538,14 @@ class _CameraExampleHomeState extends State Widget _cameraTogglesRowWidget() { final List toggles = []; + final onChanged = (CameraDescription? description) { + if (description == null) { + return; + } + + onNewCameraSelected(description); + }; + if (cameras.isEmpty) { return const Text('No camera found'); } else { @@ -531,9 +557,10 @@ class _CameraExampleHomeState extends State title: Icon(getCameraLensIcon(cameraDescription.lensDirection)), groupValue: controller?.description, value: cameraDescription, - onChanged: controller != null && controller.value.isRecordingVideo - ? null - : onNewCameraSelected, + onChanged: + controller != null && controller!.value.isRecordingVideo + ? null + : onChanged, ), ), ); @@ -547,21 +574,25 @@ class _CameraExampleHomeState extends State void showInSnackBar(String message) { // ignore: deprecated_member_use - _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(message))); + _scaffoldKey.currentState?.showSnackBar(SnackBar(content: Text(message))); } void onViewFinderTap(TapDownDetails details, BoxConstraints constraints) { + if (controller == null) { + return; + } + final offset = Offset( details.localPosition.dx / constraints.maxWidth, details.localPosition.dy / constraints.maxHeight, ); - controller.setExposurePoint(offset); - controller.setFocusPoint(offset); + controller!.setExposurePoint(offset); + controller!.setFocusPoint(offset); } void onNewCameraSelected(CameraDescription cameraDescription) async { if (controller != null) { - await controller.dispose(); + await controller!.dispose(); } controller = CameraController( cameraDescription, @@ -571,24 +602,28 @@ class _CameraExampleHomeState extends State ); // If the controller is updated then update the UI. - controller.addListener(() { + controller!.addListener(() { if (mounted) setState(() {}); - if (controller.value.hasError) { - showInSnackBar('Camera error ${controller.value.errorDescription}'); + if (controller!.value.hasError) { + showInSnackBar('Camera error ${controller!.value.errorDescription}'); } }); try { - await controller.initialize(); + await controller!.initialize(); await Future.wait([ - controller + controller! .getMinExposureOffset() .then((value) => _minAvailableExposureOffset = value), - controller + controller! .getMaxExposureOffset() .then((value) => _maxAvailableExposureOffset = value), - controller.getMaxZoomLevel().then((value) => _maxAvailableZoom = value), - controller.getMinZoomLevel().then((value) => _minAvailableZoom = value), + controller! + .getMaxZoomLevel() + .then((value) => _maxAvailableZoom = value), + controller! + .getMinZoomLevel() + .then((value) => _minAvailableZoom = value), ]); } on CameraException catch (e) { _showCameraException(e); @@ -600,7 +635,7 @@ class _CameraExampleHomeState extends State } void onTakePictureButtonPressed() { - takePicture().then((XFile file) { + takePicture().then((XFile? file) { if (mounted) { setState(() { imageFile = file; @@ -613,51 +648,51 @@ class _CameraExampleHomeState extends State } void onFlashModeButtonPressed() { - if (_flashModeControlRowAnimationController.value == 1) { - _flashModeControlRowAnimationController.reverse(); + if (_flashModeControlRowAnimationController?.value == 1) { + _flashModeControlRowAnimationController?.reverse(); } else { - _flashModeControlRowAnimationController.forward(); - _exposureModeControlRowAnimationController.reverse(); - _focusModeControlRowAnimationController.reverse(); + _flashModeControlRowAnimationController?.forward(); + _exposureModeControlRowAnimationController?.reverse(); + _focusModeControlRowAnimationController?.reverse(); } } void onExposureModeButtonPressed() { - if (_exposureModeControlRowAnimationController.value == 1) { - _exposureModeControlRowAnimationController.reverse(); + if (_exposureModeControlRowAnimationController?.value == 1) { + _exposureModeControlRowAnimationController?.reverse(); } else { - _exposureModeControlRowAnimationController.forward(); - _flashModeControlRowAnimationController.reverse(); - _focusModeControlRowAnimationController.reverse(); + _exposureModeControlRowAnimationController?.forward(); + _flashModeControlRowAnimationController?.reverse(); + _focusModeControlRowAnimationController?.reverse(); } } void onFocusModeButtonPressed() { - if (_focusModeControlRowAnimationController.value == 1) { - _focusModeControlRowAnimationController.reverse(); + if (_focusModeControlRowAnimationController?.value == 1) { + _focusModeControlRowAnimationController?.reverse(); } else { - _focusModeControlRowAnimationController.forward(); - _flashModeControlRowAnimationController.reverse(); - _exposureModeControlRowAnimationController.reverse(); + _focusModeControlRowAnimationController?.forward(); + _flashModeControlRowAnimationController?.reverse(); + _exposureModeControlRowAnimationController?.reverse(); } } void onAudioModeButtonPressed() { enableAudio = !enableAudio; if (controller != null) { - onNewCameraSelected(controller.description); + onNewCameraSelected(controller!.description); } } void onCaptureOrientationLockButtonPressed() async { if (controller != null) { - if (controller.value.isCaptureOrientationLocked) { - await controller.unlockCaptureOrientation(); + if (controller!.value.isCaptureOrientationLocked) { + await controller!.unlockCaptureOrientation(); showInSnackBar('Capture orientation unlocked'); } else { - await controller.lockCaptureOrientation(); + await controller!.lockCaptureOrientation(); showInSnackBar( - 'Capture orientation locked to ${controller.value.lockedCaptureOrientation.toString().split('.').last}'); + 'Capture orientation locked to ${controller!.value.lockedCaptureOrientation.toString().split('.').last}'); } } } @@ -715,31 +750,31 @@ class _CameraExampleHomeState extends State } Future startVideoRecording() async { - if (!controller.value.isInitialized) { + if (controller == null || !controller!.value.isInitialized) { showInSnackBar('Error: select a camera first.'); return; } - if (controller.value.isRecordingVideo) { + if (controller!.value.isRecordingVideo) { // A recording is already started, do nothing. return; } try { - await controller.startVideoRecording(); + await controller!.startVideoRecording(); } on CameraException catch (e) { _showCameraException(e); return; } } - Future stopVideoRecording() async { - if (!controller.value.isRecordingVideo) { + Future stopVideoRecording() async { + if (controller == null || !controller!.value.isRecordingVideo) { return null; } try { - return controller.stopVideoRecording(); + return controller!.stopVideoRecording(); } on CameraException catch (e) { _showCameraException(e); return null; @@ -747,12 +782,12 @@ class _CameraExampleHomeState extends State } Future pauseVideoRecording() async { - if (!controller.value.isRecordingVideo) { + if (controller == null || !controller!.value.isRecordingVideo) { return null; } try { - await controller.pauseVideoRecording(); + await controller!.pauseVideoRecording(); } on CameraException catch (e) { _showCameraException(e); rethrow; @@ -760,12 +795,12 @@ class _CameraExampleHomeState extends State } Future resumeVideoRecording() async { - if (!controller.value.isRecordingVideo) { + if (controller == null || !controller!.value.isRecordingVideo) { return null; } try { - await controller.resumeVideoRecording(); + await controller!.resumeVideoRecording(); } on CameraException catch (e) { _showCameraException(e); rethrow; @@ -773,8 +808,12 @@ class _CameraExampleHomeState extends State } Future setFlashMode(FlashMode mode) async { + if (controller == null) { + return; + } + try { - await controller.setFlashMode(mode); + await controller!.setFlashMode(mode); } on CameraException catch (e) { _showCameraException(e); rethrow; @@ -782,8 +821,12 @@ class _CameraExampleHomeState extends State } Future setExposureMode(ExposureMode mode) async { + if (controller == null) { + return; + } + try { - await controller.setExposureMode(mode); + await controller!.setExposureMode(mode); } on CameraException catch (e) { _showCameraException(e); rethrow; @@ -791,11 +834,15 @@ class _CameraExampleHomeState extends State } Future setExposureOffset(double offset) async { + if (controller == null) { + return; + } + setState(() { _currentExposureOffset = offset; }); try { - offset = await controller.setExposureOffset(offset); + offset = await controller!.setExposureOffset(offset); } on CameraException catch (e) { _showCameraException(e); rethrow; @@ -803,8 +850,12 @@ class _CameraExampleHomeState extends State } Future setFocusMode(FocusMode mode) async { + if (controller == null) { + return; + } + try { - await controller.setFocusMode(mode); + await controller!.setFocusMode(mode); } on CameraException catch (e) { _showCameraException(e); rethrow; @@ -812,16 +863,20 @@ class _CameraExampleHomeState extends State } Future _startVideoPlayer() async { + if (videoFile == null) { + return; + } + final VideoPlayerController vController = - VideoPlayerController.file(File(videoFile.path)); + VideoPlayerController.file(File(videoFile!.path)); videoPlayerListener = () { - if (videoController != null && videoController.value.size != null) { + if (videoController != null && videoController!.value.size != null) { // Refreshing the state to update video player with the correct ratio. if (mounted) setState(() {}); - videoController.removeListener(videoPlayerListener); + videoController!.removeListener(videoPlayerListener!); } }; - vController.addListener(videoPlayerListener); + vController.addListener(videoPlayerListener!); await vController.setLooping(true); await vController.initialize(); await videoController?.dispose(); @@ -834,19 +889,19 @@ class _CameraExampleHomeState extends State await vController.play(); } - Future takePicture() async { - if (!controller.value.isInitialized) { + Future takePicture() async { + if (controller == null || !controller!.value.isInitialized) { showInSnackBar('Error: select a camera first.'); return null; } - if (controller.value.isTakingPicture) { + if (controller!.value.isTakingPicture) { // A capture is already pending, do nothing. return null; } try { - XFile file = await controller.takePicture(); + XFile file = await controller!.takePicture(); return file; } on CameraException catch (e) { _showCameraException(e); diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index 2a45fd69194c..ecf8418e01b3 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -9,10 +9,10 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - path_provider: ^0.5.0 + path_provider: ^2.0.0 flutter: sdk: flutter - video_player: ^0.10.0 + video_player: ^2.0.0 integration_test: path: ../../../integration_test @@ -21,11 +21,11 @@ dev_dependencies: sdk: flutter flutter_driver: sdk: flutter - pedantic: ^1.8.0 + pedantic: ^1.10.0 flutter: uses-material-design: true environment: - sdk: ">=2.7.0 <3.0.0" - flutter: ">=1.9.1+hotfix.4" + sdk: ">=2.12.0-259.9.beta <3.0.0" + flutter: ">=1.22.0" diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index 2d620505def2..53f9b3b40ad1 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -2,26 +2,31 @@ name: camera description: A Flutter plugin for getting information about and controlling the camera on Android and iOS. Supports previewing the camera feed, capturing images, capturing video, and streaming image buffers to dart. -version: 0.8.0-nullsafety.3 +version: 0.8.0 homepage: https://github.com/flutter/plugins/tree/master/packages/camera/camera dependencies: flutter: sdk: flutter - camera_platform_interface: ^2.0.0-nullsafety + camera_platform_interface: ^2.0.0 pedantic: ^1.10.0 - quiver: ^3.0.0-nullsafety.3 + quiver: ^3.0.0 dev_dependencies: - video_player: ^2.0.0-nullsafety.7 + video_player: ^2.0.0 flutter_test: sdk: flutter flutter_driver: sdk: flutter - mockito: ^5.0.0-nullsafety.5 - plugin_platform_interface: ^1.1.0-nullsafety.2 + + # TODO(mvanbeusekom): Update to stable 5.0.0 release when dependency conflict + # with flutter_driver has been resolved: + # Because mockito >=5.0.0 depends on analyzer ^1.0.0 which depends on crypto ^3.0.0 + # every version of flutter_driver from sdk depends on crypto 2.1.5. + mockito: ^5.0.0-nullsafety.7 + plugin_platform_interface: ^2.0.0 flutter: plugin: @@ -33,5 +38,5 @@ flutter: pluginClass: CameraPlugin environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: ">=2.12.0-259.9.beta <3.0.0" flutter: ">=1.22.0" From ee628d50ad06a0606483b3f365d92585299257af Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Fri, 26 Feb 2021 16:40:09 +0100 Subject: [PATCH 2/8] Make integration_test a dev dependency --- packages/camera/camera/example/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index ecf8418e01b3..b3ef8e497dc1 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -13,14 +13,14 @@ dependencies: flutter: sdk: flutter video_player: ^2.0.0 - integration_test: - path: ../../../integration_test dev_dependencies: flutter_test: sdk: flutter flutter_driver: sdk: flutter + integration_test: + path: ../../../integration_test pedantic: ^1.10.0 flutter: From 367eb9cd018535f306502f212565cc67b08f7c23 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Fri, 26 Feb 2021 16:52:04 +0100 Subject: [PATCH 3/8] Added test: any dependency --- packages/camera/camera/example/pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index b3ef8e497dc1..1a2953e8caa3 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -21,6 +21,7 @@ dev_dependencies: sdk: flutter integration_test: path: ../../../integration_test + test: any pedantic: ^1.10.0 flutter: From 842d623d9e03f2efecd5e8b69640659fb7585d3f Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Fri, 26 Feb 2021 17:42:05 +0100 Subject: [PATCH 4/8] Process feedback --- packages/camera/camera/example/lib/main.dart | 199 ++++++++++--------- 1 file changed, 104 insertions(+), 95 deletions(-) diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index b6b74c9f5972..90a88a0d566c 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -11,6 +11,10 @@ import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; +import '../../lib/camera.dart'; +import '../../lib/camera.dart'; +import '../../lib/camera.dart'; + class CameraExampleHome extends StatefulWidget { @override _CameraExampleHomeState createState() { @@ -32,8 +36,13 @@ IconData getCameraLensIcon(CameraLensDirection direction) { } } -void logError(String code, String? message) => +void logError(String code, String? message) { + if (message != null) { print('Error: $code\nError Message: $message'); + } else { + print('Error: $code'); + } +} class _CameraExampleHomeState extends State with WidgetsBindingObserver, TickerProviderStateMixin { @@ -46,14 +55,14 @@ class _CameraExampleHomeState extends State double _minAvailableExposureOffset = 0.0; double _maxAvailableExposureOffset = 0.0; double _currentExposureOffset = 0.0; - AnimationController? _flashModeControlRowAnimationController; - Animation? _flashModeControlRowAnimation; - AnimationController? _exposureModeControlRowAnimationController; - Animation? _exposureModeControlRowAnimation; - AnimationController? _focusModeControlRowAnimationController; - Animation? _focusModeControlRowAnimation; - double? _minAvailableZoom; - double? _maxAvailableZoom; + late AnimationController _flashModeControlRowAnimationController; + late Animation _flashModeControlRowAnimation; + late AnimationController _exposureModeControlRowAnimationController; + late Animation _exposureModeControlRowAnimation; + late AnimationController _focusModeControlRowAnimationController; + late Animation _focusModeControlRowAnimation; + double _minAvailableZoom = 1.0; + double _maxAvailableZoom = 1.0; double _currentScale = 1.0; double _baseScale = 1.0; @@ -102,14 +111,16 @@ class _CameraExampleHomeState extends State @override void didChangeAppLifecycleState(AppLifecycleState state) { // App state changed before we got the chance to initialize. - if (controller == null || !controller!.value.isInitialized) { + if ((controller?.value.isInitialized ?? false)) { return; } + final CameraController cameraController = controller!; + if (state == AppLifecycleState.inactive) { - controller?.dispose(); + cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { - onNewCameraSelected(controller!.description); + onNewCameraSelected(cameraController.description); } } @@ -163,7 +174,7 @@ class _CameraExampleHomeState extends State /// Display the preview from the camera (or a message if the preview is not available). Widget _cameraPreviewWidget() { - if (controller == null || !controller!.value.isInitialized) { + if (!(controller?.value.isInitialized ?? false)) { return const Text( 'Tap a camera', style: TextStyle( @@ -203,7 +214,7 @@ class _CameraExampleHomeState extends State } _currentScale = (_baseScale * details.scale) - .clamp(_minAvailableZoom ?? 1, _maxAvailableZoom ?? 1); + .clamp(_minAvailableZoom, _maxAvailableZoom); await controller!.setZoomLevel(_currentScale); } @@ -290,8 +301,8 @@ class _CameraExampleHomeState extends State } Widget _flashModeControlRowWidget() { - return _wrapInSizeTransition( - animation: _flashModeControlRowAnimation!, + return SizeTransition( + sizeFactor: _flashModeControlRowAnimation, child: ClipRect( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -351,8 +362,8 @@ class _CameraExampleHomeState extends State : Colors.blue, ); - return _wrapInSizeTransition( - animation: _exposureModeControlRowAnimation!, + return SizeTransition( + sizeFactor: _exposureModeControlRowAnimation, child: ClipRect( child: Container( color: Colors.grey.shade50, @@ -429,8 +440,8 @@ class _CameraExampleHomeState extends State : Colors.blue, ); - return _wrapInSizeTransition( - animation: _focusModeControlRowAnimation, + return SizeTransition( + sizeFactor: _focusModeControlRowAnimation, child: ClipRect( child: Container( color: Colors.grey.shade50, @@ -470,22 +481,29 @@ class _CameraExampleHomeState extends State ); } - Widget _wrapInSizeTransition({ - required Widget child, - Animation? animation, - }) { - if (animation == null) { - return child; - } - - return SizeTransition( - sizeFactor: animation, - child: child, - ); - } - /// Display the control bar with buttons to take pictures and record videos. Widget _captureControlRowWidget() { + VoidCallback? takePictureButton; + VoidCallback? recordButtonPressed; + VoidCallback? pauseOrResumeButtonPressed ; + VoidCallback? stopButtonPressed; + + if (controller != null) { + final CameraController cameraController = controller!; + + if (cameraController.value.isInitialized) { + if (cameraController.value.isRecordingVideo) { + takePictureButton = onTakePictureButtonPressed; + recordButtonPressed = onVideoRecordButtonPressed; + } else { + stopButtonPressed = onStopButtonPressed; + pauseOrResumeButtonPressed = cameraController.value.isRecordingPaused + ? onResumeButtonPressed + : onPauseButtonPressed; + } + } + } + return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisSize: MainAxisSize.max, @@ -493,42 +511,24 @@ class _CameraExampleHomeState extends State IconButton( icon: const Icon(Icons.camera_alt), color: Colors.blue, - onPressed: controller != null && - controller!.value.isInitialized && - !controller!.value.isRecordingVideo - ? onTakePictureButtonPressed - : null, + onPressed: takePictureButton, ), IconButton( icon: const Icon(Icons.videocam), color: Colors.blue, - onPressed: controller != null && - controller!.value.isInitialized && - !controller!.value.isRecordingVideo - ? onVideoRecordButtonPressed - : null, + onPressed: recordButtonPressed, ), 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, + onPressed: pauseOrResumeButtonPressed, ), IconButton( icon: const Icon(Icons.stop), color: Colors.red, - onPressed: controller != null && - controller!.value.isInitialized && - controller!.value.isRecordingVideo - ? onStopButtonPressed - : null, + onPressed: stopButtonPressed, ) ], ); @@ -582,46 +582,50 @@ class _CameraExampleHomeState extends State return; } + final CameraController cameraController = controller!; + final offset = Offset( details.localPosition.dx / constraints.maxWidth, details.localPosition.dy / constraints.maxHeight, ); - controller!.setExposurePoint(offset); - controller!.setFocusPoint(offset); + cameraController.setExposurePoint(offset); + cameraController.setFocusPoint(offset); } void onNewCameraSelected(CameraDescription cameraDescription) async { if (controller != null) { await controller!.dispose(); } - controller = CameraController( + final CameraController cameraController = CameraController( cameraDescription, ResolutionPreset.medium, enableAudio: enableAudio, imageFormatGroup: ImageFormatGroup.jpeg, ); + controller = cameraController; // If the controller is updated then update the UI. - controller!.addListener(() { + cameraController.addListener(() { if (mounted) setState(() {}); - if (controller!.value.hasError) { - showInSnackBar('Camera error ${controller!.value.errorDescription}'); + if (cameraController.value.hasError) { + showInSnackBar( + 'Camera error ${cameraController.value.errorDescription}'); } }); try { - await controller!.initialize(); + await cameraController.initialize(); await Future.wait([ - controller! + cameraController .getMinExposureOffset() .then((value) => _minAvailableExposureOffset = value), - controller! + cameraController .getMaxExposureOffset() .then((value) => _maxAvailableExposureOffset = value), - controller! + cameraController .getMaxZoomLevel() .then((value) => _maxAvailableZoom = value), - controller! + cameraController .getMinZoomLevel() .then((value) => _minAvailableZoom = value), ]); @@ -648,32 +652,32 @@ class _CameraExampleHomeState extends State } void onFlashModeButtonPressed() { - if (_flashModeControlRowAnimationController?.value == 1) { - _flashModeControlRowAnimationController?.reverse(); + if (_flashModeControlRowAnimationController.value == 1) { + _flashModeControlRowAnimationController.reverse(); } else { - _flashModeControlRowAnimationController?.forward(); - _exposureModeControlRowAnimationController?.reverse(); - _focusModeControlRowAnimationController?.reverse(); + _flashModeControlRowAnimationController.forward(); + _exposureModeControlRowAnimationController.reverse(); + _focusModeControlRowAnimationController.reverse(); } } void onExposureModeButtonPressed() { - if (_exposureModeControlRowAnimationController?.value == 1) { - _exposureModeControlRowAnimationController?.reverse(); + if (_exposureModeControlRowAnimationController.value == 1) { + _exposureModeControlRowAnimationController.reverse(); } else { - _exposureModeControlRowAnimationController?.forward(); - _flashModeControlRowAnimationController?.reverse(); - _focusModeControlRowAnimationController?.reverse(); + _exposureModeControlRowAnimationController.forward(); + _flashModeControlRowAnimationController.reverse(); + _focusModeControlRowAnimationController.reverse(); } } void onFocusModeButtonPressed() { - if (_focusModeControlRowAnimationController?.value == 1) { - _focusModeControlRowAnimationController?.reverse(); + if (_focusModeControlRowAnimationController.value == 1) { + _focusModeControlRowAnimationController.reverse(); } else { - _focusModeControlRowAnimationController?.forward(); - _flashModeControlRowAnimationController?.reverse(); - _exposureModeControlRowAnimationController?.reverse(); + _focusModeControlRowAnimationController.forward(); + _flashModeControlRowAnimationController.reverse(); + _exposureModeControlRowAnimationController.reverse(); } } @@ -686,13 +690,14 @@ class _CameraExampleHomeState extends State void onCaptureOrientationLockButtonPressed() async { if (controller != null) { - if (controller!.value.isCaptureOrientationLocked) { - await controller!.unlockCaptureOrientation(); + final CameraController cameraController = controller!; + if (cameraController.value.isCaptureOrientationLocked) { + await cameraController.unlockCaptureOrientation(); showInSnackBar('Capture orientation unlocked'); } else { - await controller!.lockCaptureOrientation(); + await cameraController.lockCaptureOrientation(); showInSnackBar( - 'Capture orientation locked to ${controller!.value.lockedCaptureOrientation.toString().split('.').last}'); + 'Capture orientation locked to ${cameraController.value.lockedCaptureOrientation.toString().split('.').last}'); } } } @@ -750,18 +755,20 @@ class _CameraExampleHomeState extends State } Future startVideoRecording() async { - if (controller == null || !controller!.value.isInitialized) { + if (!(controller?.value.isRecordingVideo ?? false)) { showInSnackBar('Error: select a camera first.'); return; } - if (controller!.value.isRecordingVideo) { + final CameraController cameraController = controller!; + + if (cameraController.value.isRecordingVideo) { // A recording is already started, do nothing. return; } try { - await controller!.startVideoRecording(); + await cameraController.startVideoRecording(); } on CameraException catch (e) { _showCameraException(e); return; @@ -769,7 +776,7 @@ class _CameraExampleHomeState extends State } Future stopVideoRecording() async { - if (controller == null || !controller!.value.isRecordingVideo) { + if (!(controller?.value.isRecordingVideo ?? false)) { return null; } @@ -782,7 +789,7 @@ class _CameraExampleHomeState extends State } Future pauseVideoRecording() async { - if (controller == null || !controller!.value.isRecordingVideo) { + if (!(controller?.value.isRecordingVideo ?? false)) { return null; } @@ -795,7 +802,7 @@ class _CameraExampleHomeState extends State } Future resumeVideoRecording() async { - if (controller == null || !controller!.value.isRecordingVideo) { + if (!(controller?.value.isRecordingVideo ?? false)) { return null; } @@ -890,18 +897,20 @@ class _CameraExampleHomeState extends State } Future takePicture() async { - if (controller == null || !controller!.value.isInitialized) { + if (!(controller?.value.isInitialized ?? false)) { showInSnackBar('Error: select a camera first.'); return null; } - if (controller!.value.isTakingPicture) { + final CameraController cameraController = controller!; + + if (cameraController.value.isTakingPicture) { // A capture is already pending, do nothing. return null; } try { - XFile file = await controller!.takePicture(); + XFile file = await cameraController.takePicture(); return file; } on CameraException catch (e) { _showCameraException(e); From 13830d83d1ece2d7bccfba5f22651975a09bd607 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Fri, 26 Feb 2021 17:49:06 +0100 Subject: [PATCH 5/8] Run integration tests on Dart 2.9 --- .../example/integration_test/camera_test.dart | 13 ++++++++++--- .../example/test_driver/integration_test.dart | 7 +++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/camera/camera/example/integration_test/camera_test.dart b/packages/camera/camera/example/integration_test/camera_test.dart index 2d29ecf093d2..4ff624c7d989 100644 --- a/packages/camera/camera/example/integration_test/camera_test.dart +++ b/packages/camera/camera/example/integration_test/camera_test.dart @@ -1,3 +1,10 @@ +// Copyright 2019, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(mvanbeusekom): Remove this once flutter_driver supports null safety. +// https://github.com/flutter/flutter/issues/71379 +// @dart = 2.9 import 'dart:async'; import 'dart:io'; import 'dart:ui'; @@ -10,7 +17,7 @@ import 'package:video_player/video_player.dart'; import 'package:integration_test/integration_test.dart'; void main() { - late Directory testDir; + Directory testDir; IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -50,7 +57,7 @@ void main() { // whether the image is exactly the desired resolution. Future testCaptureImageResolution( CameraController controller, ResolutionPreset preset) async { - final Size expectedSize = presetExpectedSizes[preset]!; + final Size expectedSize = presetExpectedSizes[preset]; print( 'Capturing photo at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); @@ -95,7 +102,7 @@ void main() { // whether the image is exactly the desired resolution. Future testCaptureVideoResolution( CameraController controller, ResolutionPreset preset) async { - final Size expectedSize = presetExpectedSizes[preset]!; + final Size expectedSize = presetExpectedSizes[preset]; print( 'Capturing video at $preset (${expectedSize.width}x${expectedSize.height}) using camera ${controller.description.name}'); diff --git a/packages/camera/camera/example/test_driver/integration_test.dart b/packages/camera/camera/example/test_driver/integration_test.dart index 1e6e3ba7941f..160b48f8f72f 100644 --- a/packages/camera/camera/example/test_driver/integration_test.dart +++ b/packages/camera/camera/example/test_driver/integration_test.dart @@ -1,3 +1,10 @@ +// Copyright 2019, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// TODO(mvanbeusekom): Remove this once flutter_driver supports null safety. +// https://github.com/flutter/flutter/issues/71379 +// @dart = 2.9 import 'dart:async'; import 'dart:convert'; import 'dart:io'; From 5fc4df8880538fcd14fe10243930aa271f88185a Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Fri, 26 Feb 2021 17:50:12 +0100 Subject: [PATCH 6/8] Remove referenced import to camera --- packages/camera/camera/example/lib/main.dart | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index 90a88a0d566c..b96bba2ec500 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -11,10 +11,6 @@ import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:video_player/video_player.dart'; -import '../../lib/camera.dart'; -import '../../lib/camera.dart'; -import '../../lib/camera.dart'; - class CameraExampleHome extends StatefulWidget { @override _CameraExampleHomeState createState() { @@ -485,7 +481,7 @@ class _CameraExampleHomeState extends State Widget _captureControlRowWidget() { VoidCallback? takePictureButton; VoidCallback? recordButtonPressed; - VoidCallback? pauseOrResumeButtonPressed ; + VoidCallback? pauseOrResumeButtonPressed; VoidCallback? stopButtonPressed; if (controller != null) { From 22d8a894a62ba3098537c0133dd0a115141b4f8e Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Fri, 26 Feb 2021 17:54:38 +0100 Subject: [PATCH 7/8] Fixed analysis warnings --- packages/camera/camera/example/lib/main.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index b96bba2ec500..99c370f645bd 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -75,7 +75,7 @@ class _CameraExampleHomeState extends State vsync: this, ); _flashModeControlRowAnimation = CurvedAnimation( - parent: _flashModeControlRowAnimationController!, + parent: _flashModeControlRowAnimationController, curve: Curves.easeInCubic, ); _exposureModeControlRowAnimationController = AnimationController( @@ -83,7 +83,7 @@ class _CameraExampleHomeState extends State vsync: this, ); _exposureModeControlRowAnimation = CurvedAnimation( - parent: _exposureModeControlRowAnimationController!, + parent: _exposureModeControlRowAnimationController, curve: Curves.easeInCubic, ); _focusModeControlRowAnimationController = AnimationController( @@ -91,7 +91,7 @@ class _CameraExampleHomeState extends State vsync: this, ); _focusModeControlRowAnimation = CurvedAnimation( - parent: _focusModeControlRowAnimationController!, + parent: _focusModeControlRowAnimationController, curve: Curves.easeInCubic, ); } @@ -99,8 +99,8 @@ class _CameraExampleHomeState extends State @override void dispose() { WidgetsBinding.instance?.removeObserver(this); - _flashModeControlRowAnimationController?.dispose(); - _exposureModeControlRowAnimationController?.dispose(); + _flashModeControlRowAnimationController.dispose(); + _exposureModeControlRowAnimationController.dispose(); super.dispose(); } From 8ba4fce4876122567cb0f526fcb17d451abf2022 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 2 Mar 2021 11:13:35 +0100 Subject: [PATCH 8/8] Processed feedback on PR --- packages/camera/camera/example/lib/main.dart | 102 ++++++++++--------- packages/camera/camera/example/pubspec.yaml | 1 - 2 files changed, 56 insertions(+), 47 deletions(-) diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index 99c370f645bd..5eebc9a379ca 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -106,13 +106,13 @@ class _CameraExampleHomeState extends State @override void didChangeAppLifecycleState(AppLifecycleState state) { + final CameraController? cameraController = controller; + // App state changed before we got the chance to initialize. - if ((controller?.value.isInitialized ?? false)) { + if (cameraController == null || !cameraController.value.isInitialized) { return; } - final CameraController cameraController = controller!; - if (state == AppLifecycleState.inactive) { cameraController.dispose(); } else if (state == AppLifecycleState.resumed) { @@ -170,7 +170,9 @@ class _CameraExampleHomeState extends State /// Display the preview from the camera (or a message if the preview is not available). Widget _cameraPreviewWidget() { - if (!(controller?.value.isInitialized ?? false)) { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { return const Text( 'Tap a camera', style: TextStyle( @@ -217,25 +219,28 @@ class _CameraExampleHomeState extends State /// Display the thumbnail of the captured image or video. Widget _thumbnailWidget() { + final VideoPlayerController? localVideoController = videoController; + return Expanded( child: Align( alignment: Alignment.centerRight, child: Row( mainAxisSize: MainAxisSize.min, children: [ - videoController == null && imageFile == null + localVideoController == null && imageFile == null ? Container() : SizedBox( - child: (videoController == null) + child: (localVideoController == null) ? Image.file(File(imageFile!.path)) : Container( child: Center( child: AspectRatio( aspectRatio: - videoController!.value.size != null - ? videoController!.value.aspectRatio + localVideoController.value.size != null + ? localVideoController + .value.aspectRatio : 1.0, - child: VideoPlayer(videoController!)), + child: VideoPlayer(localVideoController)), ), decoration: BoxDecoration( border: Border.all(color: Colors.pink)), @@ -479,26 +484,7 @@ class _CameraExampleHomeState extends State /// Display the control bar with buttons to take pictures and record videos. Widget _captureControlRowWidget() { - VoidCallback? takePictureButton; - VoidCallback? recordButtonPressed; - VoidCallback? pauseOrResumeButtonPressed; - VoidCallback? stopButtonPressed; - - if (controller != null) { - final CameraController cameraController = controller!; - - if (cameraController.value.isInitialized) { - if (cameraController.value.isRecordingVideo) { - takePictureButton = onTakePictureButtonPressed; - recordButtonPressed = onVideoRecordButtonPressed; - } else { - stopButtonPressed = onStopButtonPressed; - pauseOrResumeButtonPressed = cameraController.value.isRecordingPaused - ? onResumeButtonPressed - : onPauseButtonPressed; - } - } - } + final CameraController? cameraController = controller; return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -507,24 +493,43 @@ class _CameraExampleHomeState extends State IconButton( icon: const Icon(Icons.camera_alt), color: Colors.blue, - onPressed: takePictureButton, + onPressed: cameraController != null && + cameraController.value.isInitialized && + !cameraController.value.isRecordingVideo + ? onTakePictureButtonPressed + : null, ), IconButton( icon: const Icon(Icons.videocam), color: Colors.blue, - onPressed: recordButtonPressed, + onPressed: cameraController != null && + cameraController.value.isInitialized && + !cameraController.value.isRecordingVideo + ? onVideoRecordButtonPressed + : null, ), IconButton( - icon: controller != null && controller!.value.isRecordingPaused + icon: cameraController != null && + cameraController.value.isRecordingPaused ? Icon(Icons.play_arrow) : Icon(Icons.pause), color: Colors.blue, - onPressed: pauseOrResumeButtonPressed, + onPressed: cameraController != null && + cameraController.value.isInitialized && + cameraController.value.isRecordingVideo + ? (cameraController.value.isRecordingPaused) + ? onResumeButtonPressed + : onPauseButtonPressed + : null, ), IconButton( icon: const Icon(Icons.stop), color: Colors.red, - onPressed: stopButtonPressed, + onPressed: cameraController != null && + cameraController.value.isInitialized && + cameraController.value.isRecordingVideo + ? onStopButtonPressed + : null, ) ], ); @@ -751,13 +756,13 @@ class _CameraExampleHomeState extends State } Future startVideoRecording() async { - if (!(controller?.value.isRecordingVideo ?? false)) { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isInitialized) { showInSnackBar('Error: select a camera first.'); return; } - final CameraController cameraController = controller!; - if (cameraController.value.isRecordingVideo) { // A recording is already started, do nothing. return; @@ -772,12 +777,14 @@ class _CameraExampleHomeState extends State } Future stopVideoRecording() async { - if (!(controller?.value.isRecordingVideo ?? false)) { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { return null; } try { - return controller!.stopVideoRecording(); + return cameraController.stopVideoRecording(); } on CameraException catch (e) { _showCameraException(e); return null; @@ -785,12 +792,14 @@ class _CameraExampleHomeState extends State } Future pauseVideoRecording() async { - if (!(controller?.value.isRecordingVideo ?? false)) { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { return null; } try { - await controller!.pauseVideoRecording(); + await cameraController.pauseVideoRecording(); } on CameraException catch (e) { _showCameraException(e); rethrow; @@ -798,12 +807,14 @@ class _CameraExampleHomeState extends State } Future resumeVideoRecording() async { - if (!(controller?.value.isRecordingVideo ?? false)) { + final CameraController? cameraController = controller; + + if (cameraController == null || !cameraController.value.isRecordingVideo) { return null; } try { - await controller!.resumeVideoRecording(); + await cameraController.resumeVideoRecording(); } on CameraException catch (e) { _showCameraException(e); rethrow; @@ -893,13 +904,12 @@ class _CameraExampleHomeState extends State } Future takePicture() async { - if (!(controller?.value.isInitialized ?? false)) { + final CameraController? cameraController = controller; + if (cameraController == null || !cameraController.value.isInitialized) { showInSnackBar('Error: select a camera first.'); return null; } - final CameraController cameraController = controller!; - if (cameraController.value.isTakingPicture) { // A capture is already pending, do nothing. return null; diff --git a/packages/camera/camera/example/pubspec.yaml b/packages/camera/camera/example/pubspec.yaml index 1a2953e8caa3..b3ef8e497dc1 100644 --- a/packages/camera/camera/example/pubspec.yaml +++ b/packages/camera/camera/example/pubspec.yaml @@ -21,7 +21,6 @@ dev_dependencies: sdk: flutter integration_test: path: ../../../integration_test - test: any pedantic: ^1.10.0 flutter: