[camerax] Implements torch mode (#4903)

Implements the torch flash mode. Also wraps classes necessary for the implementation (a method in `Camera`, `CameraControl`).

Fixes https://github.com/flutter/flutter/issues/120715.
Fixes https://github.com/flutter/flutter/issues/115846.
Part of https://github.com/flutter/flutter/issues/115847.
diff --git a/packages/camera/camera_android_camerax/CHANGELOG.md b/packages/camera/camera_android_camerax/CHANGELOG.md
index 6db9956..995e917 100644
--- a/packages/camera/camera_android_camerax/CHANGELOG.md
+++ b/packages/camera/camera_android_camerax/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.5.0+19
+
+* Implements torch flash mode.
+
 ## 0.5.0+18
 
 * Implements `startVideoCapturing`.
diff --git a/packages/camera/camera_android_camerax/README.md b/packages/camera/camera_android_camerax/README.md
index 63c0bf5..a9a3ddc 100644
--- a/packages/camera/camera_android_camerax/README.md
+++ b/packages/camera/camera_android_camerax/README.md
@@ -35,10 +35,6 @@
 
 `lockCaptureOrientation` & `unLockCaptureOrientation` are unimplemented.
 
-### Torch mode \[[Issue #120715][120715]\]
-
-Calling `setFlashMode` with mode `FlashMode.torch` currently does nothing.
-
 ### Exposure mode, point, & offset configuration \[[Issue #120468][120468]\]
 
 `setExposureMode`, `setExposurePoint`, & `setExposureOffset` are unimplemented.
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java
index d2a9c27..eeb3c02 100644
--- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraAndroidCameraxPlugin.java
@@ -25,6 +25,7 @@
   private VideoCaptureHostApiImpl videoCaptureHostApiImpl;
   private ImageAnalysisHostApiImpl imageAnalysisHostApiImpl;
   private ImageCaptureHostApiImpl imageCaptureHostApiImpl;
+  private CameraControlHostApiImpl cameraControlHostApiImpl;
   public @Nullable SystemServicesHostApiImpl systemServicesHostApiImpl;
 
   @VisibleForTesting
@@ -81,7 +82,8 @@
     GeneratedCameraXLibrary.LiveDataHostApi.setup(binaryMessenger, liveDataHostApiImpl);
     GeneratedCameraXLibrary.ObserverHostApi.setup(
         binaryMessenger, new ObserverHostApiImpl(binaryMessenger, instanceManager));
-    imageAnalysisHostApiImpl = new ImageAnalysisHostApiImpl(binaryMessenger, instanceManager);
+    imageAnalysisHostApiImpl =
+        new ImageAnalysisHostApiImpl(binaryMessenger, instanceManager, context);
     GeneratedCameraXLibrary.ImageAnalysisHostApi.setup(binaryMessenger, imageAnalysisHostApiImpl);
     GeneratedCameraXLibrary.AnalyzerHostApi.setup(
         binaryMessenger, new AnalyzerHostApiImpl(binaryMessenger, instanceManager));
@@ -107,6 +109,8 @@
         binaryMessenger, new FallbackStrategyHostApiImpl(instanceManager));
     GeneratedCameraXLibrary.QualitySelectorHostApi.setup(
         binaryMessenger, new QualitySelectorHostApiImpl(instanceManager));
+    cameraControlHostApiImpl = new CameraControlHostApiImpl(instanceManager, context);
+    GeneratedCameraXLibrary.CameraControlHostApi.setup(binaryMessenger, cameraControlHostApiImpl);
   }
 
   @Override
@@ -128,7 +132,6 @@
     Activity activity = activityPluginBinding.getActivity();
 
     setUp(pluginBinding.getBinaryMessenger(), activity, pluginBinding.getTextureRegistry());
-    updateContext(activity);
 
     if (activity instanceof LifecycleOwner) {
       processCameraProviderHostApiImpl.setLifecycleOwner((LifecycleOwner) activity);
@@ -183,5 +186,8 @@
     if (imageAnalysisHostApiImpl != null) {
       imageAnalysisHostApiImpl.setContext(context);
     }
+    if (cameraControlHostApiImpl != null) {
+      cameraControlHostApiImpl.setContext(context);
+    }
   }
 }
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlFlutterApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlFlutterApiImpl.java
new file mode 100644
index 0000000..1b61618
--- /dev/null
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlFlutterApiImpl.java
@@ -0,0 +1,29 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.camerax;
+
+import androidx.annotation.NonNull;
+import androidx.camera.core.CameraControl;
+import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraControlFlutterApi;
+
+public class CameraControlFlutterApiImpl extends CameraControlFlutterApi {
+  private final @NonNull InstanceManager instanceManager;
+
+  public CameraControlFlutterApiImpl(
+      @NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
+    super(binaryMessenger);
+    this.instanceManager = instanceManager;
+  }
+
+  /**
+   * Creates a {@link CameraControl} instance in Dart. {@code reply} is not used so it can be empty.
+   */
+  void create(CameraControl cameraControl, Reply<Void> reply) {
+    if (!instanceManager.containsInstance(cameraControl)) {
+      create(instanceManager.addHostCreatedInstance(cameraControl), reply);
+    }
+  }
+}
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlHostApiImpl.java
new file mode 100644
index 0000000..8241fda
--- /dev/null
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraControlHostApiImpl.java
@@ -0,0 +1,101 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.camerax;
+
+import android.content.Context;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.camera.core.CameraControl;
+import androidx.core.content.ContextCompat;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraControlHostApi;
+import java.util.Objects;
+
+/**
+ * Host API implementation for {@link CameraControl}.
+ *
+ * <p>This class handles instantiating and adding native object instances that are attached to a
+ * Dart instance or handle method calls on the associated native class or an instance of the class.
+ */
+public class CameraControlHostApiImpl implements CameraControlHostApi {
+  private final InstanceManager instanceManager;
+  private final CameraControlProxy proxy;
+
+  /** Proxy for constructors and static method of {@link CameraControl}. */
+  @VisibleForTesting
+  public static class CameraControlProxy {
+    Context context;
+
+    /** Enables or disables the torch of the specified {@link CameraControl} instance. */
+    @NonNull
+    public void enableTorch(
+        @NonNull CameraControl cameraControl,
+        @NonNull Boolean torch,
+        @NonNull GeneratedCameraXLibrary.Result<Void> result) {
+      ListenableFuture<Void> enableTorchFuture = cameraControl.enableTorch(torch);
+
+      Futures.addCallback(
+          enableTorchFuture,
+          new FutureCallback<Void>() {
+            public void onSuccess(Void voidResult) {
+              result.success(null);
+            }
+
+            public void onFailure(Throwable t) {
+              result.error(t);
+            }
+          },
+          ContextCompat.getMainExecutor(context));
+    }
+  }
+
+  /**
+   * Constructs an {@link CameraControlHostApiImpl}.
+   *
+   * @param instanceManager maintains instances stored to communicate with attached Dart objects
+   */
+  public CameraControlHostApiImpl(
+      @NonNull InstanceManager instanceManager, @NonNull Context context) {
+    this(instanceManager, new CameraControlProxy(), context);
+  }
+
+  /**
+   * Constructs an {@link CameraControlHostApiImpl}.
+   *
+   * @param instanceManager maintains instances stored to communicate with attached Dart objects
+   * @param proxy proxy for constructors and static method of {@link CameraControl}
+   * @param context {@link Context} used to retrieve {@code Executor} used to enable torch mode
+   */
+  @VisibleForTesting
+  CameraControlHostApiImpl(
+      @NonNull InstanceManager instanceManager,
+      @NonNull CameraControlProxy proxy,
+      @NonNull Context context) {
+    this.instanceManager = instanceManager;
+    this.proxy = proxy;
+    proxy.context = context;
+  }
+
+  /**
+   * Sets the context that the {@code ProcessCameraProvider} will use to enable/disable torch mode.
+   *
+   * <p>If using the camera plugin in an add-to-app context, ensure that a new instance of the
+   * {@code CameraControl} is fetched via {@code #enableTorch} anytime the context changes.
+   */
+  public void setContext(@NonNull Context context) {
+    this.proxy.context = context;
+  }
+
+  @Override
+  public void enableTorch(
+      @NonNull Long identifier,
+      @NonNull Boolean torch,
+      @NonNull GeneratedCameraXLibrary.Result<Void> result) {
+    proxy.enableTorch(
+        Objects.requireNonNull(instanceManager.getInstance(identifier)), torch, result);
+  }
+}
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraHostApiImpl.java
index 98f5f74..603cc62 100644
--- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraHostApiImpl.java
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/CameraHostApiImpl.java
@@ -6,6 +6,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.camera.core.Camera;
+import androidx.camera.core.CameraControl;
 import androidx.camera.core.CameraInfo;
 import io.flutter.plugin.common.BinaryMessenger;
 import io.flutter.plugins.camerax.GeneratedCameraXLibrary.CameraHostApi;
@@ -28,14 +29,33 @@
   @Override
   @NonNull
   public Long getCameraInfo(@NonNull Long identifier) {
-    Camera camera = (Camera) Objects.requireNonNull(instanceManager.getInstance(identifier));
+    Camera camera = getCameraInstance(identifier);
     CameraInfo cameraInfo = camera.getCameraInfo();
 
-    if (!instanceManager.containsInstance(cameraInfo)) {
-      CameraInfoFlutterApiImpl cameraInfoFlutterApiImpl =
-          new CameraInfoFlutterApiImpl(binaryMessenger, instanceManager);
-      cameraInfoFlutterApiImpl.create(cameraInfo, reply -> {});
-    }
+    CameraInfoFlutterApiImpl cameraInfoFlutterApiImpl =
+        new CameraInfoFlutterApiImpl(binaryMessenger, instanceManager);
+    cameraInfoFlutterApiImpl.create(cameraInfo, reply -> {});
     return instanceManager.getIdentifierForStrongReference(cameraInfo);
   }
+
+  /**
+   * Retrieves the {@link CameraControl} instance that provides access to asynchronous operations
+   * like zoom and focus & metering on the {@link Camera} instance with the specified identifier.
+   */
+  @Override
+  @NonNull
+  public Long getCameraControl(@NonNull Long identifier) {
+    Camera camera = getCameraInstance(identifier);
+    CameraControl cameraControl = camera.getCameraControl();
+
+    CameraControlFlutterApiImpl cameraControlFlutterApiImpl =
+        new CameraControlFlutterApiImpl(binaryMessenger, instanceManager);
+    cameraControlFlutterApiImpl.create(cameraControl, reply -> {});
+    return instanceManager.getIdentifierForStrongReference(cameraControl);
+  }
+
+  /** Retrieives the {@link Camera} instance associated with the specified {@code identifier}. */
+  private Camera getCameraInstance(@NonNull Long identifier) {
+    return (Camera) Objects.requireNonNull(instanceManager.getInstance(identifier));
+  }
 }
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java
index fe9a935..224ba11 100644
--- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/GeneratedCameraXLibrary.java
@@ -1136,6 +1136,9 @@
     @NonNull
     Long getCameraInfo(@NonNull Long identifier);
 
+    @NonNull
+    Long getCameraControl(@NonNull Long identifier);
+
     /** The codec used by CameraHostApi. */
     static @NonNull MessageCodec<Object> getCodec() {
       return new StandardMessageCodec();
@@ -1166,6 +1169,31 @@
           channel.setMessageHandler(null);
         }
       }
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger, "dev.flutter.pigeon.CameraHostApi.getCameraControl", getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                ArrayList<Object> wrapped = new ArrayList<Object>();
+                ArrayList<Object> args = (ArrayList<Object>) message;
+                Number identifierArg = (Number) args.get(0);
+                try {
+                  Long output =
+                      api.getCameraControl(
+                          (identifierArg == null) ? null : identifierArg.longValue());
+                  wrapped.add(0, output);
+                } catch (Throwable exception) {
+                  ArrayList<Object> wrappedError = wrapError(exception);
+                  wrapped = wrappedError;
+                }
+                reply.reply(wrapped);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
     }
   }
   /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
@@ -3203,4 +3231,82 @@
       }
     }
   }
+  /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
+  public interface CameraControlHostApi {
+
+    void enableTorch(
+        @NonNull Long identifier, @NonNull Boolean torch, @NonNull Result<Void> result);
+
+    /** The codec used by CameraControlHostApi. */
+    static @NonNull MessageCodec<Object> getCodec() {
+      return new StandardMessageCodec();
+    }
+    /**
+     * Sets up an instance of `CameraControlHostApi` to handle messages through the
+     * `binaryMessenger`.
+     */
+    static void setup(
+        @NonNull BinaryMessenger binaryMessenger, @Nullable CameraControlHostApi api) {
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger, "dev.flutter.pigeon.CameraControlHostApi.enableTorch", getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                ArrayList<Object> wrapped = new ArrayList<Object>();
+                ArrayList<Object> args = (ArrayList<Object>) message;
+                Number identifierArg = (Number) args.get(0);
+                Boolean torchArg = (Boolean) args.get(1);
+                Result<Void> resultCallback =
+                    new Result<Void>() {
+                      public void success(Void result) {
+                        wrapped.add(0, null);
+                        reply.reply(wrapped);
+                      }
+
+                      public void error(Throwable error) {
+                        ArrayList<Object> wrappedError = wrapError(error);
+                        reply.reply(wrappedError);
+                      }
+                    };
+
+                api.enableTorch(
+                    (identifierArg == null) ? null : identifierArg.longValue(),
+                    torchArg,
+                    resultCallback);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
+    }
+  }
+  /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
+  public static class CameraControlFlutterApi {
+    private final @NonNull BinaryMessenger binaryMessenger;
+
+    public CameraControlFlutterApi(@NonNull BinaryMessenger argBinaryMessenger) {
+      this.binaryMessenger = argBinaryMessenger;
+    }
+
+    /** Public interface for sending reply. */
+    @SuppressWarnings("UnknownNullness")
+    public interface Reply<T> {
+      void reply(T reply);
+    }
+    /** The codec used by CameraControlFlutterApi. */
+    static @NonNull MessageCodec<Object> getCodec() {
+      return new StandardMessageCodec();
+    }
+
+    public void create(@NonNull Long identifierArg, @NonNull Reply<Void> callback) {
+      BasicMessageChannel<Object> channel =
+          new BasicMessageChannel<>(
+              binaryMessenger, "dev.flutter.pigeon.CameraControlFlutterApi.create", getCodec());
+      channel.send(
+          new ArrayList<Object>(Collections.singletonList(identifierArg)),
+          channelReply -> callback.reply(null));
+    }
+  }
 }
diff --git a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ImageAnalysisHostApiImpl.java b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ImageAnalysisHostApiImpl.java
index 5849147..00bceb7 100644
--- a/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ImageAnalysisHostApiImpl.java
+++ b/packages/camera/camera_android_camerax/android/src/main/java/io/flutter/plugins/camerax/ImageAnalysisHostApiImpl.java
@@ -24,9 +24,12 @@
   @VisibleForTesting @NonNull public CameraXProxy cameraXProxy = new CameraXProxy();
 
   public ImageAnalysisHostApiImpl(
-      @NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
+      @NonNull BinaryMessenger binaryMessenger,
+      @NonNull InstanceManager instanceManager,
+      @NonNull Context context) {
     this.binaryMessenger = binaryMessenger;
     this.instanceManager = instanceManager;
+    this.context = context;
   }
 
   /**
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraControlTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraControlTest.java
new file mode 100644
index 0000000..5e17f64
--- /dev/null
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraControlTest.java
@@ -0,0 +1,111 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.camerax;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import androidx.camera.core.CameraControl;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import io.flutter.plugin.common.BinaryMessenger;
+import java.util.Objects;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+public class CameraControlTest {
+  @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+
+  @Mock public BinaryMessenger mockBinaryMessenger;
+  @Mock public CameraControl cameraControl;
+
+  InstanceManager testInstanceManager;
+
+  @Before
+  public void setUp() {
+    testInstanceManager = InstanceManager.create(identifier -> {});
+  }
+
+  @After
+  public void tearDown() {
+    testInstanceManager.stopFinalizationListener();
+  }
+
+  @Test
+  public void enableTorch_turnsTorchModeOnAndOffAsExpected() {
+    try (MockedStatic<Futures> mockedFutures = Mockito.mockStatic(Futures.class)) {
+      final CameraControlHostApiImpl cameraControlHostApiImpl =
+          new CameraControlHostApiImpl(testInstanceManager, mock(Context.class));
+      final Long cameraControlIdentifier = 88L;
+      final boolean enableTorch = true;
+
+      @SuppressWarnings("unchecked")
+      final ListenableFuture<Void> enableTorchFuture = mock(ListenableFuture.class);
+
+      testInstanceManager.addDartCreatedInstance(cameraControl, cameraControlIdentifier);
+
+      when(cameraControl.enableTorch(true)).thenReturn(enableTorchFuture);
+
+      @SuppressWarnings("unchecked")
+      final ArgumentCaptor<FutureCallback<Void>> futureCallbackCaptor =
+          ArgumentCaptor.forClass(FutureCallback.class);
+
+      // Test turning on torch mode.
+      @SuppressWarnings("unchecked")
+      final GeneratedCameraXLibrary.Result<Void> successfulMockResult =
+          mock(GeneratedCameraXLibrary.Result.class);
+      cameraControlHostApiImpl.enableTorch(
+          cameraControlIdentifier, enableTorch, successfulMockResult);
+      mockedFutures.verify(
+          () -> Futures.addCallback(eq(enableTorchFuture), futureCallbackCaptor.capture(), any()));
+      mockedFutures.clearInvocations();
+
+      FutureCallback<Void> successfulEnableTorchCallback = futureCallbackCaptor.getValue();
+
+      successfulEnableTorchCallback.onSuccess(mock(Void.class));
+      verify(successfulMockResult).success(null);
+
+      // Test turning off torch mode.
+      @SuppressWarnings("unchecked")
+      final GeneratedCameraXLibrary.Result<Void> failedMockResult =
+          mock(GeneratedCameraXLibrary.Result.class);
+      final Throwable testThrowable = new Throwable();
+      cameraControlHostApiImpl.enableTorch(cameraControlIdentifier, enableTorch, failedMockResult);
+      mockedFutures.verify(
+          () -> Futures.addCallback(eq(enableTorchFuture), futureCallbackCaptor.capture(), any()));
+
+      FutureCallback<Void> failedEnableTorchCallback = futureCallbackCaptor.getValue();
+
+      failedEnableTorchCallback.onFailure(testThrowable);
+      verify(failedMockResult).error(testThrowable);
+    }
+  }
+
+  @Test
+  public void flutterApiCreate_makesCallToCreateInstanceOnDartSide() {
+    final CameraControlFlutterApiImpl spyFlutterApi =
+        spy(new CameraControlFlutterApiImpl(mockBinaryMessenger, testInstanceManager));
+
+    spyFlutterApi.create(cameraControl, reply -> {});
+
+    final long cameraControlIdentifier =
+        Objects.requireNonNull(testInstanceManager.getIdentifierForStrongReference(cameraControl));
+    verify(spyFlutterApi).create(eq(cameraControlIdentifier), any());
+  }
+}
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java
index fb6b0b3..590529c 100644
--- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/CameraTest.java
@@ -13,6 +13,7 @@
 import static org.mockito.Mockito.when;
 
 import androidx.camera.core.Camera;
+import androidx.camera.core.CameraControl;
 import androidx.camera.core.CameraInfo;
 import io.flutter.plugin.common.BinaryMessenger;
 import java.util.Objects;
@@ -60,6 +61,23 @@
   }
 
   @Test
+  public void getCameraControl_retrievesExpectedCameraControlInstance() {
+    final CameraHostApiImpl cameraHostApiImpl =
+        new CameraHostApiImpl(mockBinaryMessenger, testInstanceManager);
+    final CameraControl mockCameraControl = mock(CameraControl.class);
+    final Long cameraIdentifier = 43L;
+    final Long mockCameraControlIdentifier = 79L;
+
+    testInstanceManager.addDartCreatedInstance(camera, cameraIdentifier);
+    testInstanceManager.addDartCreatedInstance(mockCameraControl, mockCameraControlIdentifier);
+
+    when(camera.getCameraControl()).thenReturn(mockCameraControl);
+
+    assertEquals(cameraHostApiImpl.getCameraControl(cameraIdentifier), mockCameraControlIdentifier);
+    verify(camera).getCameraControl();
+  }
+
+  @Test
   public void flutterApiCreate_makesCallToCreateInstanceOnDartSide() {
     final CameraFlutterApiImpl spyFlutterApi =
         spy(new CameraFlutterApiImpl(mockBinaryMessenger, testInstanceManager));
diff --git a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ImageAnalysisTest.java b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ImageAnalysisTest.java
index b9a6d29..08a9f80 100644
--- a/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ImageAnalysisTest.java
+++ b/packages/camera/camera_android_camerax/android/src/test/java/io/flutter/plugins/camerax/ImageAnalysisTest.java
@@ -50,7 +50,7 @@
   @Test
   public void hostApiCreate_createsExpectedImageAnalysisInstanceWithExpectedIdentifier() {
     final ImageAnalysisHostApiImpl hostApi =
-        new ImageAnalysisHostApiImpl(mockBinaryMessenger, instanceManager);
+        new ImageAnalysisHostApiImpl(mockBinaryMessenger, instanceManager, context);
     final CameraXProxy mockCameraXProxy = mock(CameraXProxy.class);
     final ImageAnalysis.Builder mockImageAnalysisBuilder = mock(ImageAnalysis.Builder.class);
     final ResolutionSelector mockResolutionSelector = mock(ResolutionSelector.class);
@@ -72,8 +72,7 @@
   @Test
   public void setAnalyzer_makesCallToSetAnalyzerOnExpectedImageAnalysisInstance() {
     final ImageAnalysisHostApiImpl hostApi =
-        new ImageAnalysisHostApiImpl(mockBinaryMessenger, instanceManager);
-    hostApi.setContext(context);
+        new ImageAnalysisHostApiImpl(mockBinaryMessenger, instanceManager, context);
 
     final ImageAnalysis.Analyzer mockAnalyzer = mock(ImageAnalysis.Analyzer.class);
     final long analyzerIdentifier = 10;
@@ -90,7 +89,7 @@
   @Test
   public void clearAnalyzer_makesCallToClearAnalyzerOnExpectedImageAnalysisInstance() {
     final ImageAnalysisHostApiImpl hostApi =
-        new ImageAnalysisHostApiImpl(mockBinaryMessenger, instanceManager);
+        new ImageAnalysisHostApiImpl(mockBinaryMessenger, instanceManager, context);
     final long instanceIdentifier = 22;
 
     instanceManager.addDartCreatedInstance(mockImageAnalysis, instanceIdentifier);
diff --git a/packages/camera/camera_android_camerax/example/lib/main.dart b/packages/camera/camera_android_camerax/example/lib/main.dart
index 5dbfffa..ec6fb51 100644
--- a/packages/camera/camera_android_camerax/example/lib/main.dart
+++ b/packages/camera/camera_android_camerax/example/lib/main.dart
@@ -274,7 +274,7 @@
             IconButton(
               icon: const Icon(Icons.flash_on),
               color: Colors.blue,
-              onPressed: () {}, // TODO(camsim99): Add functionality back here.
+              onPressed: controller != null ? onFlashModeButtonPressed : null,
             ),
             // The exposure and focus mode are currently not supported on the web.
             ...!kIsWeb
@@ -326,28 +326,36 @@
               color: controller?.value.flashMode == FlashMode.off
                   ? Colors.orange
                   : Colors.blue,
-              onPressed: () {}, // TODO(camsim99): Add functionality back here.
+              onPressed: controller != null
+                  ? () => onSetFlashModeButtonPressed(FlashMode.off)
+                  : null,
             ),
             IconButton(
               icon: const Icon(Icons.flash_auto),
               color: controller?.value.flashMode == FlashMode.auto
                   ? Colors.orange
                   : Colors.blue,
-              onPressed: () {}, // TODO(camsim99): Add functionality back here.
+              onPressed: controller != null
+                  ? () => onSetFlashModeButtonPressed(FlashMode.auto)
+                  : null,
             ),
             IconButton(
               icon: const Icon(Icons.flash_on),
               color: controller?.value.flashMode == FlashMode.always
                   ? Colors.orange
                   : Colors.blue,
-              onPressed: () {}, // TODO(camsim99): Add functionality back here.
+              onPressed: controller != null
+                  ? () => onSetFlashModeButtonPressed(FlashMode.always)
+                  : null,
             ),
             IconButton(
               icon: const Icon(Icons.highlight),
               color: controller?.value.flashMode == FlashMode.torch
                   ? Colors.orange
                   : Colors.blue,
-              onPressed: () {}, // TODO(camsim99): Add functionality back here.
+              onPressed: controller != null
+                  ? () => onSetFlashModeButtonPressed(FlashMode.torch)
+                  : null,
             ),
           ],
         ),
diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart
index c915961..8b06bf6 100644
--- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart
+++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax.dart
@@ -11,6 +11,7 @@
 
 import 'analyzer.dart';
 import 'camera.dart';
+import 'camera_control.dart';
 import 'camera_info.dart';
 import 'camera_selector.dart';
 import 'camera_state.dart';
@@ -111,6 +112,10 @@
   /// The flash mode currently configured for [imageCapture].
   int? _currentFlashMode;
 
+  /// Whether or not torch flash mode has been enabled for the [camera].
+  @visibleForTesting
+  bool torchEnabled = false;
+
   /// The [ImageAnalysis] instance that can be configured to analyze individual
   /// frames.
   ImageAnalysis? imageAnalysis;
@@ -483,14 +488,37 @@
   Future<XFile> takePicture(int cameraId) async {
     if (_currentFlashMode != null) {
       await imageCapture!.setFlashMode(_currentFlashMode!);
+    } else if (torchEnabled) {
+      // Ensure any previously set flash modes are unset when torch mode has
+      // been enabled.
+      await imageCapture!.setFlashMode(ImageCapture.flashModeOff);
     }
     final String picturePath = await imageCapture!.takePicture();
     return XFile(picturePath);
   }
 
   /// Sets the flash mode for the selected camera.
+  ///
+  /// When the [FlashMode.torch] is enabled, any previously set [FlashMode] with
+  /// this method will be disabled, just as with any other [FlashMode]; while
+  /// this is not default native Android behavior as defined by the CameraX API,
+  /// this behavior is compliant with the plugin platform interface.
+  ///
+  /// This method combines the notion of setting the flash mode of the
+  /// [imageCapture] UseCase and enabling the camera torch, as described
+  /// by https://developer.android.com/reference/androidx/camera/core/ImageCapture
+  /// and https://developer.android.com/reference/androidx/camera/core/CameraControl#enableTorch(boolean),
+  /// respectively.
   @override
   Future<void> setFlashMode(int cameraId, FlashMode mode) async {
+    CameraControl? cameraControl;
+    // Turn off torch mode if it is enabled and not being redundantly set.
+    if (mode != FlashMode.torch && torchEnabled) {
+      cameraControl = await camera!.getCameraControl();
+      await cameraControl.enableTorch(false);
+      torchEnabled = false;
+    }
+
     switch (mode) {
       case FlashMode.off:
         _currentFlashMode = ImageCapture.flashModeOff;
@@ -502,7 +530,14 @@
         _currentFlashMode = ImageCapture.flashModeOn;
         break;
       case FlashMode.torch:
-        // TODO(camsim99): Implement torch mode when CameraControl is wrapped.
+        _currentFlashMode = null;
+        if (torchEnabled) {
+          // Torch mode enabled already.
+          return;
+        }
+        cameraControl = await camera!.getCameraControl();
+        await cameraControl.enableTorch(true);
+        torchEnabled = true;
         break;
     }
   }
diff --git a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart
index b268323..b59e0df 100644
--- a/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart
+++ b/packages/camera/camera_android_camerax/lib/src/android_camera_camerax_flutter_api_impls.dart
@@ -4,6 +4,7 @@
 
 import 'analyzer.dart';
 import 'camera.dart';
+import 'camera_control.dart';
 import 'camera_info.dart';
 import 'camera_selector.dart';
 import 'camera_state.dart';
@@ -26,27 +27,27 @@
 /// Handles initialization of Flutter APIs for the Android CameraX library.
 class AndroidCameraXCameraFlutterApis {
   /// Creates a [AndroidCameraXCameraFlutterApis].
-  AndroidCameraXCameraFlutterApis({
-    JavaObjectFlutterApiImpl? javaObjectFlutterApiImpl,
-    CameraFlutterApiImpl? cameraFlutterApiImpl,
-    CameraInfoFlutterApiImpl? cameraInfoFlutterApiImpl,
-    CameraSelectorFlutterApiImpl? cameraSelectorFlutterApiImpl,
-    ProcessCameraProviderFlutterApiImpl? processCameraProviderFlutterApiImpl,
-    SystemServicesFlutterApiImpl? systemServicesFlutterApiImpl,
-    CameraStateErrorFlutterApiImpl? cameraStateErrorFlutterApiImpl,
-    CameraStateFlutterApiImpl? cameraStateFlutterApiImpl,
-    PendingRecordingFlutterApiImpl? pendingRecordingFlutterApiImpl,
-    RecordingFlutterApiImpl? recordingFlutterApiImpl,
-    RecorderFlutterApiImpl? recorderFlutterApiImpl,
-    VideoCaptureFlutterApiImpl? videoCaptureFlutterApiImpl,
-    ExposureStateFlutterApiImpl? exposureStateFlutterApiImpl,
-    ZoomStateFlutterApiImpl? zoomStateFlutterApiImpl,
-    LiveDataFlutterApiImpl? liveDataFlutterApiImpl,
-    ObserverFlutterApiImpl? observerFlutterApiImpl,
-    ImageProxyFlutterApiImpl? imageProxyFlutterApiImpl,
-    PlaneProxyFlutterApiImpl? planeProxyFlutterApiImpl,
-    AnalyzerFlutterApiImpl? analyzerFlutterApiImpl,
-  }) {
+  AndroidCameraXCameraFlutterApis(
+      {JavaObjectFlutterApiImpl? javaObjectFlutterApiImpl,
+      CameraFlutterApiImpl? cameraFlutterApiImpl,
+      CameraInfoFlutterApiImpl? cameraInfoFlutterApiImpl,
+      CameraSelectorFlutterApiImpl? cameraSelectorFlutterApiImpl,
+      ProcessCameraProviderFlutterApiImpl? processCameraProviderFlutterApiImpl,
+      SystemServicesFlutterApiImpl? systemServicesFlutterApiImpl,
+      CameraStateErrorFlutterApiImpl? cameraStateErrorFlutterApiImpl,
+      CameraStateFlutterApiImpl? cameraStateFlutterApiImpl,
+      PendingRecordingFlutterApiImpl? pendingRecordingFlutterApiImpl,
+      RecordingFlutterApiImpl? recordingFlutterApiImpl,
+      RecorderFlutterApiImpl? recorderFlutterApiImpl,
+      VideoCaptureFlutterApiImpl? videoCaptureFlutterApiImpl,
+      ExposureStateFlutterApiImpl? exposureStateFlutterApiImpl,
+      ZoomStateFlutterApiImpl? zoomStateFlutterApiImpl,
+      LiveDataFlutterApiImpl? liveDataFlutterApiImpl,
+      ObserverFlutterApiImpl? observerFlutterApiImpl,
+      ImageProxyFlutterApiImpl? imageProxyFlutterApiImpl,
+      PlaneProxyFlutterApiImpl? planeProxyFlutterApiImpl,
+      AnalyzerFlutterApiImpl? analyzerFlutterApiImpl,
+      CameraControlFlutterApiImpl? cameraControlFlutterApiImpl}) {
     this.javaObjectFlutterApiImpl =
         javaObjectFlutterApiImpl ?? JavaObjectFlutterApiImpl();
     this.cameraInfoFlutterApiImpl =
@@ -85,6 +86,8 @@
         imageProxyFlutterApiImpl ?? ImageProxyFlutterApiImpl();
     this.planeProxyFlutterApiImpl =
         planeProxyFlutterApiImpl ?? PlaneProxyFlutterApiImpl();
+    this.cameraControlFlutterApiImpl =
+        cameraControlFlutterApiImpl ?? CameraControlFlutterApiImpl();
   }
 
   static bool _haveBeenSetUp = false;
@@ -153,6 +156,9 @@
   /// Flutter Api implementation for [PlaneProxy].
   late final PlaneProxyFlutterApiImpl planeProxyFlutterApiImpl;
 
+  /// Flutter Api implementation for [CameraControl].
+  late final CameraControlFlutterApiImpl cameraControlFlutterApiImpl;
+
   /// Ensures all the Flutter APIs have been setup to receive calls from native code.
   void ensureSetUp() {
     if (!_haveBeenSetUp) {
@@ -176,6 +182,7 @@
       PlaneProxyFlutterApi.setup(planeProxyFlutterApiImpl);
       LiveDataFlutterApi.setup(liveDataFlutterApiImpl);
       ObserverFlutterApi.setup(observerFlutterApiImpl);
+      CameraControlFlutterApi.setup(cameraControlFlutterApiImpl);
       _haveBeenSetUp = true;
     }
   }
diff --git a/packages/camera/camera_android_camerax/lib/src/camera.dart b/packages/camera/camera_android_camerax/lib/src/camera.dart
index 8bc1dbf..1001450 100644
--- a/packages/camera/camera_android_camerax/lib/src/camera.dart
+++ b/packages/camera/camera_android_camerax/lib/src/camera.dart
@@ -6,6 +6,7 @@
 import 'package:meta/meta.dart' show immutable;
 
 import 'android_camera_camerax_flutter_api_impls.dart';
+import 'camera_control.dart';
 import 'camera_info.dart';
 import 'camerax_library.g.dart';
 import 'instance_manager.dart';
@@ -23,27 +24,33 @@
       : super.detached(
             binaryMessenger: binaryMessenger,
             instanceManager: instanceManager) {
-    _api = CameraHostApiImpl(
+    _api = _CameraHostApiImpl(
         binaryMessenger: binaryMessenger, instanceManager: instanceManager);
     AndroidCameraXCameraFlutterApis.instance.ensureSetUp();
   }
 
-  late final CameraHostApiImpl _api;
+  late final _CameraHostApiImpl _api;
 
-  /// Retrieve the [CameraInfo] instance that contains information about this
+  /// Retrieves the [CameraInfo] instance that contains information about this
   /// instance.
   Future<CameraInfo> getCameraInfo() async {
     return _api.getCameraInfoFromInstance(this);
   }
+
+  /// Retrieves the [CameraControl] instance that provides asynchronous
+  /// operations like zoom and focus & metering for this instance.
+  Future<CameraControl> getCameraControl() async {
+    return _api.getCameraControlFromInstance(this);
+  }
 }
 
 /// Host API implementation of [Camera].
-class CameraHostApiImpl extends CameraHostApi {
-  /// Constructs a [CameraHostApiImpl].
+class _CameraHostApiImpl extends CameraHostApi {
+  /// Constructs a [_CameraHostApiImpl].
   ///
   /// An [instanceManager] is typically passed when a copy of an instance
   /// contained by an [InstanceManager] is being created.
-  CameraHostApiImpl({this.binaryMessenger, InstanceManager? instanceManager})
+  _CameraHostApiImpl({this.binaryMessenger, InstanceManager? instanceManager})
       : super(binaryMessenger: binaryMessenger) {
     this.instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
   }
@@ -57,7 +64,7 @@
   /// Maintains instances stored to communicate with native language objects.
   late final InstanceManager instanceManager;
 
-  /// Gets the [CameraInfo] associated with the specified instance of [Camera].
+  /// Gets the [CameraInfo] associated with the specified [Camera] instance.
   Future<CameraInfo> getCameraInfoFromInstance(Camera instance) async {
     final int identifier = instanceManager.getIdentifier(instance)!;
     final int cameraInfoId = await getCameraInfo(identifier);
@@ -65,6 +72,15 @@
     return instanceManager
         .getInstanceWithWeakReference<CameraInfo>(cameraInfoId)!;
   }
+
+  /// Gets the [CameraControl] associated with the specified [Camera] instance.
+  Future<CameraControl> getCameraControlFromInstance(Camera instance) async {
+    final int identifier = instanceManager.getIdentifier(instance)!;
+    final int cameraControlId = await getCameraControl(identifier);
+
+    return instanceManager
+        .getInstanceWithWeakReference<CameraControl>(cameraControlId)!;
+  }
 }
 
 /// Flutter API implementation of [Camera].
diff --git a/packages/camera/camera_android_camerax/lib/src/camera_control.dart b/packages/camera/camera_android_camerax/lib/src/camera_control.dart
new file mode 100644
index 0000000..b30195c
--- /dev/null
+++ b/packages/camera/camera_android_camerax/lib/src/camera_control.dart
@@ -0,0 +1,109 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/services.dart' show BinaryMessenger, PlatformException;
+import 'package:meta/meta.dart' show immutable;
+
+import 'android_camera_camerax_flutter_api_impls.dart';
+import 'camerax_library.g.dart';
+import 'instance_manager.dart';
+import 'java_object.dart';
+import 'system_services.dart';
+
+/// The interface that provides asynchronous operations like zoom and focus &
+/// metering, which affects output of all [UseCase]s currently bound to the
+/// corresponding [Camera] instance.
+///
+/// See https://developer.android.com/reference/androidx/camera/core/CameraControl.
+@immutable
+class CameraControl extends JavaObject {
+  /// Constructs a [CameraControl] that is not automatically attached to a native object.
+  CameraControl.detached(
+      {BinaryMessenger? binaryMessenger, InstanceManager? instanceManager})
+      : super.detached(
+            binaryMessenger: binaryMessenger,
+            instanceManager: instanceManager) {
+    _api = _CameraControlHostApiImpl(
+        binaryMessenger: binaryMessenger, instanceManager: instanceManager);
+    AndroidCameraXCameraFlutterApis.instance.ensureSetUp();
+  }
+
+  late final _CameraControlHostApiImpl _api;
+
+  /// Enables or disables the torch of related [Camera] instance.
+  Future<void> enableTorch(bool torch) async {
+    return _api.enableTorchFromInstance(this, torch);
+  }
+}
+
+/// Host API implementation of [CameraControl].
+class _CameraControlHostApiImpl extends CameraControlHostApi {
+  /// Constructs a [_CameraControlHostApiImpl].
+  ///
+  /// An [instanceManager] is typically passed when a copy of an instance
+  /// contained by an [InstanceManager] is being created.
+  _CameraControlHostApiImpl(
+      {this.binaryMessenger, InstanceManager? instanceManager})
+      : super(binaryMessenger: binaryMessenger) {
+    this.instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
+  }
+
+  /// Receives binary data across the Flutter platform barrier.
+  ///
+  /// If it is null, the default BinaryMessenger will be used which routes to
+  /// the host platform.
+  final BinaryMessenger? binaryMessenger;
+
+  /// Maintains instances stored to communicate with native language objects.
+  late final InstanceManager instanceManager;
+
+  /// Enables or disables the torch for the specified [CameraControl] instance.
+  Future<void> enableTorchFromInstance(
+      CameraControl instance, bool torch) async {
+    final int identifier = instanceManager.getIdentifier(instance)!;
+    try {
+      await enableTorch(identifier, torch);
+    } on PlatformException catch (e) {
+      SystemServices.cameraErrorStreamController
+          .add(e.message ?? 'The camera was unable to change torch modes.');
+    }
+  }
+}
+
+/// Flutter API implementation of [CameraControl].
+class CameraControlFlutterApiImpl extends CameraControlFlutterApi {
+  /// Constructs a [CameraControlFlutterApiImpl].
+  ///
+  /// If [binaryMessenger] is null, the default [BinaryMessenger] will be used,
+  /// which routes to the host platform.
+  ///
+  /// An [instanceManager] is typically passed when a copy of an instance
+  /// contained by an [InstanceManager] is being created. If left null, it
+  /// will default to the global instance defined in [JavaObject].
+  CameraControlFlutterApiImpl({
+    BinaryMessenger? binaryMessenger,
+    InstanceManager? instanceManager,
+  })  : _binaryMessenger = binaryMessenger,
+        _instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
+
+  /// Receives binary data across the Flutter platform barrier.
+  final BinaryMessenger? _binaryMessenger;
+
+  /// Maintains instances stored to communicate with native language objects.
+  final InstanceManager _instanceManager;
+
+  @override
+  void create(int identifier) {
+    _instanceManager.addHostCreatedInstance(
+      CameraControl.detached(
+          binaryMessenger: _binaryMessenger, instanceManager: _instanceManager),
+      identifier,
+      onCopy: (CameraControl original) {
+        return CameraControl.detached(
+            binaryMessenger: _binaryMessenger,
+            instanceManager: _instanceManager);
+      },
+    );
+  }
+}
diff --git a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart
index dda3a81..4fc4d15 100644
--- a/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart
+++ b/packages/camera/camera_android_camerax/lib/src/camerax_library.g.dart
@@ -780,6 +780,33 @@
       return (replyList[0] as int?)!;
     }
   }
+
+  Future<int> getCameraControl(int arg_identifier) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.CameraHostApi.getCameraControl', codec,
+        binaryMessenger: _binaryMessenger);
+    final List<Object?>? replyList =
+        await channel.send(<Object?>[arg_identifier]) as List<Object?>?;
+    if (replyList == null) {
+      throw PlatformException(
+        code: 'channel-error',
+        message: 'Unable to establish connection on channel.',
+      );
+    } else if (replyList.length > 1) {
+      throw PlatformException(
+        code: replyList[0]! as String,
+        message: replyList[1] as String?,
+        details: replyList[2],
+      );
+    } else if (replyList[0] == null) {
+      throw PlatformException(
+        code: 'null-error',
+        message: 'Host platform returned null value for non-null return value.',
+      );
+    } else {
+      return (replyList[0] as int?)!;
+    }
+  }
 }
 
 abstract class CameraFlutterApi {
@@ -2626,3 +2653,65 @@
     }
   }
 }
+
+class CameraControlHostApi {
+  /// Constructor for [CameraControlHostApi].  The [binaryMessenger] named argument is
+  /// available for dependency injection.  If it is left null, the default
+  /// BinaryMessenger will be used which routes to the host platform.
+  CameraControlHostApi({BinaryMessenger? binaryMessenger})
+      : _binaryMessenger = binaryMessenger;
+  final BinaryMessenger? _binaryMessenger;
+
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
+
+  Future<void> enableTorch(int arg_identifier, bool arg_torch) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.CameraControlHostApi.enableTorch', codec,
+        binaryMessenger: _binaryMessenger);
+    final List<Object?>? replyList = await channel
+        .send(<Object?>[arg_identifier, arg_torch]) as List<Object?>?;
+    if (replyList == null) {
+      throw PlatformException(
+        code: 'channel-error',
+        message: 'Unable to establish connection on channel.',
+      );
+    } else if (replyList.length > 1) {
+      throw PlatformException(
+        code: replyList[0]! as String,
+        message: replyList[1] as String?,
+        details: replyList[2],
+      );
+    } else {
+      return;
+    }
+  }
+}
+
+abstract class CameraControlFlutterApi {
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
+
+  void create(int identifier);
+
+  static void setup(CameraControlFlutterApi? api,
+      {BinaryMessenger? binaryMessenger}) {
+    {
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.CameraControlFlutterApi.create', codec,
+          binaryMessenger: binaryMessenger);
+      if (api == null) {
+        channel.setMessageHandler(null);
+      } else {
+        channel.setMessageHandler((Object? message) async {
+          assert(message != null,
+              'Argument for dev.flutter.pigeon.CameraControlFlutterApi.create was null.');
+          final List<Object?> args = (message as List<Object?>?)!;
+          final int? arg_identifier = (args[0] as int?);
+          assert(arg_identifier != null,
+              'Argument for dev.flutter.pigeon.CameraControlFlutterApi.create was null, expected non-null int.');
+          api.create(arg_identifier!);
+          return;
+        });
+      }
+    }
+  }
+}
diff --git a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart
index 9ebf34b..2c5b9c2 100644
--- a/packages/camera/camera_android_camerax/pigeons/camerax_library.dart
+++ b/packages/camera/camera_android_camerax/pigeons/camerax_library.dart
@@ -197,6 +197,8 @@
 @HostApi(dartHostTestHandler: 'TestCameraHostApi')
 abstract class CameraHostApi {
   int getCameraInfo(int identifier);
+
+  int getCameraControl(int identifier);
 }
 
 @FlutterApi()
@@ -417,3 +419,14 @@
   void create(int identifier, VideoQuality quality,
       VideoResolutionFallbackRule fallbackRule);
 }
+
+@HostApi(dartHostTestHandler: 'TestCameraControlHostApi')
+abstract class CameraControlHostApi {
+  @async
+  void enableTorch(int identifier, bool torch);
+}
+
+@FlutterApi()
+abstract class CameraControlFlutterApi {
+  void create(int identifier);
+}
diff --git a/packages/camera/camera_android_camerax/pubspec.yaml b/packages/camera/camera_android_camerax/pubspec.yaml
index 75b3c03..238d94c 100644
--- a/packages/camera/camera_android_camerax/pubspec.yaml
+++ b/packages/camera/camera_android_camerax/pubspec.yaml
@@ -2,7 +2,7 @@
 description: Android implementation of the camera plugin using the CameraX library.
 repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android_camerax
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
-version: 0.5.0+18
+version: 0.5.0+19
 
 environment:
   sdk: ">=2.19.0 <4.0.0"
diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart
index b90aabd..10c8e5b 100644
--- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart
+++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.dart
@@ -8,6 +8,7 @@
 import 'package:camera_android_camerax/camera_android_camerax.dart';
 import 'package:camera_android_camerax/src/analyzer.dart';
 import 'package:camera_android_camerax/src/camera.dart';
+import 'package:camera_android_camerax/src/camera_control.dart';
 import 'package:camera_android_camerax/src/camera_info.dart';
 import 'package:camera_android_camerax/src/camera_selector.dart';
 import 'package:camera_android_camerax/src/camera_state.dart';
@@ -46,6 +47,7 @@
 @GenerateNiceMocks(<MockSpec<Object>>[
   MockSpec<Camera>(),
   MockSpec<CameraInfo>(),
+  MockSpec<CameraControl>(),
   MockSpec<CameraImageData>(),
   MockSpec<CameraSelector>(),
   MockSpec<ExposureState>(),
@@ -1025,12 +1027,35 @@
     expect(imageFile.path, equals(testPicturePath));
   });
 
-  test('setFlashMode configures ImageCapture with expected flash mode',
+  test('takePicture turns non-torch flash mode off when torch mode enabled',
+      () async {
+    final AndroidCameraCameraX camera = AndroidCameraCameraX();
+    const int cameraId = 77;
+    final MockCameraControl mockCameraControl = MockCameraControl();
+
+    camera.imageCapture = MockImageCapture();
+    camera.camera = MockCamera();
+
+    when(camera.camera!.getCameraControl())
+        .thenAnswer((_) async => mockCameraControl);
+
+    await camera.setFlashMode(cameraId, FlashMode.torch);
+    await camera.takePicture(cameraId);
+    verify(camera.imageCapture!.setFlashMode(ImageCapture.flashModeOff));
+  });
+
+  test(
+      'setFlashMode configures ImageCapture with expected non-torch flash mode',
       () async {
     final AndroidCameraCameraX camera = AndroidCameraCameraX();
     const int cameraId = 22;
+    final MockCameraControl mockCameraControl = MockCameraControl();
 
     camera.imageCapture = MockImageCapture();
+    camera.camera = MockCamera();
+
+    when(camera.camera!.getCameraControl())
+        .thenAnswer((_) async => mockCameraControl);
 
     for (final FlashMode flashMode in FlashMode.values) {
       await camera.setFlashMode(cameraId, flashMode);
@@ -1047,19 +1072,68 @@
           expectedFlashMode = ImageCapture.flashModeOn;
           break;
         case FlashMode.torch:
-          // TODO(camsim99): Test torch mode when implemented.
+          expectedFlashMode = null;
           break;
       }
 
       if (expectedFlashMode == null) {
+        // Torch mode enabled and won't be used for configuring image capture.
         continue;
       }
 
+      verifyNever(mockCameraControl.enableTorch(true));
+      expect(camera.torchEnabled, isFalse);
       await camera.takePicture(cameraId);
       verify(camera.imageCapture!.setFlashMode(expectedFlashMode));
     }
   });
 
+  test('setFlashMode turns on torch mode as expected', () async {
+    final AndroidCameraCameraX camera = AndroidCameraCameraX();
+    const int cameraId = 44;
+    final MockCameraControl mockCameraControl = MockCameraControl();
+
+    camera.camera = MockCamera();
+
+    when(camera.camera!.getCameraControl())
+        .thenAnswer((_) async => mockCameraControl);
+
+    await camera.setFlashMode(cameraId, FlashMode.torch);
+
+    verify(mockCameraControl.enableTorch(true));
+    expect(camera.torchEnabled, isTrue);
+  });
+
+  test('setFlashMode turns off torch mode when non-torch flash modes set',
+      () async {
+    final AndroidCameraCameraX camera = AndroidCameraCameraX();
+    const int cameraId = 33;
+    final MockCameraControl mockCameraControl = MockCameraControl();
+
+    camera.camera = MockCamera();
+
+    when(camera.camera!.getCameraControl())
+        .thenAnswer((_) async => mockCameraControl);
+
+    for (final FlashMode flashMode in FlashMode.values) {
+      camera.torchEnabled = true;
+      await camera.setFlashMode(cameraId, flashMode);
+
+      switch (flashMode) {
+        case FlashMode.off:
+        case FlashMode.auto:
+        case FlashMode.always:
+          verify(mockCameraControl.enableTorch(false));
+          expect(camera.torchEnabled, isFalse);
+          break;
+        case FlashMode.torch:
+          verifyNever(mockCameraControl.enableTorch(true));
+          expect(camera.torchEnabled, true);
+          break;
+      }
+    }
+  });
+
   test('getMinExposureOffset returns expected exposure offset', () async {
     final AndroidCameraCameraX camera = AndroidCameraCameraX();
     final MockCameraInfo mockCameraInfo = MockCameraInfo();
diff --git a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart
index 1d3d636..f275ea7 100644
--- a/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart
+++ b/packages/camera/camera_android_camerax/test/android_camera_camerax_test.mocks.dart
@@ -5,39 +5,40 @@
 // @dart=2.19
 
 // ignore_for_file: no_leading_underscores_for_library_prefixes
-import 'dart:async' as _i14;
-import 'dart:typed_data' as _i23;
+import 'dart:async' as _i15;
+import 'dart:typed_data' as _i24;
 
-import 'package:camera_android_camerax/src/analyzer.dart' as _i19;
-import 'package:camera_android_camerax/src/camera.dart' as _i7;
+import 'package:camera_android_camerax/src/analyzer.dart' as _i20;
+import 'package:camera_android_camerax/src/camera.dart' as _i8;
+import 'package:camera_android_camerax/src/camera_control.dart' as _i3;
 import 'package:camera_android_camerax/src/camera_info.dart' as _i2;
-import 'package:camera_android_camerax/src/camera_selector.dart' as _i17;
-import 'package:camera_android_camerax/src/camera_state.dart' as _i15;
-import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i6;
-import 'package:camera_android_camerax/src/exposure_state.dart' as _i4;
-import 'package:camera_android_camerax/src/image_analysis.dart' as _i18;
-import 'package:camera_android_camerax/src/image_capture.dart' as _i20;
-import 'package:camera_android_camerax/src/image_proxy.dart' as _i21;
-import 'package:camera_android_camerax/src/live_data.dart' as _i3;
-import 'package:camera_android_camerax/src/observer.dart' as _i29;
-import 'package:camera_android_camerax/src/pending_recording.dart' as _i8;
-import 'package:camera_android_camerax/src/plane_proxy.dart' as _i22;
-import 'package:camera_android_camerax/src/preview.dart' as _i24;
+import 'package:camera_android_camerax/src/camera_selector.dart' as _i18;
+import 'package:camera_android_camerax/src/camera_state.dart' as _i16;
+import 'package:camera_android_camerax/src/camerax_library.g.dart' as _i7;
+import 'package:camera_android_camerax/src/exposure_state.dart' as _i5;
+import 'package:camera_android_camerax/src/image_analysis.dart' as _i19;
+import 'package:camera_android_camerax/src/image_capture.dart' as _i21;
+import 'package:camera_android_camerax/src/image_proxy.dart' as _i22;
+import 'package:camera_android_camerax/src/live_data.dart' as _i4;
+import 'package:camera_android_camerax/src/observer.dart' as _i30;
+import 'package:camera_android_camerax/src/pending_recording.dart' as _i9;
+import 'package:camera_android_camerax/src/plane_proxy.dart' as _i23;
+import 'package:camera_android_camerax/src/preview.dart' as _i25;
 import 'package:camera_android_camerax/src/process_camera_provider.dart'
-    as _i25;
-import 'package:camera_android_camerax/src/recorder.dart' as _i10;
-import 'package:camera_android_camerax/src/recording.dart' as _i9;
-import 'package:camera_android_camerax/src/use_case.dart' as _i26;
-import 'package:camera_android_camerax/src/video_capture.dart' as _i27;
-import 'package:camera_android_camerax/src/zoom_state.dart' as _i16;
+    as _i26;
+import 'package:camera_android_camerax/src/recorder.dart' as _i11;
+import 'package:camera_android_camerax/src/recording.dart' as _i10;
+import 'package:camera_android_camerax/src/use_case.dart' as _i27;
+import 'package:camera_android_camerax/src/video_capture.dart' as _i28;
+import 'package:camera_android_camerax/src/zoom_state.dart' as _i17;
 import 'package:camera_platform_interface/camera_platform_interface.dart'
-    as _i5;
-import 'package:flutter/foundation.dart' as _i13;
-import 'package:flutter/services.dart' as _i12;
-import 'package:flutter/widgets.dart' as _i11;
+    as _i6;
+import 'package:flutter/foundation.dart' as _i14;
+import 'package:flutter/services.dart' as _i13;
+import 'package:flutter/widgets.dart' as _i12;
 import 'package:mockito/mockito.dart' as _i1;
 
-import 'test_camerax_library.g.dart' as _i28;
+import 'test_camerax_library.g.dart' as _i29;
 
 // ignore_for_file: type=lint
 // ignore_for_file: avoid_redundant_argument_values
@@ -60,9 +61,8 @@
         );
 }
 
-class _FakeLiveData_1<T extends Object> extends _i1.SmartFake
-    implements _i3.LiveData<T> {
-  _FakeLiveData_1(
+class _FakeCameraControl_1 extends _i1.SmartFake implements _i3.CameraControl {
+  _FakeCameraControl_1(
     Object parent,
     Invocation parentInvocation,
   ) : super(
@@ -71,8 +71,9 @@
         );
 }
 
-class _FakeExposureState_2 extends _i1.SmartFake implements _i4.ExposureState {
-  _FakeExposureState_2(
+class _FakeLiveData_2<T extends Object> extends _i1.SmartFake
+    implements _i4.LiveData<T> {
+  _FakeLiveData_2(
     Object parent,
     Invocation parentInvocation,
   ) : super(
@@ -81,9 +82,8 @@
         );
 }
 
-class _FakeCameraImageFormat_3 extends _i1.SmartFake
-    implements _i5.CameraImageFormat {
-  _FakeCameraImageFormat_3(
+class _FakeExposureState_3 extends _i1.SmartFake implements _i5.ExposureState {
+  _FakeExposureState_3(
     Object parent,
     Invocation parentInvocation,
   ) : super(
@@ -92,9 +92,9 @@
         );
 }
 
-class _FakeExposureCompensationRange_4 extends _i1.SmartFake
-    implements _i6.ExposureCompensationRange {
-  _FakeExposureCompensationRange_4(
+class _FakeCameraImageFormat_4 extends _i1.SmartFake
+    implements _i6.CameraImageFormat {
+  _FakeCameraImageFormat_4(
     Object parent,
     Invocation parentInvocation,
   ) : super(
@@ -103,9 +103,9 @@
         );
 }
 
-class _FakeResolutionInfo_5 extends _i1.SmartFake
-    implements _i6.ResolutionInfo {
-  _FakeResolutionInfo_5(
+class _FakeExposureCompensationRange_5 extends _i1.SmartFake
+    implements _i7.ExposureCompensationRange {
+  _FakeExposureCompensationRange_5(
     Object parent,
     Invocation parentInvocation,
   ) : super(
@@ -114,8 +114,9 @@
         );
 }
 
-class _FakeCamera_6 extends _i1.SmartFake implements _i7.Camera {
-  _FakeCamera_6(
+class _FakeResolutionInfo_6 extends _i1.SmartFake
+    implements _i7.ResolutionInfo {
+  _FakeResolutionInfo_6(
     Object parent,
     Invocation parentInvocation,
   ) : super(
@@ -124,9 +125,8 @@
         );
 }
 
-class _FakePendingRecording_7 extends _i1.SmartFake
-    implements _i8.PendingRecording {
-  _FakePendingRecording_7(
+class _FakeCamera_7 extends _i1.SmartFake implements _i8.Camera {
+  _FakeCamera_7(
     Object parent,
     Invocation parentInvocation,
   ) : super(
@@ -135,8 +135,9 @@
         );
 }
 
-class _FakeRecording_8 extends _i1.SmartFake implements _i9.Recording {
-  _FakeRecording_8(
+class _FakePendingRecording_8 extends _i1.SmartFake
+    implements _i9.PendingRecording {
+  _FakePendingRecording_8(
     Object parent,
     Invocation parentInvocation,
   ) : super(
@@ -145,8 +146,8 @@
         );
 }
 
-class _FakeRecorder_9 extends _i1.SmartFake implements _i10.Recorder {
-  _FakeRecorder_9(
+class _FakeRecording_9 extends _i1.SmartFake implements _i10.Recording {
+  _FakeRecording_9(
     Object parent,
     Invocation parentInvocation,
   ) : super(
@@ -155,8 +156,18 @@
         );
 }
 
-class _FakeWidget_10 extends _i1.SmartFake implements _i11.Widget {
-  _FakeWidget_10(
+class _FakeRecorder_10 extends _i1.SmartFake implements _i11.Recorder {
+  _FakeRecorder_10(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeWidget_11 extends _i1.SmartFake implements _i12.Widget {
+  _FakeWidget_11(
     Object parent,
     Invocation parentInvocation,
   ) : super(
@@ -166,13 +177,13 @@
 
   @override
   String toString(
-          {_i12.DiagnosticLevel? minLevel = _i12.DiagnosticLevel.info}) =>
+          {_i13.DiagnosticLevel? minLevel = _i13.DiagnosticLevel.info}) =>
       super.toString();
 }
 
-class _FakeInheritedWidget_11 extends _i1.SmartFake
-    implements _i11.InheritedWidget {
-  _FakeInheritedWidget_11(
+class _FakeInheritedWidget_12 extends _i1.SmartFake
+    implements _i12.InheritedWidget {
+  _FakeInheritedWidget_12(
     Object parent,
     Invocation parentInvocation,
   ) : super(
@@ -182,13 +193,13 @@
 
   @override
   String toString(
-          {_i12.DiagnosticLevel? minLevel = _i12.DiagnosticLevel.info}) =>
+          {_i13.DiagnosticLevel? minLevel = _i13.DiagnosticLevel.info}) =>
       super.toString();
 }
 
-class _FakeDiagnosticsNode_12 extends _i1.SmartFake
-    implements _i13.DiagnosticsNode {
-  _FakeDiagnosticsNode_12(
+class _FakeDiagnosticsNode_13 extends _i1.SmartFake
+    implements _i14.DiagnosticsNode {
+  _FakeDiagnosticsNode_13(
     Object parent,
     Invocation parentInvocation,
   ) : super(
@@ -198,8 +209,8 @@
 
   @override
   String toString({
-    _i13.TextTreeConfiguration? parentConfiguration,
-    _i12.DiagnosticLevel? minLevel = _i12.DiagnosticLevel.info,
+    _i14.TextTreeConfiguration? parentConfiguration,
+    _i13.DiagnosticLevel? minLevel = _i13.DiagnosticLevel.info,
   }) =>
       super.toString();
 }
@@ -208,14 +219,14 @@
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockCamera extends _i1.Mock implements _i7.Camera {
+class MockCamera extends _i1.Mock implements _i8.Camera {
   @override
-  _i14.Future<_i2.CameraInfo> getCameraInfo() => (super.noSuchMethod(
+  _i15.Future<_i2.CameraInfo> getCameraInfo() => (super.noSuchMethod(
         Invocation.method(
           #getCameraInfo,
           [],
         ),
-        returnValue: _i14.Future<_i2.CameraInfo>.value(_FakeCameraInfo_0(
+        returnValue: _i15.Future<_i2.CameraInfo>.value(_FakeCameraInfo_0(
           this,
           Invocation.method(
             #getCameraInfo,
@@ -223,14 +234,36 @@
           ),
         )),
         returnValueForMissingStub:
-            _i14.Future<_i2.CameraInfo>.value(_FakeCameraInfo_0(
+            _i15.Future<_i2.CameraInfo>.value(_FakeCameraInfo_0(
           this,
           Invocation.method(
             #getCameraInfo,
             [],
           ),
         )),
-      ) as _i14.Future<_i2.CameraInfo>);
+      ) as _i15.Future<_i2.CameraInfo>);
+  @override
+  _i15.Future<_i3.CameraControl> getCameraControl() => (super.noSuchMethod(
+        Invocation.method(
+          #getCameraControl,
+          [],
+        ),
+        returnValue: _i15.Future<_i3.CameraControl>.value(_FakeCameraControl_1(
+          this,
+          Invocation.method(
+            #getCameraControl,
+            [],
+          ),
+        )),
+        returnValueForMissingStub:
+            _i15.Future<_i3.CameraControl>.value(_FakeCameraControl_1(
+          this,
+          Invocation.method(
+            #getCameraControl,
+            [],
+          ),
+        )),
+      ) as _i15.Future<_i3.CameraControl>);
 }
 
 /// A class which mocks [CameraInfo].
@@ -239,23 +272,23 @@
 // ignore: must_be_immutable
 class MockCameraInfo extends _i1.Mock implements _i2.CameraInfo {
   @override
-  _i14.Future<int> getSensorRotationDegrees() => (super.noSuchMethod(
+  _i15.Future<int> getSensorRotationDegrees() => (super.noSuchMethod(
         Invocation.method(
           #getSensorRotationDegrees,
           [],
         ),
-        returnValue: _i14.Future<int>.value(0),
-        returnValueForMissingStub: _i14.Future<int>.value(0),
-      ) as _i14.Future<int>);
+        returnValue: _i15.Future<int>.value(0),
+        returnValueForMissingStub: _i15.Future<int>.value(0),
+      ) as _i15.Future<int>);
   @override
-  _i14.Future<_i3.LiveData<_i15.CameraState>> getCameraState() =>
+  _i15.Future<_i4.LiveData<_i16.CameraState>> getCameraState() =>
       (super.noSuchMethod(
         Invocation.method(
           #getCameraState,
           [],
         ),
-        returnValue: _i14.Future<_i3.LiveData<_i15.CameraState>>.value(
-            _FakeLiveData_1<_i15.CameraState>(
+        returnValue: _i15.Future<_i4.LiveData<_i16.CameraState>>.value(
+            _FakeLiveData_2<_i16.CameraState>(
           this,
           Invocation.method(
             #getCameraState,
@@ -263,22 +296,22 @@
           ),
         )),
         returnValueForMissingStub:
-            _i14.Future<_i3.LiveData<_i15.CameraState>>.value(
-                _FakeLiveData_1<_i15.CameraState>(
+            _i15.Future<_i4.LiveData<_i16.CameraState>>.value(
+                _FakeLiveData_2<_i16.CameraState>(
           this,
           Invocation.method(
             #getCameraState,
             [],
           ),
         )),
-      ) as _i14.Future<_i3.LiveData<_i15.CameraState>>);
+      ) as _i15.Future<_i4.LiveData<_i16.CameraState>>);
   @override
-  _i14.Future<_i4.ExposureState> getExposureState() => (super.noSuchMethod(
+  _i15.Future<_i5.ExposureState> getExposureState() => (super.noSuchMethod(
         Invocation.method(
           #getExposureState,
           [],
         ),
-        returnValue: _i14.Future<_i4.ExposureState>.value(_FakeExposureState_2(
+        returnValue: _i15.Future<_i5.ExposureState>.value(_FakeExposureState_3(
           this,
           Invocation.method(
             #getExposureState,
@@ -286,23 +319,23 @@
           ),
         )),
         returnValueForMissingStub:
-            _i14.Future<_i4.ExposureState>.value(_FakeExposureState_2(
+            _i15.Future<_i5.ExposureState>.value(_FakeExposureState_3(
           this,
           Invocation.method(
             #getExposureState,
             [],
           ),
         )),
-      ) as _i14.Future<_i4.ExposureState>);
+      ) as _i15.Future<_i5.ExposureState>);
   @override
-  _i14.Future<_i3.LiveData<_i16.ZoomState>> getZoomState() =>
+  _i15.Future<_i4.LiveData<_i17.ZoomState>> getZoomState() =>
       (super.noSuchMethod(
         Invocation.method(
           #getZoomState,
           [],
         ),
-        returnValue: _i14.Future<_i3.LiveData<_i16.ZoomState>>.value(
-            _FakeLiveData_1<_i16.ZoomState>(
+        returnValue: _i15.Future<_i4.LiveData<_i17.ZoomState>>.value(
+            _FakeLiveData_2<_i17.ZoomState>(
           this,
           Invocation.method(
             #getZoomState,
@@ -310,34 +343,50 @@
           ),
         )),
         returnValueForMissingStub:
-            _i14.Future<_i3.LiveData<_i16.ZoomState>>.value(
-                _FakeLiveData_1<_i16.ZoomState>(
+            _i15.Future<_i4.LiveData<_i17.ZoomState>>.value(
+                _FakeLiveData_2<_i17.ZoomState>(
           this,
           Invocation.method(
             #getZoomState,
             [],
           ),
         )),
-      ) as _i14.Future<_i3.LiveData<_i16.ZoomState>>);
+      ) as _i15.Future<_i4.LiveData<_i17.ZoomState>>);
+}
+
+/// A class which mocks [CameraControl].
+///
+/// See the documentation for Mockito's code generation for more information.
+// ignore: must_be_immutable
+class MockCameraControl extends _i1.Mock implements _i3.CameraControl {
+  @override
+  _i15.Future<void> enableTorch(bool? torch) => (super.noSuchMethod(
+        Invocation.method(
+          #enableTorch,
+          [torch],
+        ),
+        returnValue: _i15.Future<void>.value(),
+        returnValueForMissingStub: _i15.Future<void>.value(),
+      ) as _i15.Future<void>);
 }
 
 /// A class which mocks [CameraImageData].
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockCameraImageData extends _i1.Mock implements _i5.CameraImageData {
+class MockCameraImageData extends _i1.Mock implements _i6.CameraImageData {
   @override
-  _i5.CameraImageFormat get format => (super.noSuchMethod(
+  _i6.CameraImageFormat get format => (super.noSuchMethod(
         Invocation.getter(#format),
-        returnValue: _FakeCameraImageFormat_3(
+        returnValue: _FakeCameraImageFormat_4(
           this,
           Invocation.getter(#format),
         ),
-        returnValueForMissingStub: _FakeCameraImageFormat_3(
+        returnValueForMissingStub: _FakeCameraImageFormat_4(
           this,
           Invocation.getter(#format),
         ),
-      ) as _i5.CameraImageFormat);
+      ) as _i6.CameraImageFormat);
   @override
   int get height => (super.noSuchMethod(
         Invocation.getter(#height),
@@ -351,50 +400,50 @@
         returnValueForMissingStub: 0,
       ) as int);
   @override
-  List<_i5.CameraImagePlane> get planes => (super.noSuchMethod(
+  List<_i6.CameraImagePlane> get planes => (super.noSuchMethod(
         Invocation.getter(#planes),
-        returnValue: <_i5.CameraImagePlane>[],
-        returnValueForMissingStub: <_i5.CameraImagePlane>[],
-      ) as List<_i5.CameraImagePlane>);
+        returnValue: <_i6.CameraImagePlane>[],
+        returnValueForMissingStub: <_i6.CameraImagePlane>[],
+      ) as List<_i6.CameraImagePlane>);
 }
 
 /// A class which mocks [CameraSelector].
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockCameraSelector extends _i1.Mock implements _i17.CameraSelector {
+class MockCameraSelector extends _i1.Mock implements _i18.CameraSelector {
   @override
-  _i14.Future<List<_i2.CameraInfo>> filter(List<_i2.CameraInfo>? cameraInfos) =>
+  _i15.Future<List<_i2.CameraInfo>> filter(List<_i2.CameraInfo>? cameraInfos) =>
       (super.noSuchMethod(
         Invocation.method(
           #filter,
           [cameraInfos],
         ),
         returnValue:
-            _i14.Future<List<_i2.CameraInfo>>.value(<_i2.CameraInfo>[]),
+            _i15.Future<List<_i2.CameraInfo>>.value(<_i2.CameraInfo>[]),
         returnValueForMissingStub:
-            _i14.Future<List<_i2.CameraInfo>>.value(<_i2.CameraInfo>[]),
-      ) as _i14.Future<List<_i2.CameraInfo>>);
+            _i15.Future<List<_i2.CameraInfo>>.value(<_i2.CameraInfo>[]),
+      ) as _i15.Future<List<_i2.CameraInfo>>);
 }
 
 /// A class which mocks [ExposureState].
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockExposureState extends _i1.Mock implements _i4.ExposureState {
+class MockExposureState extends _i1.Mock implements _i5.ExposureState {
   @override
-  _i6.ExposureCompensationRange get exposureCompensationRange =>
+  _i7.ExposureCompensationRange get exposureCompensationRange =>
       (super.noSuchMethod(
         Invocation.getter(#exposureCompensationRange),
-        returnValue: _FakeExposureCompensationRange_4(
+        returnValue: _FakeExposureCompensationRange_5(
           this,
           Invocation.getter(#exposureCompensationRange),
         ),
-        returnValueForMissingStub: _FakeExposureCompensationRange_4(
+        returnValueForMissingStub: _FakeExposureCompensationRange_5(
           this,
           Invocation.getter(#exposureCompensationRange),
         ),
-      ) as _i6.ExposureCompensationRange);
+      ) as _i7.ExposureCompensationRange);
   @override
   double get exposureCompensationStep => (super.noSuchMethod(
         Invocation.getter(#exposureCompensationStep),
@@ -407,57 +456,57 @@
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockImageAnalysis extends _i1.Mock implements _i18.ImageAnalysis {
+class MockImageAnalysis extends _i1.Mock implements _i19.ImageAnalysis {
   @override
-  _i14.Future<void> setAnalyzer(_i19.Analyzer? analyzer) => (super.noSuchMethod(
+  _i15.Future<void> setAnalyzer(_i20.Analyzer? analyzer) => (super.noSuchMethod(
         Invocation.method(
           #setAnalyzer,
           [analyzer],
         ),
-        returnValue: _i14.Future<void>.value(),
-        returnValueForMissingStub: _i14.Future<void>.value(),
-      ) as _i14.Future<void>);
+        returnValue: _i15.Future<void>.value(),
+        returnValueForMissingStub: _i15.Future<void>.value(),
+      ) as _i15.Future<void>);
   @override
-  _i14.Future<void> clearAnalyzer() => (super.noSuchMethod(
+  _i15.Future<void> clearAnalyzer() => (super.noSuchMethod(
         Invocation.method(
           #clearAnalyzer,
           [],
         ),
-        returnValue: _i14.Future<void>.value(),
-        returnValueForMissingStub: _i14.Future<void>.value(),
-      ) as _i14.Future<void>);
+        returnValue: _i15.Future<void>.value(),
+        returnValueForMissingStub: _i15.Future<void>.value(),
+      ) as _i15.Future<void>);
 }
 
 /// A class which mocks [ImageCapture].
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockImageCapture extends _i1.Mock implements _i20.ImageCapture {
+class MockImageCapture extends _i1.Mock implements _i21.ImageCapture {
   @override
-  _i14.Future<void> setFlashMode(int? newFlashMode) => (super.noSuchMethod(
+  _i15.Future<void> setFlashMode(int? newFlashMode) => (super.noSuchMethod(
         Invocation.method(
           #setFlashMode,
           [newFlashMode],
         ),
-        returnValue: _i14.Future<void>.value(),
-        returnValueForMissingStub: _i14.Future<void>.value(),
-      ) as _i14.Future<void>);
+        returnValue: _i15.Future<void>.value(),
+        returnValueForMissingStub: _i15.Future<void>.value(),
+      ) as _i15.Future<void>);
   @override
-  _i14.Future<String> takePicture() => (super.noSuchMethod(
+  _i15.Future<String> takePicture() => (super.noSuchMethod(
         Invocation.method(
           #takePicture,
           [],
         ),
-        returnValue: _i14.Future<String>.value(''),
-        returnValueForMissingStub: _i14.Future<String>.value(''),
-      ) as _i14.Future<String>);
+        returnValue: _i15.Future<String>.value(''),
+        returnValueForMissingStub: _i15.Future<String>.value(''),
+      ) as _i15.Future<String>);
 }
 
 /// A class which mocks [ImageProxy].
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockImageProxy extends _i1.Mock implements _i21.ImageProxy {
+class MockImageProxy extends _i1.Mock implements _i22.ImageProxy {
   @override
   int get format => (super.noSuchMethod(
         Invocation.getter(#format),
@@ -477,38 +526,38 @@
         returnValueForMissingStub: 0,
       ) as int);
   @override
-  _i14.Future<List<_i22.PlaneProxy>> getPlanes() => (super.noSuchMethod(
+  _i15.Future<List<_i23.PlaneProxy>> getPlanes() => (super.noSuchMethod(
         Invocation.method(
           #getPlanes,
           [],
         ),
         returnValue:
-            _i14.Future<List<_i22.PlaneProxy>>.value(<_i22.PlaneProxy>[]),
+            _i15.Future<List<_i23.PlaneProxy>>.value(<_i23.PlaneProxy>[]),
         returnValueForMissingStub:
-            _i14.Future<List<_i22.PlaneProxy>>.value(<_i22.PlaneProxy>[]),
-      ) as _i14.Future<List<_i22.PlaneProxy>>);
+            _i15.Future<List<_i23.PlaneProxy>>.value(<_i23.PlaneProxy>[]),
+      ) as _i15.Future<List<_i23.PlaneProxy>>);
   @override
-  _i14.Future<void> close() => (super.noSuchMethod(
+  _i15.Future<void> close() => (super.noSuchMethod(
         Invocation.method(
           #close,
           [],
         ),
-        returnValue: _i14.Future<void>.value(),
-        returnValueForMissingStub: _i14.Future<void>.value(),
-      ) as _i14.Future<void>);
+        returnValue: _i15.Future<void>.value(),
+        returnValueForMissingStub: _i15.Future<void>.value(),
+      ) as _i15.Future<void>);
 }
 
 /// A class which mocks [PlaneProxy].
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockPlaneProxy extends _i1.Mock implements _i22.PlaneProxy {
+class MockPlaneProxy extends _i1.Mock implements _i23.PlaneProxy {
   @override
-  _i23.Uint8List get buffer => (super.noSuchMethod(
+  _i24.Uint8List get buffer => (super.noSuchMethod(
         Invocation.getter(#buffer),
-        returnValue: _i23.Uint8List(0),
-        returnValueForMissingStub: _i23.Uint8List(0),
-      ) as _i23.Uint8List);
+        returnValue: _i24.Uint8List(0),
+        returnValueForMissingStub: _i24.Uint8List(0),
+      ) as _i24.Uint8List);
   @override
   int get pixelStride => (super.noSuchMethod(
         Invocation.getter(#pixelStride),
@@ -527,16 +576,16 @@
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockPreview extends _i1.Mock implements _i24.Preview {
+class MockPreview extends _i1.Mock implements _i25.Preview {
   @override
-  _i14.Future<int> setSurfaceProvider() => (super.noSuchMethod(
+  _i15.Future<int> setSurfaceProvider() => (super.noSuchMethod(
         Invocation.method(
           #setSurfaceProvider,
           [],
         ),
-        returnValue: _i14.Future<int>.value(0),
-        returnValueForMissingStub: _i14.Future<int>.value(0),
-      ) as _i14.Future<int>);
+        returnValue: _i15.Future<int>.value(0),
+        returnValueForMissingStub: _i15.Future<int>.value(0),
+      ) as _i15.Future<int>);
   @override
   void releaseFlutterSurfaceTexture() => super.noSuchMethod(
         Invocation.method(
@@ -546,13 +595,13 @@
         returnValueForMissingStub: null,
       );
   @override
-  _i14.Future<_i6.ResolutionInfo> getResolutionInfo() => (super.noSuchMethod(
+  _i15.Future<_i7.ResolutionInfo> getResolutionInfo() => (super.noSuchMethod(
         Invocation.method(
           #getResolutionInfo,
           [],
         ),
         returnValue:
-            _i14.Future<_i6.ResolutionInfo>.value(_FakeResolutionInfo_5(
+            _i15.Future<_i7.ResolutionInfo>.value(_FakeResolutionInfo_6(
           this,
           Invocation.method(
             #getResolutionInfo,
@@ -560,14 +609,14 @@
           ),
         )),
         returnValueForMissingStub:
-            _i14.Future<_i6.ResolutionInfo>.value(_FakeResolutionInfo_5(
+            _i15.Future<_i7.ResolutionInfo>.value(_FakeResolutionInfo_6(
           this,
           Invocation.method(
             #getResolutionInfo,
             [],
           ),
         )),
-      ) as _i14.Future<_i6.ResolutionInfo>);
+      ) as _i15.Future<_i7.ResolutionInfo>);
 }
 
 /// A class which mocks [ProcessCameraProvider].
@@ -575,23 +624,23 @@
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
 class MockProcessCameraProvider extends _i1.Mock
-    implements _i25.ProcessCameraProvider {
+    implements _i26.ProcessCameraProvider {
   @override
-  _i14.Future<List<_i2.CameraInfo>> getAvailableCameraInfos() =>
+  _i15.Future<List<_i2.CameraInfo>> getAvailableCameraInfos() =>
       (super.noSuchMethod(
         Invocation.method(
           #getAvailableCameraInfos,
           [],
         ),
         returnValue:
-            _i14.Future<List<_i2.CameraInfo>>.value(<_i2.CameraInfo>[]),
+            _i15.Future<List<_i2.CameraInfo>>.value(<_i2.CameraInfo>[]),
         returnValueForMissingStub:
-            _i14.Future<List<_i2.CameraInfo>>.value(<_i2.CameraInfo>[]),
-      ) as _i14.Future<List<_i2.CameraInfo>>);
+            _i15.Future<List<_i2.CameraInfo>>.value(<_i2.CameraInfo>[]),
+      ) as _i15.Future<List<_i2.CameraInfo>>);
   @override
-  _i14.Future<_i7.Camera> bindToLifecycle(
-    _i17.CameraSelector? cameraSelector,
-    List<_i26.UseCase>? useCases,
+  _i15.Future<_i8.Camera> bindToLifecycle(
+    _i18.CameraSelector? cameraSelector,
+    List<_i27.UseCase>? useCases,
   ) =>
       (super.noSuchMethod(
         Invocation.method(
@@ -601,7 +650,7 @@
             useCases,
           ],
         ),
-        returnValue: _i14.Future<_i7.Camera>.value(_FakeCamera_6(
+        returnValue: _i15.Future<_i8.Camera>.value(_FakeCamera_7(
           this,
           Invocation.method(
             #bindToLifecycle,
@@ -611,7 +660,7 @@
             ],
           ),
         )),
-        returnValueForMissingStub: _i14.Future<_i7.Camera>.value(_FakeCamera_6(
+        returnValueForMissingStub: _i15.Future<_i8.Camera>.value(_FakeCamera_7(
           this,
           Invocation.method(
             #bindToLifecycle,
@@ -621,18 +670,18 @@
             ],
           ),
         )),
-      ) as _i14.Future<_i7.Camera>);
+      ) as _i15.Future<_i8.Camera>);
   @override
-  _i14.Future<bool> isBound(_i26.UseCase? useCase) => (super.noSuchMethod(
+  _i15.Future<bool> isBound(_i27.UseCase? useCase) => (super.noSuchMethod(
         Invocation.method(
           #isBound,
           [useCase],
         ),
-        returnValue: _i14.Future<bool>.value(false),
-        returnValueForMissingStub: _i14.Future<bool>.value(false),
-      ) as _i14.Future<bool>);
+        returnValue: _i15.Future<bool>.value(false),
+        returnValueForMissingStub: _i15.Future<bool>.value(false),
+      ) as _i15.Future<bool>);
   @override
-  void unbind(List<_i26.UseCase>? useCases) => super.noSuchMethod(
+  void unbind(List<_i27.UseCase>? useCases) => super.noSuchMethod(
         Invocation.method(
           #unbind,
           [useCases],
@@ -653,16 +702,16 @@
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockRecorder extends _i1.Mock implements _i10.Recorder {
+class MockRecorder extends _i1.Mock implements _i11.Recorder {
   @override
-  _i14.Future<_i8.PendingRecording> prepareRecording(String? path) =>
+  _i15.Future<_i9.PendingRecording> prepareRecording(String? path) =>
       (super.noSuchMethod(
         Invocation.method(
           #prepareRecording,
           [path],
         ),
         returnValue:
-            _i14.Future<_i8.PendingRecording>.value(_FakePendingRecording_7(
+            _i15.Future<_i9.PendingRecording>.value(_FakePendingRecording_8(
           this,
           Invocation.method(
             #prepareRecording,
@@ -670,28 +719,28 @@
           ),
         )),
         returnValueForMissingStub:
-            _i14.Future<_i8.PendingRecording>.value(_FakePendingRecording_7(
+            _i15.Future<_i9.PendingRecording>.value(_FakePendingRecording_8(
           this,
           Invocation.method(
             #prepareRecording,
             [path],
           ),
         )),
-      ) as _i14.Future<_i8.PendingRecording>);
+      ) as _i15.Future<_i9.PendingRecording>);
 }
 
 /// A class which mocks [PendingRecording].
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockPendingRecording extends _i1.Mock implements _i8.PendingRecording {
+class MockPendingRecording extends _i1.Mock implements _i9.PendingRecording {
   @override
-  _i14.Future<_i9.Recording> start() => (super.noSuchMethod(
+  _i15.Future<_i10.Recording> start() => (super.noSuchMethod(
         Invocation.method(
           #start,
           [],
         ),
-        returnValue: _i14.Future<_i9.Recording>.value(_FakeRecording_8(
+        returnValue: _i15.Future<_i10.Recording>.value(_FakeRecording_9(
           this,
           Invocation.method(
             #start,
@@ -699,71 +748,71 @@
           ),
         )),
         returnValueForMissingStub:
-            _i14.Future<_i9.Recording>.value(_FakeRecording_8(
+            _i15.Future<_i10.Recording>.value(_FakeRecording_9(
           this,
           Invocation.method(
             #start,
             [],
           ),
         )),
-      ) as _i14.Future<_i9.Recording>);
+      ) as _i15.Future<_i10.Recording>);
 }
 
 /// A class which mocks [Recording].
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockRecording extends _i1.Mock implements _i9.Recording {
+class MockRecording extends _i1.Mock implements _i10.Recording {
   @override
-  _i14.Future<void> close() => (super.noSuchMethod(
+  _i15.Future<void> close() => (super.noSuchMethod(
         Invocation.method(
           #close,
           [],
         ),
-        returnValue: _i14.Future<void>.value(),
-        returnValueForMissingStub: _i14.Future<void>.value(),
-      ) as _i14.Future<void>);
+        returnValue: _i15.Future<void>.value(),
+        returnValueForMissingStub: _i15.Future<void>.value(),
+      ) as _i15.Future<void>);
   @override
-  _i14.Future<void> pause() => (super.noSuchMethod(
+  _i15.Future<void> pause() => (super.noSuchMethod(
         Invocation.method(
           #pause,
           [],
         ),
-        returnValue: _i14.Future<void>.value(),
-        returnValueForMissingStub: _i14.Future<void>.value(),
-      ) as _i14.Future<void>);
+        returnValue: _i15.Future<void>.value(),
+        returnValueForMissingStub: _i15.Future<void>.value(),
+      ) as _i15.Future<void>);
   @override
-  _i14.Future<void> resume() => (super.noSuchMethod(
+  _i15.Future<void> resume() => (super.noSuchMethod(
         Invocation.method(
           #resume,
           [],
         ),
-        returnValue: _i14.Future<void>.value(),
-        returnValueForMissingStub: _i14.Future<void>.value(),
-      ) as _i14.Future<void>);
+        returnValue: _i15.Future<void>.value(),
+        returnValueForMissingStub: _i15.Future<void>.value(),
+      ) as _i15.Future<void>);
   @override
-  _i14.Future<void> stop() => (super.noSuchMethod(
+  _i15.Future<void> stop() => (super.noSuchMethod(
         Invocation.method(
           #stop,
           [],
         ),
-        returnValue: _i14.Future<void>.value(),
-        returnValueForMissingStub: _i14.Future<void>.value(),
-      ) as _i14.Future<void>);
+        returnValue: _i15.Future<void>.value(),
+        returnValueForMissingStub: _i15.Future<void>.value(),
+      ) as _i15.Future<void>);
 }
 
 /// A class which mocks [VideoCapture].
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockVideoCapture extends _i1.Mock implements _i27.VideoCapture {
+class MockVideoCapture extends _i1.Mock implements _i28.VideoCapture {
   @override
-  _i14.Future<_i10.Recorder> getOutput() => (super.noSuchMethod(
+  _i15.Future<_i11.Recorder> getOutput() => (super.noSuchMethod(
         Invocation.method(
           #getOutput,
           [],
         ),
-        returnValue: _i14.Future<_i10.Recorder>.value(_FakeRecorder_9(
+        returnValue: _i15.Future<_i11.Recorder>.value(_FakeRecorder_10(
           this,
           Invocation.method(
             #getOutput,
@@ -771,32 +820,32 @@
           ),
         )),
         returnValueForMissingStub:
-            _i14.Future<_i10.Recorder>.value(_FakeRecorder_9(
+            _i15.Future<_i11.Recorder>.value(_FakeRecorder_10(
           this,
           Invocation.method(
             #getOutput,
             [],
           ),
         )),
-      ) as _i14.Future<_i10.Recorder>);
+      ) as _i15.Future<_i11.Recorder>);
 }
 
 /// A class which mocks [BuildContext].
 ///
 /// See the documentation for Mockito's code generation for more information.
-class MockBuildContext extends _i1.Mock implements _i11.BuildContext {
+class MockBuildContext extends _i1.Mock implements _i12.BuildContext {
   @override
-  _i11.Widget get widget => (super.noSuchMethod(
+  _i12.Widget get widget => (super.noSuchMethod(
         Invocation.getter(#widget),
-        returnValue: _FakeWidget_10(
+        returnValue: _FakeWidget_11(
           this,
           Invocation.getter(#widget),
         ),
-        returnValueForMissingStub: _FakeWidget_10(
+        returnValueForMissingStub: _FakeWidget_11(
           this,
           Invocation.getter(#widget),
         ),
-      ) as _i11.Widget);
+      ) as _i12.Widget);
   @override
   bool get mounted => (super.noSuchMethod(
         Invocation.getter(#mounted),
@@ -810,8 +859,8 @@
         returnValueForMissingStub: false,
       ) as bool);
   @override
-  _i11.InheritedWidget dependOnInheritedElement(
-    _i11.InheritedElement? ancestor, {
+  _i12.InheritedWidget dependOnInheritedElement(
+    _i12.InheritedElement? ancestor, {
     Object? aspect,
   }) =>
       (super.noSuchMethod(
@@ -820,7 +869,7 @@
           [ancestor],
           {#aspect: aspect},
         ),
-        returnValue: _FakeInheritedWidget_11(
+        returnValue: _FakeInheritedWidget_12(
           this,
           Invocation.method(
             #dependOnInheritedElement,
@@ -828,7 +877,7 @@
             {#aspect: aspect},
           ),
         ),
-        returnValueForMissingStub: _FakeInheritedWidget_11(
+        returnValueForMissingStub: _FakeInheritedWidget_12(
           this,
           Invocation.method(
             #dependOnInheritedElement,
@@ -836,9 +885,9 @@
             {#aspect: aspect},
           ),
         ),
-      ) as _i11.InheritedWidget);
+      ) as _i12.InheritedWidget);
   @override
-  void visitAncestorElements(_i11.ConditionalElementVisitor? visitor) =>
+  void visitAncestorElements(_i12.ConditionalElementVisitor? visitor) =>
       super.noSuchMethod(
         Invocation.method(
           #visitAncestorElements,
@@ -847,7 +896,7 @@
         returnValueForMissingStub: null,
       );
   @override
-  void visitChildElements(_i11.ElementVisitor? visitor) => super.noSuchMethod(
+  void visitChildElements(_i12.ElementVisitor? visitor) => super.noSuchMethod(
         Invocation.method(
           #visitChildElements,
           [visitor],
@@ -855,7 +904,7 @@
         returnValueForMissingStub: null,
       );
   @override
-  void dispatchNotification(_i11.Notification? notification) =>
+  void dispatchNotification(_i12.Notification? notification) =>
       super.noSuchMethod(
         Invocation.method(
           #dispatchNotification,
@@ -864,9 +913,9 @@
         returnValueForMissingStub: null,
       );
   @override
-  _i13.DiagnosticsNode describeElement(
+  _i14.DiagnosticsNode describeElement(
     String? name, {
-    _i13.DiagnosticsTreeStyle? style = _i13.DiagnosticsTreeStyle.errorProperty,
+    _i14.DiagnosticsTreeStyle? style = _i14.DiagnosticsTreeStyle.errorProperty,
   }) =>
       (super.noSuchMethod(
         Invocation.method(
@@ -874,7 +923,7 @@
           [name],
           {#style: style},
         ),
-        returnValue: _FakeDiagnosticsNode_12(
+        returnValue: _FakeDiagnosticsNode_13(
           this,
           Invocation.method(
             #describeElement,
@@ -882,7 +931,7 @@
             {#style: style},
           ),
         ),
-        returnValueForMissingStub: _FakeDiagnosticsNode_12(
+        returnValueForMissingStub: _FakeDiagnosticsNode_13(
           this,
           Invocation.method(
             #describeElement,
@@ -890,11 +939,11 @@
             {#style: style},
           ),
         ),
-      ) as _i13.DiagnosticsNode);
+      ) as _i14.DiagnosticsNode);
   @override
-  _i13.DiagnosticsNode describeWidget(
+  _i14.DiagnosticsNode describeWidget(
     String? name, {
-    _i13.DiagnosticsTreeStyle? style = _i13.DiagnosticsTreeStyle.errorProperty,
+    _i14.DiagnosticsTreeStyle? style = _i14.DiagnosticsTreeStyle.errorProperty,
   }) =>
       (super.noSuchMethod(
         Invocation.method(
@@ -902,7 +951,7 @@
           [name],
           {#style: style},
         ),
-        returnValue: _FakeDiagnosticsNode_12(
+        returnValue: _FakeDiagnosticsNode_13(
           this,
           Invocation.method(
             #describeWidget,
@@ -910,7 +959,7 @@
             {#style: style},
           ),
         ),
-        returnValueForMissingStub: _FakeDiagnosticsNode_12(
+        returnValueForMissingStub: _FakeDiagnosticsNode_13(
           this,
           Invocation.method(
             #describeWidget,
@@ -918,9 +967,9 @@
             {#style: style},
           ),
         ),
-      ) as _i13.DiagnosticsNode);
+      ) as _i14.DiagnosticsNode);
   @override
-  List<_i13.DiagnosticsNode> describeMissingAncestor(
+  List<_i14.DiagnosticsNode> describeMissingAncestor(
           {required Type? expectedAncestorType}) =>
       (super.noSuchMethod(
         Invocation.method(
@@ -928,38 +977,38 @@
           [],
           {#expectedAncestorType: expectedAncestorType},
         ),
-        returnValue: <_i13.DiagnosticsNode>[],
-        returnValueForMissingStub: <_i13.DiagnosticsNode>[],
-      ) as List<_i13.DiagnosticsNode>);
+        returnValue: <_i14.DiagnosticsNode>[],
+        returnValueForMissingStub: <_i14.DiagnosticsNode>[],
+      ) as List<_i14.DiagnosticsNode>);
   @override
-  _i13.DiagnosticsNode describeOwnershipChain(String? name) =>
+  _i14.DiagnosticsNode describeOwnershipChain(String? name) =>
       (super.noSuchMethod(
         Invocation.method(
           #describeOwnershipChain,
           [name],
         ),
-        returnValue: _FakeDiagnosticsNode_12(
+        returnValue: _FakeDiagnosticsNode_13(
           this,
           Invocation.method(
             #describeOwnershipChain,
             [name],
           ),
         ),
-        returnValueForMissingStub: _FakeDiagnosticsNode_12(
+        returnValueForMissingStub: _FakeDiagnosticsNode_13(
           this,
           Invocation.method(
             #describeOwnershipChain,
             [name],
           ),
         ),
-      ) as _i13.DiagnosticsNode);
+      ) as _i14.DiagnosticsNode);
 }
 
 /// A class which mocks [TestInstanceManagerHostApi].
 ///
 /// See the documentation for Mockito's code generation for more information.
 class MockTestInstanceManagerHostApi extends _i1.Mock
-    implements _i28.TestInstanceManagerHostApi {
+    implements _i29.TestInstanceManagerHostApi {
   @override
   void clear() => super.noSuchMethod(
         Invocation.method(
@@ -974,19 +1023,19 @@
 ///
 /// See the documentation for Mockito's code generation for more information.
 class MockTestSystemServicesHostApi extends _i1.Mock
-    implements _i28.TestSystemServicesHostApi {
+    implements _i29.TestSystemServicesHostApi {
   @override
-  _i14.Future<_i6.CameraPermissionsErrorData?> requestCameraPermissions(
+  _i15.Future<_i7.CameraPermissionsErrorData?> requestCameraPermissions(
           bool? enableAudio) =>
       (super.noSuchMethod(
         Invocation.method(
           #requestCameraPermissions,
           [enableAudio],
         ),
-        returnValue: _i14.Future<_i6.CameraPermissionsErrorData?>.value(),
+        returnValue: _i15.Future<_i7.CameraPermissionsErrorData?>.value(),
         returnValueForMissingStub:
-            _i14.Future<_i6.CameraPermissionsErrorData?>.value(),
-      ) as _i14.Future<_i6.CameraPermissionsErrorData?>);
+            _i15.Future<_i7.CameraPermissionsErrorData?>.value(),
+      ) as _i15.Future<_i7.CameraPermissionsErrorData?>);
   @override
   void startListeningForDeviceOrientationChange(
     bool? isFrontFacing,
@@ -1032,7 +1081,7 @@
 ///
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
-class MockZoomState extends _i1.Mock implements _i16.ZoomState {
+class MockZoomState extends _i1.Mock implements _i17.ZoomState {
   @override
   double get minZoomRatio => (super.noSuchMethod(
         Invocation.getter(#minZoomRatio),
@@ -1052,30 +1101,30 @@
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
 class MockLiveCameraState extends _i1.Mock
-    implements _i3.LiveData<_i15.CameraState> {
+    implements _i4.LiveData<_i16.CameraState> {
   MockLiveCameraState() {
     _i1.throwOnMissingStub(this);
   }
 
   @override
-  _i14.Future<void> observe(_i29.Observer<_i15.CameraState>? observer) =>
+  _i15.Future<void> observe(_i30.Observer<_i16.CameraState>? observer) =>
       (super.noSuchMethod(
         Invocation.method(
           #observe,
           [observer],
         ),
-        returnValue: _i14.Future<void>.value(),
-        returnValueForMissingStub: _i14.Future<void>.value(),
-      ) as _i14.Future<void>);
+        returnValue: _i15.Future<void>.value(),
+        returnValueForMissingStub: _i15.Future<void>.value(),
+      ) as _i15.Future<void>);
   @override
-  _i14.Future<void> removeObservers() => (super.noSuchMethod(
+  _i15.Future<void> removeObservers() => (super.noSuchMethod(
         Invocation.method(
           #removeObservers,
           [],
         ),
-        returnValue: _i14.Future<void>.value(),
-        returnValueForMissingStub: _i14.Future<void>.value(),
-      ) as _i14.Future<void>);
+        returnValue: _i15.Future<void>.value(),
+        returnValueForMissingStub: _i15.Future<void>.value(),
+      ) as _i15.Future<void>);
 }
 
 /// A class which mocks [LiveData].
@@ -1083,28 +1132,28 @@
 /// See the documentation for Mockito's code generation for more information.
 // ignore: must_be_immutable
 class MockLiveZoomState extends _i1.Mock
-    implements _i3.LiveData<_i16.ZoomState> {
+    implements _i4.LiveData<_i17.ZoomState> {
   MockLiveZoomState() {
     _i1.throwOnMissingStub(this);
   }
 
   @override
-  _i14.Future<void> observe(_i29.Observer<_i16.ZoomState>? observer) =>
+  _i15.Future<void> observe(_i30.Observer<_i17.ZoomState>? observer) =>
       (super.noSuchMethod(
         Invocation.method(
           #observe,
           [observer],
         ),
-        returnValue: _i14.Future<void>.value(),
-        returnValueForMissingStub: _i14.Future<void>.value(),
-      ) as _i14.Future<void>);
+        returnValue: _i15.Future<void>.value(),
+        returnValueForMissingStub: _i15.Future<void>.value(),
+      ) as _i15.Future<void>);
   @override
-  _i14.Future<void> removeObservers() => (super.noSuchMethod(
+  _i15.Future<void> removeObservers() => (super.noSuchMethod(
         Invocation.method(
           #removeObservers,
           [],
         ),
-        returnValue: _i14.Future<void>.value(),
-        returnValueForMissingStub: _i14.Future<void>.value(),
-      ) as _i14.Future<void>);
+        returnValue: _i15.Future<void>.value(),
+        returnValueForMissingStub: _i15.Future<void>.value(),
+      ) as _i15.Future<void>);
 }
diff --git a/packages/camera/camera_android_camerax/test/camera_control_test.dart b/packages/camera/camera_android_camerax/test/camera_control_test.dart
new file mode 100644
index 0000000..99acc94
--- /dev/null
+++ b/packages/camera/camera_android_camerax/test/camera_control_test.dart
@@ -0,0 +1,67 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:camera_android_camerax/src/camera_control.dart';
+import 'package:camera_android_camerax/src/instance_manager.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+
+import 'camera_control_test.mocks.dart';
+import 'test_camerax_library.g.dart';
+
+@GenerateMocks(<Type>[TestCameraControlHostApi, TestInstanceManagerHostApi])
+void main() {
+  TestWidgetsFlutterBinding.ensureInitialized();
+
+  // Mocks the call to clear the native InstanceManager.
+  TestInstanceManagerHostApi.setup(MockTestInstanceManagerHostApi());
+
+  group('CameraControl', () {
+    tearDown(() => TestCameraHostApi.setup(null));
+
+    test('enableTorch makes call on Java side to enable torch', () async {
+      final MockTestCameraControlHostApi mockApi =
+          MockTestCameraControlHostApi();
+      TestCameraControlHostApi.setup(mockApi);
+
+      final InstanceManager instanceManager = InstanceManager(
+        onWeakReferenceRemoved: (_) {},
+      );
+
+      final CameraControl cameraControl = CameraControl.detached(
+        instanceManager: instanceManager,
+      );
+      const int cameraControlIdentifier = 22;
+
+      instanceManager.addHostCreatedInstance(
+        cameraControl,
+        cameraControlIdentifier,
+        onCopy: (_) => CameraControl.detached(instanceManager: instanceManager),
+      );
+
+      const bool enableTorch = true;
+      await cameraControl.enableTorch(enableTorch);
+
+      verify(mockApi.enableTorch(cameraControlIdentifier, enableTorch));
+    });
+
+    test('flutterApiCreate makes call to add instance to instance manager', () {
+      final InstanceManager instanceManager = InstanceManager(
+        onWeakReferenceRemoved: (_) {},
+      );
+      final CameraControlFlutterApiImpl flutterApi =
+          CameraControlFlutterApiImpl(
+        instanceManager: instanceManager,
+      );
+      const int cameraControlIdentifier = 67;
+
+      flutterApi.create(cameraControlIdentifier);
+
+      expect(
+          instanceManager.getInstanceWithWeakReference(cameraControlIdentifier),
+          isA<CameraControl>());
+    });
+  });
+}
diff --git a/packages/camera/camera_android_camerax/test/camera_control_test.mocks.dart b/packages/camera/camera_android_camerax/test/camera_control_test.mocks.dart
new file mode 100644
index 0000000..0f43f0c
--- /dev/null
+++ b/packages/camera/camera_android_camerax/test/camera_control_test.mocks.dart
@@ -0,0 +1,69 @@
+// Mocks generated by Mockito 5.4.1 from annotations
+// in camera_android_camerax/test/camera_control_test.dart.
+// Do not manually edit this file.
+
+// @dart=2.19
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:async' as _i3;
+
+import 'package:mockito/mockito.dart' as _i1;
+
+import 'test_camerax_library.g.dart' as _i2;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+/// A class which mocks [TestCameraControlHostApi].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockTestCameraControlHostApi extends _i1.Mock
+    implements _i2.TestCameraControlHostApi {
+  MockTestCameraControlHostApi() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  _i3.Future<void> enableTorch(
+    int? identifier,
+    bool? torch,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #enableTorch,
+          [
+            identifier,
+            torch,
+          ],
+        ),
+        returnValue: _i3.Future<void>.value(),
+        returnValueForMissingStub: _i3.Future<void>.value(),
+      ) as _i3.Future<void>);
+}
+
+/// A class which mocks [TestInstanceManagerHostApi].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockTestInstanceManagerHostApi extends _i1.Mock
+    implements _i2.TestInstanceManagerHostApi {
+  MockTestInstanceManagerHostApi() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  void clear() => super.noSuchMethod(
+        Invocation.method(
+          #clear,
+          [],
+        ),
+        returnValueForMissingStub: null,
+      );
+}
diff --git a/packages/camera/camera_android_camerax/test/camera_test.dart b/packages/camera/camera_android_camerax/test/camera_test.dart
index 05f5fe7..22d9dab 100644
--- a/packages/camera/camera_android_camerax/test/camera_test.dart
+++ b/packages/camera/camera_android_camerax/test/camera_test.dart
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 import 'package:camera_android_camerax/src/camera.dart';
+import 'package:camera_android_camerax/src/camera_control.dart';
 import 'package:camera_android_camerax/src/camera_info.dart';
 import 'package:camera_android_camerax/src/instance_manager.dart';
 import 'package:flutter_test/flutter_test.dart';
@@ -54,6 +55,39 @@
       verify(mockApi.getCameraInfo(cameraIdentifier));
     });
 
+    test('getCameraControl makes call to retrieve expected CameraControl',
+        () async {
+      final MockTestCameraHostApi mockApi = MockTestCameraHostApi();
+      TestCameraHostApi.setup(mockApi);
+
+      final InstanceManager instanceManager = InstanceManager(
+        onWeakReferenceRemoved: (_) {},
+      );
+
+      final Camera camera = Camera.detached(
+        instanceManager: instanceManager,
+      );
+      const int cameraIdentifier = 42;
+      final CameraControl cameraControl = CameraControl.detached();
+      const int cameraControlIdentifier = 8;
+      instanceManager.addHostCreatedInstance(
+        camera,
+        cameraIdentifier,
+        onCopy: (_) => Camera.detached(instanceManager: instanceManager),
+      );
+      instanceManager.addHostCreatedInstance(
+        cameraControl,
+        cameraControlIdentifier,
+        onCopy: (_) => CameraControl.detached(instanceManager: instanceManager),
+      );
+
+      when(mockApi.getCameraControl(cameraIdentifier))
+          .thenAnswer((_) => cameraControlIdentifier);
+
+      expect(await camera.getCameraControl(), equals(cameraControl));
+      verify(mockApi.getCameraControl(cameraIdentifier));
+    });
+
     test('flutterApiCreate makes call to add instance to instance manager', () {
       final InstanceManager instanceManager = InstanceManager(
         onWeakReferenceRemoved: (_) {},
diff --git a/packages/camera/camera_android_camerax/test/camera_test.mocks.dart b/packages/camera/camera_android_camerax/test/camera_test.mocks.dart
index 47a15ae..7bae865 100644
--- a/packages/camera/camera_android_camerax/test/camera_test.mocks.dart
+++ b/packages/camera/camera_android_camerax/test/camera_test.mocks.dart
@@ -36,6 +36,14 @@
         ),
         returnValue: 0,
       ) as int);
+  @override
+  int getCameraControl(int? identifier) => (super.noSuchMethod(
+        Invocation.method(
+          #getCameraControl,
+          [identifier],
+        ),
+        returnValue: 0,
+      ) as int);
 }
 
 /// A class which mocks [TestInstanceManagerHostApi].
diff --git a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart
index c649a73..88e44ae 100644
--- a/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart
+++ b/packages/camera/camera_android_camerax/test/test_camerax_library.g.dart
@@ -426,6 +426,8 @@
 
   int getCameraInfo(int identifier);
 
+  int getCameraControl(int identifier);
+
   static void setup(TestCameraHostApi? api,
       {BinaryMessenger? binaryMessenger}) {
     {
@@ -450,6 +452,28 @@
         });
       }
     }
+    {
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.CameraHostApi.getCameraControl', codec,
+          binaryMessenger: binaryMessenger);
+      if (api == null) {
+        _testBinaryMessengerBinding!.defaultBinaryMessenger
+            .setMockDecodedMessageHandler<Object?>(channel, null);
+      } else {
+        _testBinaryMessengerBinding!.defaultBinaryMessenger
+            .setMockDecodedMessageHandler<Object?>(channel,
+                (Object? message) async {
+          assert(message != null,
+              'Argument for dev.flutter.pigeon.CameraHostApi.getCameraControl was null.');
+          final List<Object?> args = (message as List<Object?>?)!;
+          final int? arg_identifier = (args[0] as int?);
+          assert(arg_identifier != null,
+              'Argument for dev.flutter.pigeon.CameraHostApi.getCameraControl was null, expected non-null int.');
+          final int output = api.getCameraControl(arg_identifier!);
+          return <Object?>[output];
+        });
+      }
+    }
   }
 }
 
@@ -1721,3 +1745,40 @@
     }
   }
 }
+
+abstract class TestCameraControlHostApi {
+  static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding =>
+      TestDefaultBinaryMessengerBinding.instance;
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
+
+  Future<void> enableTorch(int identifier, bool torch);
+
+  static void setup(TestCameraControlHostApi? api,
+      {BinaryMessenger? binaryMessenger}) {
+    {
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.CameraControlHostApi.enableTorch', codec,
+          binaryMessenger: binaryMessenger);
+      if (api == null) {
+        _testBinaryMessengerBinding!.defaultBinaryMessenger
+            .setMockDecodedMessageHandler<Object?>(channel, null);
+      } else {
+        _testBinaryMessengerBinding!.defaultBinaryMessenger
+            .setMockDecodedMessageHandler<Object?>(channel,
+                (Object? message) async {
+          assert(message != null,
+              'Argument for dev.flutter.pigeon.CameraControlHostApi.enableTorch was null.');
+          final List<Object?> args = (message as List<Object?>?)!;
+          final int? arg_identifier = (args[0] as int?);
+          assert(arg_identifier != null,
+              'Argument for dev.flutter.pigeon.CameraControlHostApi.enableTorch was null, expected non-null int.');
+          final bool? arg_torch = (args[1] as bool?);
+          assert(arg_torch != null,
+              'Argument for dev.flutter.pigeon.CameraControlHostApi.enableTorch was null, expected non-null bool.');
+          await api.enableTorch(arg_identifier!, arg_torch!);
+          return <Object?>[];
+        });
+      }
+    }
+  }
+}