[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"