diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md index 5fd62d2b716b..8d2c20108ed0 100644 --- a/packages/camera/camera/CHANGELOG.md +++ b/packages/camera/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.7.0+2 + +* Improved error feedback by differentiating between uninitialized and disposed camera controllers. + ## 0.7.0+1 * Fixes picture captures causing a crash on some Huawei devices. diff --git a/packages/camera/camera/example/lib/main.dart b/packages/camera/camera/example/lib/main.dart index 490cae6676d3..6244aa5a8e37 100644 --- a/packages/camera/camera/example/lib/main.dart +++ b/packages/camera/camera/example/lib/main.dart @@ -580,10 +580,16 @@ class _CameraExampleHomeState extends State try { await controller.initialize(); - _minAvailableExposureOffset = await controller.getMinExposureOffset(); - _maxAvailableExposureOffset = await controller.getMaxExposureOffset(); - _maxAvailableZoom = await controller.getMaxZoomLevel(); - _minAvailableZoom = await controller.getMinZoomLevel(); + await Future.wait([ + controller + .getMinExposureOffset() + .then((value) => _minAvailableExposureOffset = value), + controller + .getMaxExposureOffset() + .then((value) => _maxAvailableExposureOffset = value), + controller.getMaxZoomLevel().then((value) => _maxAvailableZoom = value), + controller.getMinZoomLevel().then((value) => _minAvailableZoom = value), + ]); } on CameraException catch (e) { _showCameraException(e); } diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart index 807bec367256..80e83c867954 100644 --- a/packages/camera/camera/lib/src/camera_controller.dart +++ b/packages/camera/camera/lib/src/camera_controller.dart @@ -323,12 +323,7 @@ class CameraController extends ValueNotifier { /// /// Throws a [CameraException] if the capture fails. Future takePicture() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController.', - 'takePicture was called on uninitialized CameraController', - ); - } + _throwIfNotInitialized("takePicture"); if (value.isTakingPicture) { throw CameraException( 'Previous capture has not returned yet.', @@ -366,13 +361,7 @@ class CameraController extends ValueNotifier { Future startImageStream(onLatestImageAvailable onAvailable) async { assert(defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS); - - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'startImageStream was called on uninitialized CameraController.', - ); - } + _throwIfNotInitialized("startImageStream"); if (value.isRecordingVideo) { throw CameraException( 'A video recording is already started.', @@ -412,13 +401,7 @@ class CameraController extends ValueNotifier { Future stopImageStream() async { assert(defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS); - - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'stopImageStream was called on uninitialized CameraController.', - ); - } + _throwIfNotInitialized("stopImageStream"); if (value.isRecordingVideo) { throw CameraException( 'A video recording is already started.', @@ -448,12 +431,7 @@ class CameraController extends ValueNotifier { /// The video is returned as a [XFile] after calling [stopVideoRecording]. /// Throws a [CameraException] if the capture fails. Future startVideoRecording() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'startVideoRecording was called on uninitialized CameraController', - ); - } + _throwIfNotInitialized("startVideoRecording"); if (value.isRecordingVideo) { throw CameraException( 'A video recording is already started.', @@ -483,12 +461,7 @@ class CameraController extends ValueNotifier { /// /// Throws a [CameraException] if the capture failed. Future stopVideoRecording() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'stopVideoRecording was called on uninitialized CameraController', - ); - } + _throwIfNotInitialized("stopVideoRecording"); if (!value.isRecordingVideo) { throw CameraException( 'No video is recording', @@ -511,12 +484,7 @@ class CameraController extends ValueNotifier { /// /// This feature is only available on iOS and Android sdk 24+. Future pauseVideoRecording() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'pauseVideoRecording was called on uninitialized CameraController', - ); - } + _throwIfNotInitialized("pauseVideoRecording"); if (!value.isRecordingVideo) { throw CameraException( 'No video is recording', @@ -535,12 +503,7 @@ class CameraController extends ValueNotifier { /// /// This feature is only available on iOS and Android sdk 24+. Future resumeVideoRecording() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'resumeVideoRecording was called on uninitialized CameraController', - ); - } + _throwIfNotInitialized("resumeVideoRecording"); if (!value.isRecordingVideo) { throw CameraException( 'No video is recording', @@ -557,12 +520,7 @@ class CameraController extends ValueNotifier { /// Returns a widget showing a live camera preview. Widget buildPreview() { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'buildView() was called on uninitialized CameraController.', - ); - } + _throwIfNotInitialized("buildPreview"); try { return CameraPlatform.instance.buildPreview(_cameraId); } on PlatformException catch (e) { @@ -572,13 +530,7 @@ class CameraController extends ValueNotifier { /// Gets the maximum supported zoom level for the selected camera. Future getMaxZoomLevel() { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'getMaxZoomLevel was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("getMaxZoomLevel"); try { return CameraPlatform.instance.getMaxZoomLevel(_cameraId); } on PlatformException catch (e) { @@ -588,13 +540,7 @@ class CameraController extends ValueNotifier { /// Gets the minimum supported zoom level for the selected camera. Future getMinZoomLevel() { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'getMinZoomLevel was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("getMinZoomLevel"); try { return CameraPlatform.instance.getMinZoomLevel(_cameraId); } on PlatformException catch (e) { @@ -608,13 +554,7 @@ class CameraController extends ValueNotifier { /// zoom level returned by the `getMaxZoomLevel`. Throws an `CameraException` /// when an illegal zoom level is suplied. Future setZoomLevel(double zoom) { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'setZoomLevel was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("setZoomLevel"); try { return CameraPlatform.instance.setZoomLevel(_cameraId, zoom); } on PlatformException catch (e) { @@ -666,13 +606,7 @@ class CameraController extends ValueNotifier { /// Gets the minimum supported exposure offset for the selected camera in EV units. Future getMinExposureOffset() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'getMinExposureOffset was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("getMinExposureOffset"); try { return CameraPlatform.instance.getMinExposureOffset(_cameraId); } on PlatformException catch (e) { @@ -682,13 +616,7 @@ class CameraController extends ValueNotifier { /// Gets the maximum supported exposure offset for the selected camera in EV units. Future getMaxExposureOffset() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'getMaxExposureOffset was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("getMaxExposureOffset"); try { return CameraPlatform.instance.getMaxExposureOffset(_cameraId); } on PlatformException catch (e) { @@ -700,13 +628,7 @@ class CameraController extends ValueNotifier { /// /// Returns 0 when the camera supports using a free value without stepping. Future getExposureOffsetStepSize() async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'getExposureOffsetStepSize was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("getExposureOffsetStepSize"); try { return CameraPlatform.instance.getExposureOffsetStepSize(_cameraId); } on PlatformException catch (e) { @@ -726,13 +648,7 @@ class CameraController extends ValueNotifier { /// /// Returns the (rounded) offset value that was set. Future setExposureOffset(double offset) async { - if (!value.isInitialized || _isDisposed) { - throw CameraException( - 'Uninitialized CameraController', - 'setExposureOffset was called on uninitialized CameraController', - ); - } - + _throwIfNotInitialized("setExposureOffset"); // Check if offset is in range List range = await Future.wait([getMinExposureOffset(), getMaxExposureOffset()]); @@ -834,4 +750,19 @@ class CameraController extends ValueNotifier { await CameraPlatform.instance.dispose(_cameraId); } } + + void _throwIfNotInitialized(String functionName) { + if (!value.isInitialized) { + throw CameraException( + 'Uninitialized CameraController', + '$functionName() was called on an uninitialized CameraController.', + ); + } + if (_isDisposed) { + throw CameraException( + 'Disposed CameraController', + '$functionName() was called on a disposed CameraController.', + ); + } + } } diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml index b406ce5ba64f..2b6d163dfbeb 100644 --- a/packages/camera/camera/pubspec.yaml +++ b/packages/camera/camera/pubspec.yaml @@ -2,7 +2,7 @@ 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.7.0+1 +version: 0.7.0+2 homepage: https://github.com/flutter/plugins/tree/master/packages/camera/camera dependencies: diff --git a/packages/camera/camera/test/camera_image_stream_test.dart b/packages/camera/camera/test/camera_image_stream_test.dart index be7047f2220f..57e3aeb36f3f 100644 --- a/packages/camera/camera/test/camera_image_stream_test.dart +++ b/packages/camera/camera/test/camera_image_stream_test.dart @@ -22,12 +22,21 @@ void main() { ResolutionPreset.max); expect( - () => cameraController.startImageStream((image) => null), - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController.', - 'startImageStream was called on uninitialized CameraController.', - ))); + () => cameraController.startImageStream((image) => null), + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'startImageStream() was called on an uninitialized CameraController.', + ), + ), + ); }); test('startImageStream() throws $CameraException when recording videos', @@ -107,12 +116,21 @@ void main() { ResolutionPreset.max); expect( - cameraController.stopImageStream, - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController.', - 'stopImageStream was called on uninitialized CameraController.', - ))); + cameraController.stopImageStream, + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'stopImageStream() was called on an uninitialized CameraController.', + ), + ), + ); }); test('stopImageStream() throws $CameraException when recording videos', diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart index 2f53691217cb..d0b09fae1304 100644 --- a/packages/camera/camera/test/camera_test.dart +++ b/packages/camera/camera/test/camera_test.dart @@ -213,12 +213,21 @@ void main() { sensorOrientation: 90), ResolutionPreset.max); expect( - cameraController.takePicture(), - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController.', - 'takePicture was called on uninitialized CameraController', - ))); + cameraController.takePicture(), + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'takePicture() was called on an uninitialized CameraController.', + ), + ), + ); }); test('takePicture() throws $CameraException when takePicture is true', @@ -286,12 +295,21 @@ void main() { ResolutionPreset.max); expect( - cameraController.startVideoRecording(), - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'startVideoRecording was called on uninitialized CameraController', - ))); + cameraController.startVideoRecording(), + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'startVideoRecording() was called on an uninitialized CameraController.', + ), + ), + ); }); test('startVideoRecording() throws $CameraException when recording videos', () async { @@ -350,12 +368,21 @@ void main() { ResolutionPreset.max); expect( - cameraController.getMaxZoomLevel, - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'getMaxZoomLevel was called on uninitialized CameraController', - ))); + cameraController.getMaxZoomLevel, + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'getMaxZoomLevel() was called on an uninitialized CameraController.', + ), + ), + ); }); test('getMaxZoomLevel() throws $CameraException when disposed', () async { @@ -370,12 +397,21 @@ void main() { await cameraController.dispose(); expect( - cameraController.getMaxZoomLevel, - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'getMaxZoomLevel was called on uninitialized CameraController', - ))); + cameraController.getMaxZoomLevel, + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Disposed CameraController', + ) + .having( + (error) => error.description, + 'description', + 'getMaxZoomLevel() was called on a disposed CameraController.', + ), + ), + ); }); test( @@ -432,12 +468,21 @@ void main() { ResolutionPreset.max); expect( - cameraController.getMinZoomLevel, - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'getMinZoomLevel was called on uninitialized CameraController', - ))); + cameraController.getMinZoomLevel, + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'getMinZoomLevel() was called on an uninitialized CameraController.', + ), + ), + ); }); test('getMinZoomLevel() throws $CameraException when disposed', () async { @@ -452,12 +497,21 @@ void main() { await cameraController.dispose(); expect( - cameraController.getMinZoomLevel, - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'getMinZoomLevel was called on uninitialized CameraController', - ))); + cameraController.getMinZoomLevel, + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Disposed CameraController', + ) + .having( + (error) => error.description, + 'description', + 'getMinZoomLevel() was called on a disposed CameraController.', + ), + ), + ); }); test( @@ -513,12 +567,21 @@ void main() { ResolutionPreset.max); expect( - () => cameraController.setZoomLevel(42.0), - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'setZoomLevel was called on uninitialized CameraController', - ))); + () => cameraController.setZoomLevel(42.0), + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Uninitialized CameraController', + ) + .having( + (error) => error.description, + 'description', + 'setZoomLevel() was called on an uninitialized CameraController.', + ), + ), + ); }); test('setZoomLevel() throws $CameraException when disposed', () async { @@ -533,12 +596,21 @@ void main() { await cameraController.dispose(); expect( - () => cameraController.setZoomLevel(42.0), - throwsA(isA().having( - (error) => error.description, - 'Uninitialized CameraController', - 'setZoomLevel was called on uninitialized CameraController', - ))); + () => cameraController.setZoomLevel(42.0), + throwsA( + isA() + .having( + (error) => error.code, + 'code', + 'Disposed CameraController', + ) + .having( + (error) => error.description, + 'description', + 'setZoomLevel() was called on a disposed CameraController.', + ), + ), + ); }); test(