[camera] Add back Optional type for nullable CameraController orientations (#6911)
* Add flag
* Add missing comment
* Add tests
* Bump versions
* Stage changelog changes
* Revert "Fix examples analyze"
This reverts commit 4db1858a29136f3fb07a223d94d7e68b6b8d4b7d.
* Revert "[camera] Remove deprecated Optional type (#6870)"
This reverts commit 3d8b73bf08bf746bcbcdd219eb87ced572cc529b.
* Add back optional
* Edit changelog
* Fix semicolon
* Add )
diff --git a/packages/camera/camera/CHANGELOG.md b/packages/camera/camera/CHANGELOG.md
index f7db10b..13c0040 100644
--- a/packages/camera/camera/CHANGELOG.md
+++ b/packages/camera/camera/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.10.3
+
+* Adds back use of Optional type.
+
## 0.10.2+1
* Updates code for stricter lint checks.
diff --git a/packages/camera/camera/lib/src/camera_controller.dart b/packages/camera/camera/lib/src/camera_controller.dart
index b201074..7a396c1 100644
--- a/packages/camera/camera/lib/src/camera_controller.dart
+++ b/packages/camera/camera/lib/src/camera_controller.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
+import 'dart:collection';
import 'dart:math';
import 'package:camera_platform_interface/camera_platform_interface.dart';
@@ -160,10 +161,10 @@
bool? exposurePointSupported,
bool? focusPointSupported,
DeviceOrientation? deviceOrientation,
- DeviceOrientation? lockedCaptureOrientation,
- DeviceOrientation? recordingOrientation,
+ Optional<DeviceOrientation>? lockedCaptureOrientation,
+ Optional<DeviceOrientation>? recordingOrientation,
bool? isPreviewPaused,
- DeviceOrientation? previewPauseOrientation,
+ Optional<DeviceOrientation>? previewPauseOrientation,
}) {
return CameraValue(
isInitialized: isInitialized ?? this.isInitialized,
@@ -180,12 +181,16 @@
exposurePointSupported ?? this.exposurePointSupported,
focusPointSupported: focusPointSupported ?? this.focusPointSupported,
deviceOrientation: deviceOrientation ?? this.deviceOrientation,
- lockedCaptureOrientation:
- lockedCaptureOrientation ?? this.lockedCaptureOrientation,
- recordingOrientation: recordingOrientation ?? this.recordingOrientation,
+ lockedCaptureOrientation: lockedCaptureOrientation == null
+ ? this.lockedCaptureOrientation
+ : lockedCaptureOrientation.orNull,
+ recordingOrientation: recordingOrientation == null
+ ? this.recordingOrientation
+ : recordingOrientation.orNull,
isPreviewPaused: isPreviewPaused ?? this.isPreviewPaused,
- previewPauseOrientation:
- previewPauseOrientation ?? this.previewPauseOrientation,
+ previewPauseOrientation: previewPauseOrientation == null
+ ? this.previewPauseOrientation
+ : previewPauseOrientation.orNull,
);
}
@@ -353,8 +358,8 @@
await CameraPlatform.instance.pausePreview(_cameraId);
value = value.copyWith(
isPreviewPaused: true,
- previewPauseOrientation:
- value.lockedCaptureOrientation ?? value.deviceOrientation);
+ previewPauseOrientation: Optional<DeviceOrientation>.of(
+ value.lockedCaptureOrientation ?? value.deviceOrientation));
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
@@ -367,7 +372,9 @@
}
try {
await CameraPlatform.instance.resumePreview(_cameraId);
- value = value.copyWith(isPreviewPaused: false);
+ value = value.copyWith(
+ isPreviewPaused: false,
+ previewPauseOrientation: const Optional<DeviceOrientation>.absent());
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
@@ -498,9 +505,9 @@
value = value.copyWith(
isRecordingVideo: true,
isRecordingPaused: false,
- isStreamingImages: onAvailable != null,
- recordingOrientation:
- value.lockedCaptureOrientation ?? value.deviceOrientation);
+ recordingOrientation: Optional<DeviceOrientation>.of(
+ value.lockedCaptureOrientation ?? value.deviceOrientation),
+ isStreamingImages: onAvailable != null);
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
@@ -525,7 +532,10 @@
try {
final XFile file =
await CameraPlatform.instance.stopVideoRecording(_cameraId);
- value = value.copyWith(isRecordingVideo: false);
+ value = value.copyWith(
+ isRecordingVideo: false,
+ recordingOrientation: const Optional<DeviceOrientation>.absent(),
+ );
return file;
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
@@ -743,7 +753,8 @@
await CameraPlatform.instance.lockCaptureOrientation(
_cameraId, orientation ?? value.deviceOrientation);
value = value.copyWith(
- lockedCaptureOrientation: orientation ?? value.deviceOrientation);
+ lockedCaptureOrientation: Optional<DeviceOrientation>.of(
+ orientation ?? value.deviceOrientation));
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
@@ -763,7 +774,8 @@
Future<void> unlockCaptureOrientation() async {
try {
await CameraPlatform.instance.unlockCaptureOrientation(_cameraId);
- value = value.copyWith();
+ value = value.copyWith(
+ lockedCaptureOrientation: const Optional<DeviceOrientation>.absent());
} on PlatformException catch (e) {
throw CameraException(e.code, e.message);
}
@@ -834,3 +846,112 @@
}
}
}
+
+/// A value that might be absent.
+///
+/// Used to represent [DeviceOrientation]s that are optional but also able
+/// to be cleared.
+@immutable
+class Optional<T> extends IterableBase<T> {
+ /// Constructs an empty Optional.
+ const Optional.absent() : _value = null;
+
+ /// Constructs an Optional of the given [value].
+ ///
+ /// Throws [ArgumentError] if [value] is null.
+ Optional.of(T value) : _value = value {
+ // TODO(cbracken): Delete and make this ctor const once mixed-mode
+ // execution is no longer around.
+ ArgumentError.checkNotNull(value);
+ }
+
+ /// Constructs an Optional of the given [value].
+ ///
+ /// If [value] is null, returns [absent()].
+ const Optional.fromNullable(T? value) : _value = value;
+
+ final T? _value;
+
+ /// True when this optional contains a value.
+ bool get isPresent => _value != null;
+
+ /// True when this optional contains no value.
+ bool get isNotPresent => _value == null;
+
+ /// Gets the Optional value.
+ ///
+ /// Throws [StateError] if [value] is null.
+ T get value {
+ if (_value == null) {
+ throw StateError('value called on absent Optional.');
+ }
+ return _value!;
+ }
+
+ /// Executes a function if the Optional value is present.
+ void ifPresent(void Function(T value) ifPresent) {
+ if (isPresent) {
+ ifPresent(_value as T);
+ }
+ }
+
+ /// Execution a function if the Optional value is absent.
+ void ifAbsent(void Function() ifAbsent) {
+ if (!isPresent) {
+ ifAbsent();
+ }
+ }
+
+ /// Gets the Optional value with a default.
+ ///
+ /// The default is returned if the Optional is [absent()].
+ ///
+ /// Throws [ArgumentError] if [defaultValue] is null.
+ T or(T defaultValue) {
+ return _value ?? defaultValue;
+ }
+
+ /// Gets the Optional value, or `null` if there is none.
+ T? get orNull => _value;
+
+ /// Transforms the Optional value.
+ ///
+ /// If the Optional is [absent()], returns [absent()] without applying the transformer.
+ ///
+ /// The transformer must not return `null`. If it does, an [ArgumentError] is thrown.
+ Optional<S> transform<S>(S Function(T value) transformer) {
+ return _value == null
+ ? Optional<S>.absent()
+ : Optional<S>.of(transformer(_value as T));
+ }
+
+ /// Transforms the Optional value.
+ ///
+ /// If the Optional is [absent()], returns [absent()] without applying the transformer.
+ ///
+ /// Returns [absent()] if the transformer returns `null`.
+ Optional<S> transformNullable<S>(S? Function(T value) transformer) {
+ return _value == null
+ ? Optional<S>.absent()
+ : Optional<S>.fromNullable(transformer(_value as T));
+ }
+
+ @override
+ Iterator<T> get iterator =>
+ isPresent ? <T>[_value as T].iterator : Iterable<T>.empty().iterator;
+
+ /// Delegates to the underlying [value] hashCode.
+ @override
+ int get hashCode => _value.hashCode;
+
+ /// Delegates to the underlying [value] operator==.
+ @override
+ bool operator ==(Object o) => o is Optional<T> && o._value == _value;
+
+ @override
+ String toString() {
+ return _value == null
+ ? 'Optional { absent }'
+ : 'Optional { value: $_value }';
+ }
+}
diff --git a/packages/camera/camera/pubspec.yaml b/packages/camera/camera/pubspec.yaml
index fb62665..1b902ab 100644
--- a/packages/camera/camera/pubspec.yaml
+++ b/packages/camera/camera/pubspec.yaml
@@ -4,7 +4,7 @@
Dart.
repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
-version: 0.10.2+1
+version: 0.10.3
environment:
sdk: ">=2.14.0 <3.0.0"
diff --git a/packages/camera/camera/test/camera_preview_test.dart b/packages/camera/camera/test/camera_preview_test.dart
index 7c43787..6677fcf 100644
--- a/packages/camera/camera/test/camera_preview_test.dart
+++ b/packages/camera/camera/test/camera_preview_test.dart
@@ -133,8 +133,11 @@
isInitialized: true,
isRecordingVideo: true,
deviceOrientation: DeviceOrientation.portraitUp,
- lockedCaptureOrientation: DeviceOrientation.landscapeRight,
- recordingOrientation: DeviceOrientation.landscapeLeft,
+ lockedCaptureOrientation:
+ const Optional<DeviceOrientation>.fromNullable(
+ DeviceOrientation.landscapeRight),
+ recordingOrientation: const Optional<DeviceOrientation>.fromNullable(
+ DeviceOrientation.landscapeLeft),
previewSize: const Size(480, 640),
);
@@ -164,8 +167,11 @@
controller.value = controller.value.copyWith(
isInitialized: true,
deviceOrientation: DeviceOrientation.portraitUp,
- lockedCaptureOrientation: DeviceOrientation.landscapeRight,
- recordingOrientation: DeviceOrientation.landscapeLeft,
+ lockedCaptureOrientation:
+ const Optional<DeviceOrientation>.fromNullable(
+ DeviceOrientation.landscapeRight),
+ recordingOrientation: const Optional<DeviceOrientation>.fromNullable(
+ DeviceOrientation.landscapeLeft),
previewSize: const Size(480, 640),
);
@@ -195,7 +201,8 @@
controller.value = controller.value.copyWith(
isInitialized: true,
deviceOrientation: DeviceOrientation.portraitUp,
- recordingOrientation: DeviceOrientation.landscapeLeft,
+ recordingOrientation: const Optional<DeviceOrientation>.fromNullable(
+ DeviceOrientation.landscapeLeft),
previewSize: const Size(480, 640),
);
diff --git a/packages/camera/camera/test/camera_test.dart b/packages/camera/camera/test/camera_test.dart
index 44a48d1..ab8354f 100644
--- a/packages/camera/camera/test/camera_test.dart
+++ b/packages/camera/camera/test/camera_test.dart
@@ -1166,7 +1166,8 @@
cameraController.value = cameraController.value.copyWith(
isPreviewPaused: false,
deviceOrientation: DeviceOrientation.portraitUp,
- lockedCaptureOrientation: DeviceOrientation.landscapeRight);
+ lockedCaptureOrientation:
+ Optional<DeviceOrientation>.of(DeviceOrientation.landscapeRight));
await cameraController.pausePreview();
diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md
index 0aec0be..0cb9957 100644
--- a/packages/camera/camera_android/CHANGELOG.md
+++ b/packages/camera/camera_android/CHANGELOG.md
@@ -1,5 +1,6 @@
-## NEXT
+## 0.10.3
+* Adds back use of Optional type.
* Updates minimum Flutter version to 3.0.
## 0.10.2+3
diff --git a/packages/camera/camera_android/example/lib/camera_controller.dart b/packages/camera/camera_android/example/lib/camera_controller.dart
index 79bf4e8..8139dcd 100644
--- a/packages/camera/camera_android/example/lib/camera_controller.dart
+++ b/packages/camera/camera_android/example/lib/camera_controller.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
+import 'dart:collection';
import 'package:camera_platform_interface/camera_platform_interface.dart';
import 'package:flutter/foundation.dart';
@@ -108,10 +109,10 @@
bool? exposurePointSupported,
bool? focusPointSupported,
DeviceOrientation? deviceOrientation,
- DeviceOrientation? lockedCaptureOrientation,
- DeviceOrientation? recordingOrientation,
+ Optional<DeviceOrientation>? lockedCaptureOrientation,
+ Optional<DeviceOrientation>? recordingOrientation,
bool? isPreviewPaused,
- DeviceOrientation? previewPauseOrientation,
+ Optional<DeviceOrientation>? previewPauseOrientation,
}) {
return CameraValue(
isInitialized: isInitialized ?? this.isInitialized,
@@ -124,12 +125,16 @@
exposureMode: exposureMode ?? this.exposureMode,
focusMode: focusMode ?? this.focusMode,
deviceOrientation: deviceOrientation ?? this.deviceOrientation,
- lockedCaptureOrientation:
- lockedCaptureOrientation ?? this.lockedCaptureOrientation,
- recordingOrientation: recordingOrientation ?? this.recordingOrientation,
+ lockedCaptureOrientation: lockedCaptureOrientation == null
+ ? this.lockedCaptureOrientation
+ : lockedCaptureOrientation.orNull,
+ recordingOrientation: recordingOrientation == null
+ ? this.recordingOrientation
+ : recordingOrientation.orNull,
isPreviewPaused: isPreviewPaused ?? this.isPreviewPaused,
- previewPauseOrientation:
- previewPauseOrientation ?? this.previewPauseOrientation,
+ previewPauseOrientation: previewPauseOrientation == null
+ ? this.previewPauseOrientation
+ : previewPauseOrientation.orNull,
);
}
@@ -257,14 +262,16 @@
await CameraPlatform.instance.pausePreview(_cameraId);
value = value.copyWith(
isPreviewPaused: true,
- previewPauseOrientation:
- value.lockedCaptureOrientation ?? value.deviceOrientation);
+ previewPauseOrientation: Optional<DeviceOrientation>.of(
+ value.lockedCaptureOrientation ?? value.deviceOrientation));
}
/// Resumes the current camera preview
Future<void> resumePreview() async {
await CameraPlatform.instance.resumePreview(_cameraId);
- value = value.copyWith(isPreviewPaused: false);
+ value = value.copyWith(
+ isPreviewPaused: false,
+ previewPauseOrientation: const Optional<DeviceOrientation>.absent());
}
/// Captures an image and returns the file where it was saved.
@@ -307,8 +314,8 @@
isRecordingVideo: true,
isRecordingPaused: false,
isStreamingImages: streamCallback != null,
- recordingOrientation:
- value.lockedCaptureOrientation ?? value.deviceOrientation);
+ recordingOrientation: Optional<DeviceOrientation>.of(
+ value.lockedCaptureOrientation ?? value.deviceOrientation));
}
/// Stops the video recording and returns the file where it was saved.
@@ -324,6 +331,7 @@
value = value.copyWith(
isRecordingVideo: false,
isRecordingPaused: false,
+ recordingOrientation: const Optional<DeviceOrientation>.absent(),
);
return file;
}
@@ -392,12 +400,16 @@
Future<void> lockCaptureOrientation() async {
await CameraPlatform.instance
.lockCaptureOrientation(_cameraId, value.deviceOrientation);
- value = value.copyWith(lockedCaptureOrientation: value.deviceOrientation);
+ value = value.copyWith(
+ lockedCaptureOrientation:
+ Optional<DeviceOrientation>.of(value.deviceOrientation));
}
/// Unlocks the capture orientation.
Future<void> unlockCaptureOrientation() async {
await CameraPlatform.instance.unlockCaptureOrientation(_cameraId);
+ value = value.copyWith(
+ lockedCaptureOrientation: const Optional<DeviceOrientation>.absent());
}
/// Sets the focus mode for taking pictures.
@@ -431,3 +443,112 @@
}
}
}
+
+/// A value that might be absent.
+///
+/// Used to represent [DeviceOrientation]s that are optional but also able
+/// to be cleared.
+@immutable
+class Optional<T> extends IterableBase<T> {
+ /// Constructs an empty Optional.
+ const Optional.absent() : _value = null;
+
+ /// Constructs an Optional of the given [value].
+ ///
+ /// Throws [ArgumentError] if [value] is null.
+ Optional.of(T value) : _value = value {
+ // TODO(cbracken): Delete and make this ctor const once mixed-mode
+ // execution is no longer around.
+ ArgumentError.checkNotNull(value);
+ }
+
+ /// Constructs an Optional of the given [value].
+ ///
+ /// If [value] is null, returns [absent()].
+ const Optional.fromNullable(T? value) : _value = value;
+
+ final T? _value;
+
+ /// True when this optional contains a value.
+ bool get isPresent => _value != null;
+
+ /// True when this optional contains no value.
+ bool get isNotPresent => _value == null;
+
+ /// Gets the Optional value.
+ ///
+ /// Throws [StateError] if [value] is null.
+ T get value {
+ if (_value == null) {
+ throw StateError('value called on absent Optional.');
+ }
+ return _value!;
+ }
+
+ /// Executes a function if the Optional value is present.
+ void ifPresent(void Function(T value) ifPresent) {
+ if (isPresent) {
+ ifPresent(_value as T);
+ }
+ }
+
+ /// Execution a function if the Optional value is absent.
+ void ifAbsent(void Function() ifAbsent) {
+ if (!isPresent) {
+ ifAbsent();
+ }
+ }
+
+ /// Gets the Optional value with a default.
+ ///
+ /// The default is returned if the Optional is [absent()].
+ ///
+ /// Throws [ArgumentError] if [defaultValue] is null.
+ T or(T defaultValue) {
+ return _value ?? defaultValue;
+ }
+
+ /// Gets the Optional value, or `null` if there is none.
+ T? get orNull => _value;
+
+ /// Transforms the Optional value.
+ ///
+ /// If the Optional is [absent()], returns [absent()] without applying the transformer.
+ ///
+ /// The transformer must not return `null`. If it does, an [ArgumentError] is thrown.
+ Optional<S> transform<S>(S Function(T value) transformer) {
+ return _value == null
+ ? Optional<S>.absent()
+ : Optional<S>.of(transformer(_value as T));
+ }
+
+ /// Transforms the Optional value.
+ ///
+ /// If the Optional is [absent()], returns [absent()] without applying the transformer.
+ ///
+ /// Returns [absent()] if the transformer returns `null`.
+ Optional<S> transformNullable<S>(S? Function(T value) transformer) {
+ return _value == null
+ ? Optional<S>.absent()
+ : Optional<S>.fromNullable(transformer(_value as T));
+ }
+
+ @override
+ Iterator<T> get iterator =>
+ isPresent ? <T>[_value as T].iterator : Iterable<T>.empty().iterator;
+
+ /// Delegates to the underlying [value] hashCode.
+ @override
+ int get hashCode => _value.hashCode;
+
+ /// Delegates to the underlying [value] operator==.
+ @override
+ bool operator ==(Object o) => o is Optional<T> && o._value == _value;
+
+ @override
+ String toString() {
+ return _value == null
+ ? 'Optional { absent }'
+ : 'Optional { value: $_value }';
+ }
+}
diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml
index 30d6153..fed2d29 100644
--- a/packages/camera/camera_android/pubspec.yaml
+++ b/packages/camera/camera_android/pubspec.yaml
@@ -2,7 +2,7 @@
description: Android implementation of the camera plugin.
repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
-version: 0.10.2+3
+version: 0.10.3
environment:
sdk: ">=2.14.0 <3.0.0"
diff --git a/packages/camera/camera_avfoundation/CHANGELOG.md b/packages/camera/camera_avfoundation/CHANGELOG.md
index caa69b6..f0605b7 100644
--- a/packages/camera/camera_avfoundation/CHANGELOG.md
+++ b/packages/camera/camera_avfoundation/CHANGELOG.md
@@ -1,5 +1,6 @@
-## NEXT
+## 0.9.11
+* Adds back use of Optional type.
* Updates minimum Flutter version to 3.0.
## 0.9.10+2
diff --git a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart
index 47c1f6f..5241868 100644
--- a/packages/camera/camera_avfoundation/example/lib/camera_controller.dart
+++ b/packages/camera/camera_avfoundation/example/lib/camera_controller.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
+import 'dart:collection';
import 'package:camera_platform_interface/camera_platform_interface.dart';
import 'package:flutter/foundation.dart';
@@ -108,10 +109,10 @@
bool? exposurePointSupported,
bool? focusPointSupported,
DeviceOrientation? deviceOrientation,
- DeviceOrientation? lockedCaptureOrientation,
- DeviceOrientation? recordingOrientation,
+ Optional<DeviceOrientation>? lockedCaptureOrientation,
+ Optional<DeviceOrientation>? recordingOrientation,
bool? isPreviewPaused,
- DeviceOrientation? previewPauseOrientation,
+ Optional<DeviceOrientation>? previewPauseOrientation,
}) {
return CameraValue(
isInitialized: isInitialized ?? this.isInitialized,
@@ -124,12 +125,16 @@
exposureMode: exposureMode ?? this.exposureMode,
focusMode: focusMode ?? this.focusMode,
deviceOrientation: deviceOrientation ?? this.deviceOrientation,
- lockedCaptureOrientation:
- lockedCaptureOrientation ?? this.lockedCaptureOrientation,
- recordingOrientation: recordingOrientation ?? this.recordingOrientation,
+ lockedCaptureOrientation: lockedCaptureOrientation == null
+ ? this.lockedCaptureOrientation
+ : lockedCaptureOrientation.orNull,
+ recordingOrientation: recordingOrientation == null
+ ? this.recordingOrientation
+ : recordingOrientation.orNull,
isPreviewPaused: isPreviewPaused ?? this.isPreviewPaused,
- previewPauseOrientation:
- previewPauseOrientation ?? this.previewPauseOrientation,
+ previewPauseOrientation: previewPauseOrientation == null
+ ? this.previewPauseOrientation
+ : previewPauseOrientation.orNull,
);
}
@@ -257,14 +262,16 @@
await CameraPlatform.instance.pausePreview(_cameraId);
value = value.copyWith(
isPreviewPaused: true,
- previewPauseOrientation:
- value.lockedCaptureOrientation ?? value.deviceOrientation);
+ previewPauseOrientation: Optional<DeviceOrientation>.of(
+ value.lockedCaptureOrientation ?? value.deviceOrientation));
}
/// Resumes the current camera preview
Future<void> resumePreview() async {
await CameraPlatform.instance.resumePreview(_cameraId);
- value = value.copyWith(isPreviewPaused: false);
+ value = value.copyWith(
+ isPreviewPaused: false,
+ previewPauseOrientation: const Optional<DeviceOrientation>.absent());
}
/// Captures an image and returns the file where it was saved.
@@ -307,8 +314,8 @@
isRecordingVideo: true,
isRecordingPaused: false,
isStreamingImages: streamCallback != null,
- recordingOrientation:
- value.lockedCaptureOrientation ?? value.deviceOrientation);
+ recordingOrientation: Optional<DeviceOrientation>.of(
+ value.lockedCaptureOrientation ?? value.deviceOrientation));
}
/// Stops the video recording and returns the file where it was saved.
@@ -321,7 +328,10 @@
final XFile file =
await CameraPlatform.instance.stopVideoRecording(_cameraId);
- value = value.copyWith(isRecordingVideo: false);
+ value = value.copyWith(
+ isRecordingVideo: false,
+ recordingOrientation: const Optional<DeviceOrientation>.absent(),
+ );
return file;
}
@@ -389,12 +399,16 @@
Future<void> lockCaptureOrientation() async {
await CameraPlatform.instance
.lockCaptureOrientation(_cameraId, value.deviceOrientation);
- value = value.copyWith(lockedCaptureOrientation: value.deviceOrientation);
+ value = value.copyWith(
+ lockedCaptureOrientation:
+ Optional<DeviceOrientation>.of(value.deviceOrientation));
}
/// Unlocks the capture orientation.
Future<void> unlockCaptureOrientation() async {
await CameraPlatform.instance.unlockCaptureOrientation(_cameraId);
+ value = value.copyWith(
+ lockedCaptureOrientation: const Optional<DeviceOrientation>.absent());
}
/// Sets the focus mode for taking pictures.
@@ -428,3 +442,112 @@
}
}
}
+
+/// A value that might be absent.
+///
+/// Used to represent [DeviceOrientation]s that are optional but also able
+/// to be cleared.
+@immutable
+class Optional<T> extends IterableBase<T> {
+ /// Constructs an empty Optional.
+ const Optional.absent() : _value = null;
+
+ /// Constructs an Optional of the given [value].
+ ///
+ /// Throws [ArgumentError] if [value] is null.
+ Optional.of(T value) : _value = value {
+ // TODO(cbracken): Delete and make this ctor const once mixed-mode
+ // execution is no longer around.
+ ArgumentError.checkNotNull(value);
+ }
+
+ /// Constructs an Optional of the given [value].
+ ///
+ /// If [value] is null, returns [absent()].
+ const Optional.fromNullable(T? value) : _value = value;
+
+ final T? _value;
+
+ /// True when this optional contains a value.
+ bool get isPresent => _value != null;
+
+ /// True when this optional contains no value.
+ bool get isNotPresent => _value == null;
+
+ /// Gets the Optional value.
+ ///
+ /// Throws [StateError] if [value] is null.
+ T get value {
+ if (_value == null) {
+ throw StateError('value called on absent Optional.');
+ }
+ return _value!;
+ }
+
+ /// Executes a function if the Optional value is present.
+ void ifPresent(void Function(T value) ifPresent) {
+ if (isPresent) {
+ ifPresent(_value as T);
+ }
+ }
+
+ /// Execution a function if the Optional value is absent.
+ void ifAbsent(void Function() ifAbsent) {
+ if (!isPresent) {
+ ifAbsent();
+ }
+ }
+
+ /// Gets the Optional value with a default.
+ ///
+ /// The default is returned if the Optional is [absent()].
+ ///
+ /// Throws [ArgumentError] if [defaultValue] is null.
+ T or(T defaultValue) {
+ return _value ?? defaultValue;
+ }
+
+ /// Gets the Optional value, or `null` if there is none.
+ T? get orNull => _value;
+
+ /// Transforms the Optional value.
+ ///
+ /// If the Optional is [absent()], returns [absent()] without applying the transformer.
+ ///
+ /// The transformer must not return `null`. If it does, an [ArgumentError] is thrown.
+ Optional<S> transform<S>(S Function(T value) transformer) {
+ return _value == null
+ ? Optional<S>.absent()
+ : Optional<S>.of(transformer(_value as T));
+ }
+
+ /// Transforms the Optional value.
+ ///
+ /// If the Optional is [absent()], returns [absent()] without applying the transformer.
+ ///
+ /// Returns [absent()] if the transformer returns `null`.
+ Optional<S> transformNullable<S>(S? Function(T value) transformer) {
+ return _value == null
+ ? Optional<S>.absent()
+ : Optional<S>.fromNullable(transformer(_value as T));
+ }
+
+ @override
+ Iterator<T> get iterator =>
+ isPresent ? <T>[_value as T].iterator : Iterable<T>.empty().iterator;
+
+ /// Delegates to the underlying [value] hashCode.
+ @override
+ int get hashCode => _value.hashCode;
+
+ /// Delegates to the underlying [value] operator==.
+ @override
+ bool operator ==(Object o) => o is Optional<T> && o._value == _value;
+
+ @override
+ String toString() {
+ return _value == null
+ ? 'Optional { absent }'
+ : 'Optional { value: $_value }';
+ }
+}
diff --git a/packages/camera/camera_avfoundation/pubspec.yaml b/packages/camera/camera_avfoundation/pubspec.yaml
index 9461d93..b272a4c 100644
--- a/packages/camera/camera_avfoundation/pubspec.yaml
+++ b/packages/camera/camera_avfoundation/pubspec.yaml
@@ -2,7 +2,7 @@
description: iOS implementation of the camera plugin.
repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera_avfoundation
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
-version: 0.9.10+2
+version: 0.9.11
environment:
sdk: ">=2.14.0 <3.0.0"