Skip to content

Commit ff84c44

Browse files
authored
[camera] Add back Optional type for nullable CameraController orientations (flutter#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 (flutter#6870)" This reverts commit 3d8b73b. * Add back optional * Edit changelog * Fix semicolon * Add )
1 parent af065a6 commit ff84c44

File tree

11 files changed

+436
-57
lines changed

11 files changed

+436
-57
lines changed

packages/camera/camera/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.10.3
2+
3+
* Adds back use of Optional type.
4+
15
## 0.10.2+1
26

37
* Updates code for stricter lint checks.

packages/camera/camera/lib/src/camera_controller.dart

Lines changed: 138 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:collection';
67
import 'dart:math';
78

89
import 'package:camera_platform_interface/camera_platform_interface.dart';
@@ -160,10 +161,10 @@ class CameraValue {
160161
bool? exposurePointSupported,
161162
bool? focusPointSupported,
162163
DeviceOrientation? deviceOrientation,
163-
DeviceOrientation? lockedCaptureOrientation,
164-
DeviceOrientation? recordingOrientation,
164+
Optional<DeviceOrientation>? lockedCaptureOrientation,
165+
Optional<DeviceOrientation>? recordingOrientation,
165166
bool? isPreviewPaused,
166-
DeviceOrientation? previewPauseOrientation,
167+
Optional<DeviceOrientation>? previewPauseOrientation,
167168
}) {
168169
return CameraValue(
169170
isInitialized: isInitialized ?? this.isInitialized,
@@ -180,12 +181,16 @@ class CameraValue {
180181
exposurePointSupported ?? this.exposurePointSupported,
181182
focusPointSupported: focusPointSupported ?? this.focusPointSupported,
182183
deviceOrientation: deviceOrientation ?? this.deviceOrientation,
183-
lockedCaptureOrientation:
184-
lockedCaptureOrientation ?? this.lockedCaptureOrientation,
185-
recordingOrientation: recordingOrientation ?? this.recordingOrientation,
184+
lockedCaptureOrientation: lockedCaptureOrientation == null
185+
? this.lockedCaptureOrientation
186+
: lockedCaptureOrientation.orNull,
187+
recordingOrientation: recordingOrientation == null
188+
? this.recordingOrientation
189+
: recordingOrientation.orNull,
186190
isPreviewPaused: isPreviewPaused ?? this.isPreviewPaused,
187-
previewPauseOrientation:
188-
previewPauseOrientation ?? this.previewPauseOrientation,
191+
previewPauseOrientation: previewPauseOrientation == null
192+
? this.previewPauseOrientation
193+
: previewPauseOrientation.orNull,
189194
);
190195
}
191196

@@ -353,8 +358,8 @@ class CameraController extends ValueNotifier<CameraValue> {
353358
await CameraPlatform.instance.pausePreview(_cameraId);
354359
value = value.copyWith(
355360
isPreviewPaused: true,
356-
previewPauseOrientation:
357-
value.lockedCaptureOrientation ?? value.deviceOrientation);
361+
previewPauseOrientation: Optional<DeviceOrientation>.of(
362+
value.lockedCaptureOrientation ?? value.deviceOrientation));
358363
} on PlatformException catch (e) {
359364
throw CameraException(e.code, e.message);
360365
}
@@ -367,7 +372,9 @@ class CameraController extends ValueNotifier<CameraValue> {
367372
}
368373
try {
369374
await CameraPlatform.instance.resumePreview(_cameraId);
370-
value = value.copyWith(isPreviewPaused: false);
375+
value = value.copyWith(
376+
isPreviewPaused: false,
377+
previewPauseOrientation: const Optional<DeviceOrientation>.absent());
371378
} on PlatformException catch (e) {
372379
throw CameraException(e.code, e.message);
373380
}
@@ -498,9 +505,9 @@ class CameraController extends ValueNotifier<CameraValue> {
498505
value = value.copyWith(
499506
isRecordingVideo: true,
500507
isRecordingPaused: false,
501-
isStreamingImages: onAvailable != null,
502-
recordingOrientation:
503-
value.lockedCaptureOrientation ?? value.deviceOrientation);
508+
recordingOrientation: Optional<DeviceOrientation>.of(
509+
value.lockedCaptureOrientation ?? value.deviceOrientation),
510+
isStreamingImages: onAvailable != null);
504511
} on PlatformException catch (e) {
505512
throw CameraException(e.code, e.message);
506513
}
@@ -525,7 +532,10 @@ class CameraController extends ValueNotifier<CameraValue> {
525532
try {
526533
final XFile file =
527534
await CameraPlatform.instance.stopVideoRecording(_cameraId);
528-
value = value.copyWith(isRecordingVideo: false);
535+
value = value.copyWith(
536+
isRecordingVideo: false,
537+
recordingOrientation: const Optional<DeviceOrientation>.absent(),
538+
);
529539
return file;
530540
} on PlatformException catch (e) {
531541
throw CameraException(e.code, e.message);
@@ -743,7 +753,8 @@ class CameraController extends ValueNotifier<CameraValue> {
743753
await CameraPlatform.instance.lockCaptureOrientation(
744754
_cameraId, orientation ?? value.deviceOrientation);
745755
value = value.copyWith(
746-
lockedCaptureOrientation: orientation ?? value.deviceOrientation);
756+
lockedCaptureOrientation: Optional<DeviceOrientation>.of(
757+
orientation ?? value.deviceOrientation));
747758
} on PlatformException catch (e) {
748759
throw CameraException(e.code, e.message);
749760
}
@@ -763,7 +774,8 @@ class CameraController extends ValueNotifier<CameraValue> {
763774
Future<void> unlockCaptureOrientation() async {
764775
try {
765776
await CameraPlatform.instance.unlockCaptureOrientation(_cameraId);
766-
value = value.copyWith();
777+
value = value.copyWith(
778+
lockedCaptureOrientation: const Optional<DeviceOrientation>.absent());
767779
} on PlatformException catch (e) {
768780
throw CameraException(e.code, e.message);
769781
}
@@ -834,3 +846,112 @@ class CameraController extends ValueNotifier<CameraValue> {
834846
}
835847
}
836848
}
849+
850+
/// A value that might be absent.
851+
///
852+
/// Used to represent [DeviceOrientation]s that are optional but also able
853+
/// to be cleared.
854+
@immutable
855+
class Optional<T> extends IterableBase<T> {
856+
/// Constructs an empty Optional.
857+
const Optional.absent() : _value = null;
858+
859+
/// Constructs an Optional of the given [value].
860+
///
861+
/// Throws [ArgumentError] if [value] is null.
862+
Optional.of(T value) : _value = value {
863+
// TODO(cbracken): Delete and make this ctor const once mixed-mode
864+
// execution is no longer around.
865+
ArgumentError.checkNotNull(value);
866+
}
867+
868+
/// Constructs an Optional of the given [value].
869+
///
870+
/// If [value] is null, returns [absent()].
871+
const Optional.fromNullable(T? value) : _value = value;
872+
873+
final T? _value;
874+
875+
/// True when this optional contains a value.
876+
bool get isPresent => _value != null;
877+
878+
/// True when this optional contains no value.
879+
bool get isNotPresent => _value == null;
880+
881+
/// Gets the Optional value.
882+
///
883+
/// Throws [StateError] if [value] is null.
884+
T get value {
885+
if (_value == null) {
886+
throw StateError('value called on absent Optional.');
887+
}
888+
return _value!;
889+
}
890+
891+
/// Executes a function if the Optional value is present.
892+
void ifPresent(void Function(T value) ifPresent) {
893+
if (isPresent) {
894+
ifPresent(_value as T);
895+
}
896+
}
897+
898+
/// Execution a function if the Optional value is absent.
899+
void ifAbsent(void Function() ifAbsent) {
900+
if (!isPresent) {
901+
ifAbsent();
902+
}
903+
}
904+
905+
/// Gets the Optional value with a default.
906+
///
907+
/// The default is returned if the Optional is [absent()].
908+
///
909+
/// Throws [ArgumentError] if [defaultValue] is null.
910+
T or(T defaultValue) {
911+
return _value ?? defaultValue;
912+
}
913+
914+
/// Gets the Optional value, or `null` if there is none.
915+
T? get orNull => _value;
916+
917+
/// Transforms the Optional value.
918+
///
919+
/// If the Optional is [absent()], returns [absent()] without applying the transformer.
920+
///
921+
/// The transformer must not return `null`. If it does, an [ArgumentError] is thrown.
922+
Optional<S> transform<S>(S Function(T value) transformer) {
923+
return _value == null
924+
? Optional<S>.absent()
925+
: Optional<S>.of(transformer(_value as T));
926+
}
927+
928+
/// Transforms the Optional value.
929+
///
930+
/// If the Optional is [absent()], returns [absent()] without applying the transformer.
931+
///
932+
/// Returns [absent()] if the transformer returns `null`.
933+
Optional<S> transformNullable<S>(S? Function(T value) transformer) {
934+
return _value == null
935+
? Optional<S>.absent()
936+
: Optional<S>.fromNullable(transformer(_value as T));
937+
}
938+
939+
@override
940+
Iterator<T> get iterator =>
941+
isPresent ? <T>[_value as T].iterator : Iterable<T>.empty().iterator;
942+
943+
/// Delegates to the underlying [value] hashCode.
944+
@override
945+
int get hashCode => _value.hashCode;
946+
947+
/// Delegates to the underlying [value] operator==.
948+
@override
949+
bool operator ==(Object o) => o is Optional<T> && o._value == _value;
950+
951+
@override
952+
String toString() {
953+
return _value == null
954+
? 'Optional { absent }'
955+
: 'Optional { value: $_value }';
956+
}
957+
}

packages/camera/camera/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ description: A Flutter plugin for controlling the camera. Supports previewing
44
Dart.
55
repository: https://github.com/flutter/plugins/tree/main/packages/camera/camera
66
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
7-
version: 0.10.2+1
7+
version: 0.10.3
88

99
environment:
1010
sdk: ">=2.14.0 <3.0.0"

packages/camera/camera/test/camera_preview_test.dart

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,11 @@ void main() {
133133
isInitialized: true,
134134
isRecordingVideo: true,
135135
deviceOrientation: DeviceOrientation.portraitUp,
136-
lockedCaptureOrientation: DeviceOrientation.landscapeRight,
137-
recordingOrientation: DeviceOrientation.landscapeLeft,
136+
lockedCaptureOrientation:
137+
const Optional<DeviceOrientation>.fromNullable(
138+
DeviceOrientation.landscapeRight),
139+
recordingOrientation: const Optional<DeviceOrientation>.fromNullable(
140+
DeviceOrientation.landscapeLeft),
138141
previewSize: const Size(480, 640),
139142
);
140143

@@ -164,8 +167,11 @@ void main() {
164167
controller.value = controller.value.copyWith(
165168
isInitialized: true,
166169
deviceOrientation: DeviceOrientation.portraitUp,
167-
lockedCaptureOrientation: DeviceOrientation.landscapeRight,
168-
recordingOrientation: DeviceOrientation.landscapeLeft,
170+
lockedCaptureOrientation:
171+
const Optional<DeviceOrientation>.fromNullable(
172+
DeviceOrientation.landscapeRight),
173+
recordingOrientation: const Optional<DeviceOrientation>.fromNullable(
174+
DeviceOrientation.landscapeLeft),
169175
previewSize: const Size(480, 640),
170176
);
171177

@@ -195,7 +201,8 @@ void main() {
195201
controller.value = controller.value.copyWith(
196202
isInitialized: true,
197203
deviceOrientation: DeviceOrientation.portraitUp,
198-
recordingOrientation: DeviceOrientation.landscapeLeft,
204+
recordingOrientation: const Optional<DeviceOrientation>.fromNullable(
205+
DeviceOrientation.landscapeLeft),
199206
previewSize: const Size(480, 640),
200207
);
201208

packages/camera/camera/test/camera_test.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,8 @@ void main() {
11661166
cameraController.value = cameraController.value.copyWith(
11671167
isPreviewPaused: false,
11681168
deviceOrientation: DeviceOrientation.portraitUp,
1169-
lockedCaptureOrientation: DeviceOrientation.landscapeRight);
1169+
lockedCaptureOrientation:
1170+
Optional<DeviceOrientation>.of(DeviceOrientation.landscapeRight));
11701171

11711172
await cameraController.pausePreview();
11721173

packages/camera/camera_android/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
## NEXT
1+
## 0.10.3
22

3+
* Adds back use of Optional type.
34
* Updates minimum Flutter version to 3.0.
45

56
## 0.10.2+3

0 commit comments

Comments
 (0)