diff --git a/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java b/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java index 1d4bedefbbf4..b967e8fe8935 100644 --- a/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java +++ b/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPlugin.java @@ -199,6 +199,11 @@ public void onMethodCall(MethodCall call, final Result result) { camera.stopVideoRecording(result); break; } + case "setPointOfInterest": + { + result.notImplemented(); + break; + } case "dispose": { if (camera != null) { diff --git a/packages/camera/example/lib/main.dart b/packages/camera/example/lib/main.dart index a0eed3ca7de6..0dec09506020 100644 --- a/packages/camera/example/lib/main.dart +++ b/packages/camera/example/lib/main.dart @@ -95,9 +95,17 @@ class _CameraExampleHomeState extends State { ); } else { return AspectRatio( - aspectRatio: controller.value.aspectRatio, - child: CameraPreview(controller), - ); + aspectRatio: controller.value.aspectRatio, + child: GestureDetector( + child: CameraPreview(controller), + onTapUp: (TapUpDetails details) { + final RenderBox box = context.findRenderObject(); + final Offset localPoint = + box.globalToLocal(details.globalPosition); + final Offset scaledPoint = + localPoint.scale(1 / box.size.width, 1 / box.size.height); + controller.setPointOfInterest(scaledPoint); + })); } } diff --git a/packages/camera/ios/Classes/CameraPlugin.m b/packages/camera/ios/Classes/CameraPlugin.m index 0bfb7515c36c..fc9211e506f0 100644 --- a/packages/camera/ios/Classes/CameraPlugin.m +++ b/packages/camera/ios/Classes/CameraPlugin.m @@ -492,6 +492,28 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result }); [cam start]; } + } else if ([@"setPointOfInterest" isEqualToString:call.method]) { + NSNumber *offsetX = call.arguments[@"offsetX"]; + NSNumber *offsetY = call.arguments[@"offsetY"]; + + NSError *error = nil; + [_camera.captureDevice lockForConfiguration:&error]; + if (error) { + result([error flutterError]); + } else { + if ([_camera.captureDevice isFocusPointOfInterestSupported]) { + _camera.captureDevice.focusPointOfInterest = + CGPointMake(offsetY.floatValue, 1 - offsetX.floatValue); + [_camera.captureDevice setFocusMode:AVCaptureFocusModeContinuousAutoFocus]; + } + if ([_camera.captureDevice isExposurePointOfInterestSupported]) { + _camera.captureDevice.exposurePointOfInterest = + CGPointMake(offsetY.floatValue, 1 - offsetX.floatValue); + [_camera.captureDevice setExposureMode:AVCaptureExposureModeContinuousAutoExposure]; + } + [_camera.captureDevice unlockForConfiguration]; + result(@{}); + } } else { NSDictionary *argsMap = call.arguments; NSUInteger textureId = ((NSNumber *)argsMap[@"textureId"]).unsignedIntegerValue; diff --git a/packages/camera/lib/camera.dart b/packages/camera/lib/camera.dart index 115245969ed9..192271b80b93 100644 --- a/packages/camera/lib/camera.dart +++ b/packages/camera/lib/camera.dart @@ -335,6 +335,26 @@ class CameraController extends ValueNotifier { } } + /// Sets the auto focus and auto exposure point. + /// + /// Throws a [CameraException] if the call fails. + Future setPointOfInterest(Offset offset) async { + if (!value.isInitialized || _isDisposed) { + throw CameraException( + 'Uninitialized CameraController.', + 'takePicture was called on uninitialized CameraController', + ); + } + try { + await _channel.invokeMethod( + 'setPointOfInterest', + {'offsetX': offset.dx, 'offsetY': offset.dy}, + ); + } on PlatformException catch (e) { + throw CameraException(e.code, e.message); + } + } + /// Releases the resources of this camera. @override Future dispose() async {