I am trying to integrate CameraX
inside my flutter project. I am doing so via platform channels. I don't want to integrate any third party lib.
Here is a screenshot of the camera only occupying one third of the height in Android
Below is my code
class ScanQr extends StatelessWidget {
const ScanQr({super.key});
@override
Widget build(BuildContext context) {
var width = MediaQuery.of(context).size.width;
var height = MediaQuery.of(context).size.height;
return Scaffold(
backgroundColor: Colors.teal,
body: SizedBox(
height: height,
width: width,
child: CealScanQrView(
width: width,
height: height,
),
),
);
}
}
class CealScanQrView extends StatelessWidget {
const CealScanQrView({required this.width, required this.height, super.key});
final double width;
final double height;
@override
Widget build(BuildContext context) {
final Map<String, dynamic> creationParams = <String, dynamic>{};
creationParams["width"] = width;
creationParams["height"] = height;
return Platform.isAndroid
? AndroidView(
viewType: cealScanQrView,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
)
: UiKitView(
viewType: cealScanQrView,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
}
Here is my android code
In MainActivity's configureFlutterEngine
method
flutterEngine
.platformViewsController
.registry
.registerViewFactory("cealScanQrView", CealScanQrViewFactory(this))
class CealScanQrView(
private val context: Context, id: Int, creationParams: Map<String?, Any?>?,
private val activity: FlutterActivity
) : PlatformView {
private var mCameraProvider: ProcessCameraProvider? = null
private var linearLayout: LinearLayout = LinearLayout(context)
private var preview: PreviewView = PreviewView(context)
private lateinit var cameraExecutor: ExecutorService
private lateinit var options: BarcodeScannerOptions
private lateinit var scanner: BarcodeScanner
private var analysisUseCase: ImageAnalysis = ImageAnalysis.Builder()
.build()
companion object {
private val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS = mutableListOf(Manifest.permission.CAMERA).toTypedArray()
}
init {
val linearLayoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
linearLayout.layoutParams = linearLayoutParams
linearLayout.orientation = LinearLayout.VERTICAL
preview.setBackgroundColor(Color.RED)
preview.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
linearLayout.addView(preview)
linearLayout.layoutParams.width = 400
linearLayout.layoutParams.height = 400
setUpCamera()
preview.viewTreeObserver.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
preview.viewTreeObserver.removeOnGlobalLayoutListener(this)
preview.layoutParams.height =
creationParams?.get("height").toString().toDouble().toInt()
preview.requestLayout()
}
})
}
private fun setUpCamera() {
if (allPermissionsGranted()) {
startCamera()
} else {
ActivityCompat.requestPermissions(
context as FlutterActivity, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS
)
}
cameraExecutor = Executors.newSingleThreadExecutor()
options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(
Barcode.FORMAT_QR_CODE
)
.build()
scanner = BarcodeScanning.getClient(options)
analysisUseCase.setAnalyzer(
// newSingleThreadExecutor() will let us perform analysis on a single worker thread
Executors.newSingleThreadExecutor()
) { imageProxy ->
processImageProxy(scanner, imageProxy)
}
}
override fun getView(): View {
return linearLayout
}
override fun dispose() {
cameraExecutor.shutdown()
}
@SuppressLint("UnsafeOptInUsageError")
private fun processImageProxy(
barcodeScanner: BarcodeScanner,
imageProxy: ImageProxy
) {
imageProxy.image?.let { image ->
val inputImage =
InputImage.fromMediaImage(
image,
imageProxy.imageInfo.rotationDegrees
)
barcodeScanner.process(inputImage)
.addOnSuccessListener { barcodeList ->
val barcode = barcodeList.getOrNull(0)
// `rawValue` is the decoded value of the barcode
barcode?.rawValue?.let { value ->
mCameraProvider?.unbindAll()
Toast.makeText(context, value, Toast.LENGTH_LONG).show()
}
}
.addOnFailureListener {
// This failure will happen if the barcode scanning model
// fails to download from Google Play Services
}
.addOnCompleteListener {
// When the image is from CameraX analysis use case, must
// call image.close() on received images when finished
// using them. Otherwise, new images may not be received
// or the camera may stall.
imageProxy.image?.close()
imageProxy.close()
}
}
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener({
// Used to bind the lifecycle of cameras to the lifecycle owner
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
mCameraProvider = cameraProvider
// Preview
val surfacePreview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(preview.surfaceProvider)
}
// Select back camera as a default
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// Unbind use cases before rebinding
cameraProvider.unbindAll()
// Bind use cases to camera
cameraProvider.bindToLifecycle(
activity,
cameraSelector,
surfacePreview,
analysisUseCase,
)
} catch (exc: Exception) {
// Do nothing on exception
}
}, ContextCompat.getMainExecutor(context))
}
}
In AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" />
There are two problems which I am facing
Firstly I don't see the camera permission when my screen opens even though I am asking the permission for it. I have to manually go to the settings screen to enable camera permission And secondly the camera does not occupy entire screen
from Native Camera View in Flutter does not occupy the whole width and height
No comments:
Post a Comment