@@ -54,6 +54,7 @@ class CameraValue {
5454 this .recordingOrientation,
5555 this .isPreviewPaused = false ,
5656 this .previewPauseOrientation,
57+ this .videoStabilizationMode = VideoStabilizationMode .off,
5758 }) : _isRecordingPaused = isRecordingPaused;
5859
5960 /// Creates a new camera controller state for an uninitialized controller.
@@ -72,6 +73,7 @@ class CameraValue {
7273 deviceOrientation: DeviceOrientation .portraitUp,
7374 isPreviewPaused: false ,
7475 description: description,
76+ videoStabilizationMode: VideoStabilizationMode .off,
7577 );
7678
7779 /// True after [CameraController.initialize] has completed successfully.
@@ -148,6 +150,9 @@ class CameraValue {
148150 /// The properties of the camera device controlled by this controller.
149151 final CameraDescription description;
150152
153+ /// The video stabilization mode in
154+ final VideoStabilizationMode videoStabilizationMode;
155+
151156 /// Creates a modified copy of the object.
152157 ///
153158 /// Explicitly specified fields get the specified value, all other fields get
@@ -171,6 +176,7 @@ class CameraValue {
171176 bool ? isPreviewPaused,
172177 CameraDescription ? description,
173178 Optional <DeviceOrientation >? previewPauseOrientation,
179+ VideoStabilizationMode ? videoStabilizationMode,
174180 }) {
175181 return CameraValue (
176182 isInitialized: isInitialized ?? this .isInitialized,
@@ -201,6 +207,8 @@ class CameraValue {
201207 previewPauseOrientation == null
202208 ? this .previewPauseOrientation
203209 : previewPauseOrientation.orNull,
210+ videoStabilizationMode:
211+ videoStabilizationMode ?? this .videoStabilizationMode,
204212 );
205213 }
206214
@@ -222,6 +230,7 @@ class CameraValue {
222230 'recordingOrientation: $recordingOrientation , '
223231 'isPreviewPaused: $isPreviewPaused , '
224232 'previewPausedOrientation: $previewPauseOrientation , '
233+ 'videoStabilizationMode: $videoStabilizationMode , '
225234 'description: $description )' ;
226235 }
227236}
@@ -701,6 +710,111 @@ class CameraController extends ValueNotifier<CameraValue> {
701710 }
702711 }
703712
713+ /// Set the video stabilization mode for the selected camera.
714+ ///
715+ /// When [allowFallback] is true (default) the camera will
716+ /// be set to the best video stabilization mode up to,
717+ /// and including, [mode] .
718+ ///
719+ /// When [allowFallback] is false and if
720+ /// [mode] is not one of the supported modes
721+ /// (see [getSupportedVideoStabilizationModes] ),
722+ /// then it throws an [ArgumentError] .
723+ ///
724+ /// This feature is only available on Android
725+ /// (when using camera_android_camerax package)
726+ /// and iOS. It is a no-op on all other platforms.
727+ Future <void > setVideoStabilizationMode (
728+ VideoStabilizationMode mode, {
729+ bool allowFallback = true ,
730+ }) async {
731+ _throwIfNotInitialized ('setVideoStabilizationMode' );
732+ try {
733+ final VideoStabilizationMode ? modeToSet =
734+ await _getVideoStabilizationModeToSet (mode, allowFallback);
735+
736+ // When _getVideoStabilizationModeToSet returns null
737+ // it means that the device doesn't support any
738+ // video stabilization mode and that doing nothing
739+ // is valid because allowFallback is true or [mode]
740+ // is [VideoStabilizationMode.off], so this results
741+ // in a no-op.
742+ if (modeToSet == null ) {
743+ return ;
744+ }
745+ await CameraPlatform .instance.setVideoStabilizationMode (
746+ _cameraId,
747+ modeToSet,
748+ );
749+ value = value.copyWith (videoStabilizationMode: modeToSet);
750+ } on PlatformException catch (e) {
751+ throw CameraException (e.code, e.message);
752+ }
753+ }
754+
755+ Future <VideoStabilizationMode ?> _getVideoStabilizationModeToSet (
756+ VideoStabilizationMode requestedMode,
757+ bool allowFallback,
758+ ) async {
759+ final Iterable <VideoStabilizationMode > supportedModes = await CameraPlatform
760+ .instance
761+ .getSupportedVideoStabilizationModes (_cameraId);
762+
763+ // In this case the device doesn't report any
764+ // available stabilization mode available and
765+ // if either it can fallback or if the requested mode
766+ // is off, then this returns null to signal that
767+ // there is nothing to be done.
768+ if (supportedModes.isEmpty &&
769+ (allowFallback || requestedMode == VideoStabilizationMode .off)) {
770+ return null ;
771+ }
772+
773+ // If it can't fallback and the specific
774+ // requested mode isn't available, then it throws.
775+ if (! allowFallback && ! supportedModes.contains (requestedMode)) {
776+ throw ArgumentError ('Unavailable video stabilization mode.' , 'mode' );
777+ }
778+
779+ // The following assumes that [VideoStabilizationMode.off] will
780+ // always be present if any other level is reported by the device.
781+ // It iterates through all the modes returned by the device,
782+ // looking for the highest mode, up to [mode].
783+ VideoStabilizationMode requestMode = VideoStabilizationMode .off;
784+ for (final VideoStabilizationMode supportedMode in supportedModes) {
785+ if (supportedMode.index <= requestedMode.index &&
786+ supportedMode.index >= requestMode.index) {
787+ requestMode = supportedMode;
788+ }
789+ }
790+
791+ return requestMode;
792+ }
793+
794+ /// Gets a list of video stabilization modes that are supported
795+ /// for the selected camera.
796+ ///
797+ /// Will return the list of supported video stabilization modes
798+ /// on Android (when using camera_android_camerax package) and
799+ /// on iOS.
800+ ///
801+ /// [VideoStabilizationMode.off] will always be listed.
802+ Future <Iterable <VideoStabilizationMode >>
803+ getSupportedVideoStabilizationModes () async {
804+ _throwIfNotInitialized ('isVideoStabilizationModeSupported' );
805+ try {
806+ final Set <VideoStabilizationMode > modes = < VideoStabilizationMode > {
807+ VideoStabilizationMode .off,
808+ ...await CameraPlatform .instance.getSupportedVideoStabilizationModes (
809+ _cameraId,
810+ ),
811+ };
812+ return modes;
813+ } on PlatformException catch (e) {
814+ throw CameraException (e.code, e.message);
815+ }
816+ }
817+
704818 /// Sets the flash mode for taking pictures.
705819 Future <void > setFlashMode (FlashMode mode) async {
706820 try {
0 commit comments