The CameraK Library is a camera solution designed for Compose Multiplatform, currently supporting both Android and iOS. While there are plans to expand support to additional platforms, this will take time.
CameraK offers features such as Camera Preview, Image Capture, Saving Images Locally, and Exposing Images as ByteArrays. It has a plugin-based API that allows developers to extend and enhance its functionality. Currently, two plugins are available: one for saving images and another for scanning QR codes.
Like What you see?
Buy me a coffee
implementation("io.github.kashif-mehmood-km:camerak:+")
if you want to save images to device add the plugin:
implementation("io.github.kashif-mehmood-km:image_saver_plugin:0.0.1")
if you want to add QR scanning capability then you need the QR scanner plugin:
implementation("io.github.kashif-mehmood-km:qr_scanner_plugin:0.0.2")
- Android
- IOS
- More will be added later
This is a simple example of how to use the CameraK library in your app.
Add the following permissions to your AndroidManifest.xml
file:
<uses-permission android:name="android.permission.CAMERA" /><uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Add the following to your info.plist file for iOS
<key>NSCameraUsageDescription</key><string>Camera permission is required for the app to work.
</string><key>NSPhotoLibraryUsageDescription</key><string>Photo Library permission is required for
the app to work.
</string>
Checking for permission and asking if needed
// Initialize Camera Permission State based on current permission status
val cameraPermissionState = remember {
mutableStateOf(
permissions.hasCameraPermission()
)
}
// Initialize Storage Permission State
val storagePermissionState = remember {
mutableStateOf(
permissions.hasStoragePermission()
)
}
if (!cameraPermissionState.value) {
permissions.RequestStoragePermission(onGranted = { cameraPermissionState.value = true },
onDenied = {
println("Camera Permission Denied")
})
}
if (!storagePermissionState.value) {
permissions.RequestStoragePermission(onGranted = { storagePermissionState.value = true },
onDenied = {
println("Storage Permission Denied")
})
}
// Initialize CameraController only when permissions are granted
if (cameraPermissionState.value && storagePermissionState.value) {
}
If permissions are granted we first create a camera controller
val cameraController = remember { mutableStateOf<CameraController?>(null) }
After this if needed, create plugins
val imageSaverPlugin = rememberImageSaverPlugin(
config = ImageSaverConfig(
isAutoSave = false, // Set to true to enable automatic saving
prefix = "MyApp", // Prefix for image names when auto-saving
directory = Directory.PICTURES, // Directory to save images
customFolderName = "CustomFolder" // Custom folder name within the directory, only works on android for now
)
)
val qrScannerPlugin = rememberQRScannerPlugin(coroutineScope = coroutineScope)
LaunchedEffect(Unit) {
qrScannerPlugin.getQrCodeFlow().distinctUntilChanged()
.collectLatest { qrCode ->
println("QR Code Detected flow: $qrCode")
snackbarHostState.showSnackbar("QR Code Detected flow: $qrCode")
qrScannerPlugin.pauseScanning()
}
}
After this we can create a Camera Preview and pass camera configuration, the it will create a
CameraController
and we can get it from the onCameraControllerReady
callback. Once we get the
controller we can then show the camera screen.
CameraPreview(modifier = Modifier.fillMaxSize(), cameraConfiguration = {
setCameraLens(CameraLens.BACK)
setFlashMode(FlashMode.OFF)
setImageFormat(ImageFormat.JPEG)
setDirectory(Directory.PICTURES)
addPlugin(imageSaverPlugin)
addPlugin(qrScannerPlugin)
}, onCameraControllerReady = {
cameraController.value = it
println("Camera Controller Ready ${cameraController.value}")
qrScannerPlugin.startScanning()
})
cameraController.value?.let { controller ->
CameraScreen(cameraController = controller, imageSaverPlugin)
}
Here is a sample camera screen that is in the Sample
module
@OptIn(ExperimentalResourceApi::class, ExperimentalUuidApi::class)
@Composable
fun CameraScreen(cameraController: CameraController, imageSaverPlugin: ImageSaverPlugin) {
val scope = rememberCoroutineScope()
var imageBitmap by remember { mutableStateOf<ImageBitmap?>(null) }
var isFlashOn by remember { mutableStateOf(false) }
Box(
modifier = Modifier.fillMaxSize()
) {
Row(
modifier = Modifier.fillMaxWidth().padding(16.dp).align(Alignment.TopStart),
horizontalArrangement = Arrangement.SpaceBetween
) {
// Flash Mode Switch
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "Flash")
Spacer(modifier = Modifier.width(8.dp))
Switch(checked = isFlashOn, onCheckedChange = {
isFlashOn = it
cameraController.toggleFlashMode()
})
}
// Camera Lens Toggle Button
Button(onClick = { cameraController.toggleCameraLens() }) {
Text(text = "Toggle Lens")
}
}
// Capture Button at the Bottom Center
Button(
onClick = {
scope.launch {
when (val result = cameraController.takePicture()) {
is ImageCaptureResult.Success -> {
imageBitmap = result.byteArray.decodeToImageBitmap()
// If auto-save is disabled, manually save the image
if (!imageSaverPlugin.config.isAutoSave) {
// Generate a custom name or use default
val customName = "Manual_${Uuid.random().toHexString()}"
imageSaverPlugin.saveImage(
byteArray = result.byteArray, imageName = customName
)
}
}
is ImageCaptureResult.Error -> {
println("Image Capture Error: ${result.exception.message}")
}
}
}
}, modifier = Modifier.size(70.dp).clip(CircleShape).align(Alignment.BottomCenter)
) {
Text(text = "Capture")
}
// Display the captured image
imageBitmap?.let { bitmap ->
Image(
bitmap = bitmap,
contentDescription = "Captured Image",
modifier = Modifier.fillMaxSize().padding(16.dp)
)
LaunchedEffect(bitmap) {
delay(3000)
imageBitmap = null
}
}
}
}
You can check the Sample
for more details.
The library is in an experimental stage, APIs can change/break.
CameraK has a plugin api which can be used by devs to enhance the capabilities of the library for their own needs, The design for it is not final but you can check qrScannerPlugin
or imageSaverPlugin
to check how you can build your own plugins.
Contributions
We welcome contributions! Before submitting a pull request, please open an issue so we can discuss the proposed changes and collaborate on improving the project.
Feature Requests Feature requests are encouraged, and I’ll do my best to address them as quickly as possible.
MIT License