CameraX Torch/Flashlight — Complete Guide
In this tutorial, you'll learn about CameraX Torch/Flashlight. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
The Problem
You call camera.cameraControl.enableTorch(true) but the flashlight doesn't turn on, or isTorchAvailable returns false on a device with a flash.
Wrong Approach ❌
// Enabling torch immediately after binding — camera not ready yet
cameraProvider.bindToLifecycle(this, cameraSelector, preview)
camera!!.cameraControl.enableTorch(true) // Camera not fully initialized
Output: Torch doesn't turn on. No error, no light.
Right Approach ✅
class TorchManager(private val context: Context) {
private var camera: Camera? = null
private var cameraProvider: ProcessCameraProvider? = null
private var isTorchOn = false
fun startCamera(previewView: PreviewView, onReady: () -> Unit = {}) {
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener({
cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build()
.also { it.setSurfaceProvider(previewView.surfaceProvider) }
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
cameraProvider?.unbindAll()
camera = cameraProvider?.bindToLifecycle(
context as LifecycleOwner,
cameraSelector,
preview
)
onReady()
} catch (e: Exception) {
Log.e("Torch", "Camera bind failed", e)
}
}, ContextCompat.getMainExecutor(context))
}
fun toggleTorch(): Boolean {
val cam = camera ?: return false
val cameraInfo = cam.cameraInfo
if (!cameraInfo.torchState.value == true) {
// Torch not available
if (cameraInfo.torchState.value == null) return false
}
isTorchOn = !isTorchOn
cam.cameraControl.enableTorch(isTorchOn)
return isTorchOn
}
fun setTorch(on: Boolean) {
camera?.cameraControl?.enableTorch(on)
isTorchOn = on
}
// Flash mode for image capture (not torch)
fun setFlashMode(imageCapture: ImageCapture, mode: Int) {
// ImageCapture.FLASH_MODE_AUTO, _ON, _OFF
imageCapture.flashMode = mode
}
fun isFlashAvailable(): Boolean {
return camera?.cameraInfo?.hasFlashUnit() ?: false
}
// Observe torch state
fun observeTorchState(callback: (Boolean) -> Unit) {
camera?.cameraInfo?.torchState?.observe(context as LifecycleOwner) { state ->
callback(state == TorchState.ON)
}
}
fun release() {
cameraProvider?.unbindAll()
camera = null
isTorchOn = false
}
}
// Usage
if (torchManager.isFlashAvailable()) {
torchManager.toggleTorch()
}
Output: Torch works correctly with proper camera initialization.
Prevention
- Wait for camera binding to complete before toggling torch.
- Check
hasFlashUnit()before attempting torch. - Observe
torchStatefor reactive UI updates. - For photo capture, use
imageCapture.flashModeinstead of torch. - Torch only works when the camera is bound to a lifecycle.
Common Mistakes with camera x torch
- Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
- Non-exhaustive pattern matches that compile with warnings then crash at runtime
- Misunderstanding that
Stringis[Char]with poor performance for large text operations
These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro