Photo by Akshay Kumawat on Unsplash

Camera in Compose Multiplatform / Kotlin Multiplatform

abdul basit

--

Have you ever wondered how we can use Camera in Compose Multiplatform or if it is possible to access those APIs?

The answer is yes, and it is very easy to do. For using the Camera, we will be using the Expect/Actual mechanism.

Let’s start

First, we need the initial project setup, and for that, we can use some templates, like this one from JetBrains. It will give us the initial project. Download or use the template to create the project and then run that.

Now the next thing is to start with the Camera implementation. In our common module, we will add an expect function

@Composable
expect fun CameraView()

Now we need to provide the implementation for this on each platform.

Android Implementation

Let’s start first with Android. I will not explain in depth the camera setup. If you want more details about it, please check this about CameraX.

We need to implement the CameraView on the Android side.

@Composable
actual fun CameraView() {

val context = LocalContext.current
val lifeCycleOwner = LocalLifecycleOwner.current

val cameraProviderFuture = remember { ProcessCameraProvider.getInstance(context) }
val previewView = remember { PreviewView(context) }

AndroidView(
factory = { previewView },
modifier = Modifier.fillMaxSize(),
update = {
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build()
preview.setSurfaceProvider(previewView.surfaceProvider)
val cameraSelector =
CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
cameraProvider.bindToLifecycle(lifeCycleOwner, cameraSelector, preview)
})
}

And that is it. Next, we need to add Camera permission on Manifest file.

With that, we are done with the Android implementation of this feature.

iOS implementation

Let’s start with the iOS implementation.

In our iOS module, we need to provide the actual implementation for Camera View.

@OptIn(ExperimentalForeignApi::class)
@Composable
actual fun CameraView() {
val device = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo).firstOrNull { device ->
(device as AVCaptureDevice).position == AVCaptureDevicePositionBack
}!! as AVCaptureDevice

val input = AVCaptureDeviceInput.deviceInputWithDevice(device, null) as AVCaptureDeviceInput

val output = AVCaptureStillImageOutput()
output.outputSettings = mapOf(AVVideoCodecKey to AVVideoCodecJPEG)

val session = AVCaptureSession()

session.sessionPreset = AVCaptureSessionPresetPhoto

session.addInput(input)
session.addOutput(output)

val cameraPreviewLayer = remember { AVCaptureVideoPreviewLayer(session = session) }

UIKitView(
modifier = Modifier.fillMaxSize(),
background = Color.Black,
factory = {
val container = UIView()
container.layer.addSublayer(cameraPreviewLayer)
cameraPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
session.startRunning()
container
},
onResize = { container: UIView, rect: CValue<CGRect> ->
CATransaction.begin()
CATransaction.setValue(true, kCATransactionDisableActions)
container.layer.setFrame(rect)
cameraPreviewLayer.setFrame(rect)
CATransaction.commit()
})
}

If you want to know more about Camera on iOS, check out this article.

After that, we need to add info in Info.plist for camera

Next call that CameraView from App Composable.

@Composable
fun App() {
MaterialTheme {
CameraView()
}
}

And that is all.

Demo

Here is the code: https://github.com/SEAbdulbasit/Camera-KMP

Here is a video tutorial on this: https://www.youtube.com/watch?v=JWO-0aknrr0&t=8s&ab_channel=AbdulBasit

--

--