Camera in Compose Multiplatform / Kotlin Multiplatform
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