Julian Steenbakker

ci: fixed scanner on ios and android

@@ -47,5 +47,16 @@ android { @@ -47,5 +47,16 @@ android {
47 dependencies { 47 dependencies {
48 implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 48 implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
49 implementation 'com.google.mlkit:barcode-scanning:17.0.2' 49 implementation 'com.google.mlkit:barcode-scanning:17.0.2'
50 - implementation 'com.google.mlkit:camera:16.0.0-beta3' 50 + implementation "androidx.camera:camera-camera2:1.1.0-beta01"
  51 + implementation 'androidx.camera:camera-lifecycle:1.1.0-beta01'
  52 +
  53 +// // The following line is optional, as the core library is included indirectly by camera-camera2
  54 +// implementation "androidx.camera:camera-core:1.1.0-alpha11"
  55 +// implementation "androidx.camera:camera-camera2:1.1.0-alpha11"
  56 +// // If you want to additionally use the CameraX Lifecycle library
  57 +// implementation "androidx.camera:camera-lifecycle:1.1.0-alpha11"
  58 +// // If you want to additionally use the CameraX View class
  59 +// implementation "androidx.camera:camera-view:1.0.0-alpha31"
  60 +// // If you want to additionally use the CameraX Extensions library
  61 +// implementation "androidx.camera:camera-extensions:1.0.0-alpha31"
51 } 62 }
1 package dev.steenbakker.mobile_scanner 1 package dev.steenbakker.mobile_scanner
2 2
3 import android.Manifest 3 import android.Manifest
  4 +import android.R.attr.height
  5 +import android.R.attr.width
4 import android.annotation.SuppressLint 6 import android.annotation.SuppressLint
5 import android.app.Activity 7 import android.app.Activity
  8 +import android.content.Context
6 import android.content.pm.PackageManager 9 import android.content.pm.PackageManager
  10 +import android.graphics.ImageFormat
  11 +import android.graphics.SurfaceTexture
  12 +import android.hardware.camera2.CameraCharacteristics
  13 +import android.hardware.camera2.CameraManager
  14 +import android.hardware.camera2.params.StreamConfigurationMap
7 import android.util.Log 15 import android.util.Log
  16 +import android.util.Rational
  17 +import android.util.Size
8 import android.view.Surface 18 import android.view.Surface
  19 +import android.view.Surface.ROTATION_0
  20 +import android.view.Surface.ROTATION_180
9 import androidx.annotation.IntDef 21 import androidx.annotation.IntDef
10 import androidx.annotation.NonNull 22 import androidx.annotation.NonNull
11 import androidx.camera.core.* 23 import androidx.camera.core.*
  24 +import androidx.camera.core.impl.PreviewConfig
12 import androidx.camera.lifecycle.ProcessCameraProvider 25 import androidx.camera.lifecycle.ProcessCameraProvider
13 import androidx.core.app.ActivityCompat 26 import androidx.core.app.ActivityCompat
14 import androidx.core.content.ContextCompat 27 import androidx.core.content.ContextCompat
15 import androidx.lifecycle.LifecycleOwner 28 import androidx.lifecycle.LifecycleOwner
16 import com.google.mlkit.vision.barcode.BarcodeScanning 29 import com.google.mlkit.vision.barcode.BarcodeScanning
17 import com.google.mlkit.vision.common.InputImage 30 import com.google.mlkit.vision.common.InputImage
18 -import io.flutter.plugin.common.* 31 +import io.flutter.plugin.common.EventChannel
  32 +import io.flutter.plugin.common.MethodCall
  33 +import io.flutter.plugin.common.MethodChannel
  34 +import io.flutter.plugin.common.PluginRegistry
19 import io.flutter.view.TextureRegistry 35 import io.flutter.view.TextureRegistry
20 36
  37 +
21 class MobileScanner(private val activity: Activity, private val textureRegistry: TextureRegistry) 38 class MobileScanner(private val activity: Activity, private val textureRegistry: TextureRegistry)
22 : MethodChannel.MethodCallHandler, EventChannel.StreamHandler, PluginRegistry.RequestPermissionsResultListener { 39 : MethodChannel.MethodCallHandler, EventChannel.StreamHandler, PluginRegistry.RequestPermissionsResultListener {
23 companion object { 40 companion object {
@@ -34,6 +51,7 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -34,6 +51,7 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
34 @AnalyzeMode 51 @AnalyzeMode
35 private var analyzeMode: Int = AnalyzeMode.NONE 52 private var analyzeMode: Int = AnalyzeMode.NONE
36 53
  54 + @ExperimentalGetImage
37 override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: MethodChannel.Result) { 55 override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: MethodChannel.Result) {
38 when (call.method) { 56 when (call.method) {
39 "state" -> stateNative(result) 57 "state" -> stateNative(result)
@@ -81,8 +99,23 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -81,8 +99,23 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
81 ActivityCompat.requestPermissions(activity, permissions, REQUEST_CODE) 99 ActivityCompat.requestPermissions(activity, permissions, REQUEST_CODE)
82 } 100 }
83 101
  102 + private var sensorOrientation = 0
  103 +
84 @ExperimentalGetImage 104 @ExperimentalGetImage
  105 +// @androidx.camera.camera2.interop.ExperimentalCamera2Interop
85 private fun startNative(call: MethodCall, result: MethodChannel.Result) { 106 private fun startNative(call: MethodCall, result: MethodChannel.Result) {
  107 +
  108 + val targetWidth: Int? = call.argument<Int>("targetWidth")
  109 + val targetHeight: Int? = call.argument<Int>("targetHeight")
  110 + val facing: Int? = call.argument<Int>("facing")
  111 +
  112 + if (targetWidth == null || targetHeight == null) {
  113 + result.error("INVALID_ARGUMENT", "Missing a required argument", "Expecting targetWidth, targetHeight")
  114 + return
  115 + }
  116 +
  117 + Log.i("LOG", "Target resolution : $targetWidth, $targetHeight")
  118 +
86 val future = ProcessCameraProvider.getInstance(activity) 119 val future = ProcessCameraProvider.getInstance(activity)
87 val executor = ContextCompat.getMainExecutor(activity) 120 val executor = ContextCompat.getMainExecutor(activity)
88 future.addListener({ 121 future.addListener({
@@ -91,13 +124,23 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -91,13 +124,23 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
91 val textureId = textureEntry!!.id() 124 val textureId = textureEntry!!.id()
92 // Preview 125 // Preview
93 val surfaceProvider = Preview.SurfaceProvider { request -> 126 val surfaceProvider = Preview.SurfaceProvider { request ->
94 - val resolution = request.resolution  
95 val texture = textureEntry!!.surfaceTexture() 127 val texture = textureEntry!!.surfaceTexture()
  128 + val resolution = request.resolution
96 texture.setDefaultBufferSize(resolution.width, resolution.height) 129 texture.setDefaultBufferSize(resolution.width, resolution.height)
  130 + Log.i("LOG", "Image resolution : ${request.resolution}")
97 val surface = Surface(texture) 131 val surface = Surface(texture)
98 - request.provideSurface(surface, executor, { }) 132 + request.provideSurface(surface, executor) { }
99 } 133 }
100 - val preview = Preview.Builder().build().apply { setSurfaceProvider(surfaceProvider) } 134 +// PreviewConfig().apply { }
  135 +// val previewConfig = PreviewConfig.Builder().apply {
  136 +// setTargetAspectRatio(SQUARE_ASPECT_RATIO)
  137 +// setTargetRotation(viewFinder.display.rotation)
  138 +// }.build()
  139 +
  140 +
  141 + val preview = Preview.Builder()
  142 + .setTargetResolution(Size(targetWidth, targetHeight))
  143 + .build().apply { setSurfaceProvider(surfaceProvider) }
101 // Analyzer 144 // Analyzer
102 val analyzer = ImageAnalysis.Analyzer { imageProxy -> // YUV_420_888 format 145 val analyzer = ImageAnalysis.Analyzer { imageProxy -> // YUV_420_888 format
103 when (analyzeMode) { 146 when (analyzeMode) {
@@ -120,6 +163,7 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -120,6 +163,7 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
120 } 163 }
121 val analysis = ImageAnalysis.Builder() 164 val analysis = ImageAnalysis.Builder()
122 .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) 165 .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
  166 + .setTargetResolution(Size(targetWidth, targetHeight))
123 .build().apply { setAnalyzer(executor, analyzer) } 167 .build().apply { setAnalyzer(executor, analyzer) }
124 // Bind to lifecycle. 168 // Bind to lifecycle.
125 val owner = activity as LifecycleOwner 169 val owner = activity as LifecycleOwner
@@ -127,17 +171,23 @@ class MobileScanner(private val activity: Activity, private val textureRegistry: @@ -127,17 +171,23 @@ class MobileScanner(private val activity: Activity, private val textureRegistry:
127 if (call.arguments == 0) CameraSelector.DEFAULT_FRONT_CAMERA 171 if (call.arguments == 0) CameraSelector.DEFAULT_FRONT_CAMERA
128 else CameraSelector.DEFAULT_BACK_CAMERA 172 else CameraSelector.DEFAULT_BACK_CAMERA
129 camera = cameraProvider!!.bindToLifecycle(owner, selector, preview, analysis) 173 camera = cameraProvider!!.bindToLifecycle(owner, selector, preview, analysis)
130 - camera!!.cameraInfo.torchState.observe(owner, { state -> 174 +
  175 + val analysisSize = analysis.resolutionInfo?.resolution ?: Size(0, 0)
  176 + val previewSize = preview.resolutionInfo?.resolution ?: Size(0, 0)
  177 + Log.i("LOG", "Analyzer: $analysisSize")
  178 + Log.i("LOG", "Preview: $previewSize")
  179 +
  180 + camera!!.cameraInfo.torchState.observe(owner) { state ->
131 // TorchState.OFF = 0; TorchState.ON = 1 181 // TorchState.OFF = 0; TorchState.ON = 1
132 val event = mapOf("name" to "torchState", "data" to state) 182 val event = mapOf("name" to "torchState", "data" to state)
133 sink?.success(event) 183 sink?.success(event)
134 - })  
135 - // TODO: seems there's not a better way to get the final resolution  
136 - @SuppressLint("RestrictedApi")  
137 - val resolution = preview.attachedSurfaceResolution!! 184 + }
  185 +
  186 + val resolution = preview.resolutionInfo!!.resolution
138 val portrait = camera!!.cameraInfo.sensorRotationDegrees % 180 == 0 187 val portrait = camera!!.cameraInfo.sensorRotationDegrees % 180 == 0
139 val width = resolution.width.toDouble() 188 val width = resolution.width.toDouble()
140 val height = resolution.height.toDouble() 189 val height = resolution.height.toDouble()
  190 +// val size = mapOf("width" to 1920.0, "height" to 1080.0)
141 val size = if (portrait) mapOf("width" to width, "height" to height) else mapOf("width" to height, "height" to width) 191 val size = if (portrait) mapOf("width" to width, "height" to height) else mapOf("width" to height, "height" to width)
142 val answer = mapOf("textureId" to textureId, "size" to size, "torchable" to camera!!.torchable) 192 val answer = mapOf("textureId" to textureId, "size" to size, "torchable" to camera!!.torchable)
143 result.success(answer) 193 result.success(answer)
@@ -18,7 +18,7 @@ class _AnalyzeViewState extends State<AnalyzeView> @@ -18,7 +18,7 @@ class _AnalyzeViewState extends State<AnalyzeView>
18 with SingleTickerProviderStateMixin { 18 with SingleTickerProviderStateMixin {
19 List<Offset> points = []; 19 List<Offset> points = [];
20 20
21 - CameraController cameraController = CameraController(); 21 + // CameraController cameraController = CameraController(context, width: 320, height: 150);
22 22
23 String? barcode = null; 23 String? barcode = null;
24 24
@@ -29,25 +29,34 @@ class _AnalyzeViewState extends State<AnalyzeView> @@ -29,25 +29,34 @@ class _AnalyzeViewState extends State<AnalyzeView>
29 body: Builder(builder: (context) { 29 body: Builder(builder: (context) {
30 return Stack( 30 return Stack(
31 children: [ 31 children: [
32 - CameraView(  
33 - controller: cameraController, 32 + MobileScanner(
  33 + // fitScreen: false,
  34 + // controller: cameraController,
34 onDetect: (barcode, args) { 35 onDetect: (barcode, args) {
35 if (this.barcode != barcode.rawValue) { 36 if (this.barcode != barcode.rawValue) {
36 this.barcode = barcode.rawValue; 37 this.barcode = barcode.rawValue;
37 if (barcode.corners != null) { 38 if (barcode.corners != null) {
38 - debugPrint('Size: ${MediaQuery.of(context).size}');  
39 ScaffoldMessenger.of(context).showSnackBar(SnackBar( 39 ScaffoldMessenger.of(context).showSnackBar(SnackBar(
40 content: Text('${barcode.rawValue}'), 40 content: Text('${barcode.rawValue}'),
41 - duration: Duration(milliseconds: 200), 41 + duration: const Duration(milliseconds: 200),
42 animation: null, 42 animation: null,
43 )); 43 ));
44 setState(() { 44 setState(() {
45 final List<Offset> points = []; 45 final List<Offset> points = [];
46 double factorWidth = args.size.width / 520; 46 double factorWidth = args.size.width / 520;
47 - double factorHeight = args.size.height / 640; 47 + // double factorHeight = wanted / args.size.height;
  48 + final size = MediaQuery.of(context).devicePixelRatio;
  49 + debugPrint('Size: ${barcode.corners}');
48 for (var point in barcode.corners!) { 50 for (var point in barcode.corners!) {
49 - points.add(Offset(point.dx * factorWidth,  
50 - point.dy * factorHeight)); 51 + final adjustedWith = point.dx ;
  52 + final adjustedHeight= point.dy ;
  53 + points.add(Offset(adjustedWith / size, adjustedHeight / size));
  54 + // points.add(Offset((point.dx ) / size,
  55 + // (point.dy) / size));
  56 + // final differenceWidth = (args.wantedSize!.width - args.size.width) / 2;
  57 + // final differenceHeight = (args.wantedSize!.height - args.size.height) / 2;
  58 + // points.add(Offset((point.dx + differenceWidth) / size,
  59 + // (point.dy + differenceHeight) / size));
51 } 60 }
52 this.points = points; 61 this.points = points;
53 }); 62 });
@@ -55,29 +64,25 @@ class _AnalyzeViewState extends State<AnalyzeView> @@ -55,29 +64,25 @@ class _AnalyzeViewState extends State<AnalyzeView>
55 } 64 }
56 // Default 640 x480 65 // Default 640 x480
57 }), 66 }),
58 - Container(  
59 - // width: 400,  
60 - // height: 400,  
61 - child: CustomPaint( 67 + CustomPaint(
62 painter: OpenPainter(points), 68 painter: OpenPainter(points),
63 ), 69 ),
64 - ),  
65 - Container(  
66 - alignment: Alignment.bottomCenter,  
67 - margin: EdgeInsets.only(bottom: 80.0),  
68 - child: IconButton(  
69 - icon: ValueListenableBuilder(  
70 - valueListenable: cameraController.torchState,  
71 - builder: (context, state, child) {  
72 - final color =  
73 - state == TorchState.off ? Colors.grey : Colors.white;  
74 - return Icon(Icons.bolt, color: color);  
75 - },  
76 - ),  
77 - iconSize: 32.0,  
78 - onPressed: () => cameraController.torch(),  
79 - ),  
80 - ), 70 + // Container(
  71 + // alignment: Alignment.bottomCenter,
  72 + // margin: EdgeInsets.only(bottom: 80.0),
  73 + // child: IconButton(
  74 + // icon: ValueListenableBuilder(
  75 + // valueListenable: cameraController.torchState,
  76 + // builder: (context, state, child) {
  77 + // final color =
  78 + // state == TorchState.off ? Colors.grey : Colors.white;
  79 + // return Icon(Icons.bolt, color: color);
  80 + // },
  81 + // ),
  82 + // iconSize: 32.0,
  83 + // onPressed: () => cameraController.torch(),
  84 + // ),
  85 + // ),
81 ], 86 ],
82 ); 87 );
83 }), 88 }),
@@ -87,7 +92,7 @@ class _AnalyzeViewState extends State<AnalyzeView> @@ -87,7 +92,7 @@ class _AnalyzeViewState extends State<AnalyzeView>
87 92
88 @override 93 @override
89 void dispose() { 94 void dispose() {
90 - cameraController.dispose(); 95 + // cameraController.dispose();
91 super.dispose(); 96 super.dispose();
92 } 97 }
93 98
@@ -80,6 +80,11 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -80,6 +80,11 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
80 analyzing = true 80 analyzing = true
81 let buffer = CMSampleBufferGetImageBuffer(sampleBuffer) 81 let buffer = CMSampleBufferGetImageBuffer(sampleBuffer)
82 let image = VisionImage(image: buffer!.image) 82 let image = VisionImage(image: buffer!.image)
  83 + image.orientation = imageOrientation(
  84 + deviceOrientation: UIDevice.current.orientation,
  85 + defaultOrientation: .portrait
  86 + )
  87 +
83 let scanner = BarcodeScanner.barcodeScanner() 88 let scanner = BarcodeScanner.barcodeScanner()
84 scanner.process(image) { [self] barcodes, error in 89 scanner.process(image) { [self] barcodes, error in
85 if error == nil && barcodes != nil { 90 if error == nil && barcodes != nil {
@@ -95,6 +100,26 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -95,6 +100,26 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
95 } 100 }
96 } 101 }
97 102
  103 + func imageOrientation(
  104 + deviceOrientation: UIDeviceOrientation,
  105 + defaultOrientation: UIDeviceOrientation
  106 + ) -> UIImage.Orientation {
  107 + switch deviceOrientation {
  108 + case .portrait:
  109 + return position == .front ? .leftMirrored : .right
  110 + case .landscapeLeft:
  111 + return position == .front ? .downMirrored : .up
  112 + case .portraitUpsideDown:
  113 + return position == .front ? .rightMirrored : .left
  114 + case .landscapeRight:
  115 + return position == .front ? .upMirrored : .down
  116 + case .faceDown, .faceUp, .unknown:
  117 + return .up
  118 + @unknown default:
  119 + return imageOrientation(deviceOrientation: defaultOrientation, defaultOrientation: .portrait)
  120 + }
  121 + }
  122 +
98 func stateNative(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 123 func stateNative(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
99 let status = AVCaptureDevice.authorizationStatus(for: .video) 124 let status = AVCaptureDevice.authorizationStatus(for: .video)
100 switch status { 125 switch status {
@@ -111,10 +136,22 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -111,10 +136,22 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
111 AVCaptureDevice.requestAccess(for: .video, completionHandler: { result($0) }) 136 AVCaptureDevice.requestAccess(for: .video, completionHandler: { result($0) })
112 } 137 }
113 138
  139 + var position = AVCaptureDevice.Position.back
  140 +
114 func startNative(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 141 func startNative(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
115 textureId = registry.register(self) 142 textureId = registry.register(self)
116 captureSession = AVCaptureSession() 143 captureSession = AVCaptureSession()
117 - let position = call.arguments as! Int == 0 ? AVCaptureDevice.Position.front : .back 144 +
  145 + let argReader = MapArgumentReader(call.arguments as? [String: Any])
  146 +
  147 + guard let targetWidth = argReader.int(key: "targetWidth"),
  148 + let targetHeight = argReader.int(key: "targetHeight"),
  149 + let facing = argReader.int(key: "facing") else {
  150 + result(FlutterError(code: "INVALID_ARGUMENT", message: "Missing a required argument", details: "Expecting targetWidth, targetHeight, formats, and optionally heartbeatTimeout"))
  151 + return
  152 + }
  153 +
  154 + position = facing == 0 ? AVCaptureDevice.Position.front : .back
118 if #available(iOS 10.0, *) { 155 if #available(iOS 10.0, *) {
119 device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: position).devices.first 156 device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: position).devices.first
120 } else { 157 } else {
@@ -129,10 +166,13 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -129,10 +166,13 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
129 } catch { 166 } catch {
130 error.throwNative(result) 167 error.throwNative(result)
131 } 168 }
  169 + captureSession.sessionPreset = AVCaptureSession.Preset.photo;
132 // Add video output. 170 // Add video output.
133 let videoOutput = AVCaptureVideoDataOutput() 171 let videoOutput = AVCaptureVideoDataOutput()
  172 +
134 videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA] 173 videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
135 videoOutput.alwaysDiscardsLateVideoFrames = true 174 videoOutput.alwaysDiscardsLateVideoFrames = true
  175 +
136 videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main) 176 videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)
137 captureSession.addOutput(videoOutput) 177 captureSession.addOutput(videoOutput)
138 for connection in videoOutput.connections { 178 for connection in videoOutput.connections {
@@ -199,3 +239,25 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -199,3 +239,25 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
199 } 239 }
200 } 240 }
201 } 241 }
  242 +
  243 +class MapArgumentReader {
  244 +
  245 + let args: [String: Any]?
  246 +
  247 + init(_ args: [String: Any]?) {
  248 + self.args = args
  249 + }
  250 +
  251 + func string(key: String) -> String? {
  252 + return args?[key] as? String
  253 + }
  254 +
  255 + func int(key: String) -> Int? {
  256 + return (args?[key] as? NSNumber)?.intValue
  257 + }
  258 +
  259 + func stringArray(key: String) -> [String]? {
  260 + return args?[key] as? [String]
  261 + }
  262 +
  263 +}
1 library mobile_scanner; 1 library mobile_scanner;
2 2
3 export 'src/mobile_scanner.dart'; 3 export 'src/mobile_scanner.dart';
4 -export 'src/camera_controller.dart';  
5 -export 'src/camera_view.dart';  
6 -export 'src/torch_state.dart'; 4 +export 'src/mobile_scanner_controller.dart';
7 export 'src/objects/barcode.dart'; 5 export 'src/objects/barcode.dart';
1 -import 'package:flutter/material.dart';  
2 -import 'package:mobile_scanner/mobile_scanner.dart';  
3 -  
4 -import 'camera_args.dart';  
5 -  
6 -/// A widget showing a live camera preview.  
7 -class CameraView extends StatefulWidget {  
8 - /// The controller of the camera.  
9 - final CameraController? controller;  
10 - final Function(Barcode barcode, CameraArgs args)? onDetect;  
11 -  
12 - /// Create a [CameraView] with a [controller], the [controller] must has been initialized.  
13 - const CameraView({Key? key, this.onDetect, this.controller}) : super(key: key);  
14 -  
15 - @override  
16 - State<CameraView> createState() => _CameraViewState();  
17 -}  
18 -  
19 -class _CameraViewState extends State<CameraView> {  
20 -  
21 - late CameraController controller;  
22 - @override  
23 - initState() {  
24 - super.initState();  
25 - controller = widget.controller ?? CameraController();  
26 - }  
27 -  
28 - @override  
29 - Widget build(BuildContext context) {  
30 - return ValueListenableBuilder(  
31 - valueListenable: controller.args,  
32 - builder: (context, value, child) {  
33 - value = value as CameraArgs?;  
34 - if (value == null) {  
35 - return Container(color: Colors.black);  
36 - } else {  
37 - controller.barcodes  
38 - .listen((a) => widget.onDetect!(a, value as CameraArgs));  
39 -  
40 - return ClipRect(  
41 - child: Transform.scale(  
42 - scale: value.size.fill(MediaQuery.of(context).size),  
43 - child: Center(  
44 - child: AspectRatio(  
45 - aspectRatio: value.size.aspectRatio,  
46 - child: Texture(textureId: value.textureId),  
47 - ),  
48 - ),  
49 - ),  
50 - );  
51 - }  
52 - });  
53 - }  
54 -  
55 - @override  
56 - void dispose() {  
57 - controller.dispose();  
58 - super.dispose();  
59 - }  
60 -}  
61 -  
62 -extension on Size {  
63 - double fill(Size targetSize) {  
64 - if (targetSize.aspectRatio < aspectRatio) {  
65 - return targetSize.height * aspectRatio / targetSize.width;  
66 - } else {  
67 - return targetSize.width / aspectRatio / targetSize.height;  
68 - }  
69 - }  
70 -}  
1 -// import 'dart:async';  
2 -// import 'package:flutter/material.dart';  
3 -// import 'package:mobile_scanner/src/mobile_scanner_handler.dart';  
4 -// import 'package:mobile_scanner/src/objects/preview_details.dart';  
5 -//  
6 -// import 'mobile_scanner_preview.dart';  
7 -// import 'objects/barcode_formats.dart';  
8 -//  
9 -// typedef ErrorCallback = Widget Function(BuildContext context, Object? error);  
10 -//  
11 -// Text _defaultNotStartedBuilder(context) => const Text("Camera Loading ...");  
12 -// Text _defaultOffscreenBuilder(context) => const Text("Camera Paused.");  
13 -// Text _defaultOnError(BuildContext context, Object? error) {  
14 -// debugPrint("Error reading from camera: $error");  
15 -// return const Text("Error reading from camera...");  
16 -// }  
17 -//  
18 -// class MobileScanner extends StatefulWidget {  
19 -// const MobileScanner(  
20 -// {Key? key,  
21 -// required this.qrCodeCallback,  
22 -// this.child,  
23 -// this.fit = BoxFit.cover,  
24 -// WidgetBuilder? notStartedBuilder,  
25 -// WidgetBuilder? offscreenBuilder,  
26 -// ErrorCallback? onError,  
27 -// this.formats,  
28 -// this.rearLens = true,  
29 -// this.manualFocus = false})  
30 -// : notStartedBuilder = notStartedBuilder ?? _defaultNotStartedBuilder,  
31 -// offscreenBuilder =  
32 -// offscreenBuilder ?? notStartedBuilder ?? _defaultOffscreenBuilder,  
33 -// onError = onError ?? _defaultOnError,  
34 -// super(key: key);  
35 -//  
36 -// final BoxFit fit;  
37 -// final ValueChanged<String?> qrCodeCallback;  
38 -// final Widget? child;  
39 -// final WidgetBuilder notStartedBuilder;  
40 -// final WidgetBuilder offscreenBuilder;  
41 -// final ErrorCallback onError;  
42 -// final List<BarcodeFormats>? formats;  
43 -// final bool rearLens;  
44 -// final bool manualFocus;  
45 -//  
46 -// static void toggleFlash() {  
47 -// MobileScannerHandler.toggleFlash();  
48 -// }  
49 -//  
50 -// static void flipCamera() {  
51 -// MobileScannerHandler.switchCamera();  
52 -// }  
53 -//  
54 -// @override  
55 -// _MobileScannerState createState() => _MobileScannerState();  
56 -// }  
57 -//  
58 -// class _MobileScannerState extends State<MobileScanner>  
59 -// with WidgetsBindingObserver {  
60 -//  
61 -// bool onScreen = true;  
62 -// Future<PreviewDetails>? _previewDetails;  
63 -//  
64 -// @override  
65 -// void initState() {  
66 -// super.initState();  
67 -// WidgetsBinding.instance!.addObserver(this);  
68 -// }  
69 -//  
70 -// @override  
71 -// void dispose() {  
72 -// WidgetsBinding.instance!.removeObserver(this);  
73 -// super.dispose();  
74 -// }  
75 -//  
76 -// @override  
77 -// void didChangeAppLifecycleState(AppLifecycleState state) {  
78 -// if (state == AppLifecycleState.resumed) {  
79 -// setState(() => onScreen = true);  
80 -// } else {  
81 -// if (_previewDetails != null && onScreen) {  
82 -// MobileScannerHandler.stop();  
83 -// }  
84 -// setState(() {  
85 -// onScreen = false;  
86 -// _previewDetails = null;  
87 -// });  
88 -// }  
89 -// }  
90 -//  
91 -// Future<PreviewDetails> _initPreview(num width, num height) async {  
92 -// final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;  
93 -// return await MobileScannerHandler.start(  
94 -// width: (devicePixelRatio * width.toInt()).ceil(),  
95 -// height: (devicePixelRatio * height.toInt()).ceil(),  
96 -// qrCodeHandler: widget.qrCodeCallback,  
97 -// formats: widget.formats,  
98 -// );  
99 -// }  
100 -//  
101 -// void switchCamera() {  
102 -// MobileScannerHandler.rearLens = !MobileScannerHandler.rearLens;  
103 -// restart();  
104 -// }  
105 -//  
106 -//  
107 -// void switchFocus() {  
108 -// MobileScannerHandler.manualFocus = !MobileScannerHandler.manualFocus;  
109 -// restart();  
110 -// }  
111 -//  
112 -// /// This method can be used to restart scanning  
113 -// /// the event that it was paused.  
114 -// Future<void> restart() async {  
115 -// await MobileScannerHandler.stop();  
116 -// setState(() {  
117 -// _previewDetails = null;  
118 -// });  
119 -// }  
120 -//  
121 -// /// This method can be used to manually stop the  
122 -// /// camera.  
123 -// Future<void> stop() async {  
124 -// await MobileScannerHandler.stop();  
125 -// }  
126 -//  
127 -// @override  
128 -// deactivate() {  
129 -// super.deactivate();  
130 -// MobileScannerHandler.stop();  
131 -// }  
132 -//  
133 -// @override  
134 -// Widget build(BuildContext context) {  
135 -// return LayoutBuilder(  
136 -// builder: (BuildContext context, BoxConstraints constraints) {  
137 -// if (_previewDetails == null && onScreen) {  
138 -// _previewDetails =  
139 -// _initPreview(constraints.maxWidth, constraints.maxHeight);  
140 -// } else if (!onScreen) {  
141 -// return widget.offscreenBuilder(context);  
142 -// }  
143 -//  
144 -// return FutureBuilder(  
145 -// future: _previewDetails,  
146 -// builder: (BuildContext context, AsyncSnapshot<PreviewDetails> details) {  
147 -// switch (details.connectionState) {  
148 -// case ConnectionState.none:  
149 -// case ConnectionState.waiting:  
150 -// return widget.notStartedBuilder(context);  
151 -// case ConnectionState.done:  
152 -// if (details.hasError) {  
153 -// debugPrint(details.error.toString());  
154 -// return widget.onError(context, details.error);  
155 -// }  
156 -// Widget preview = SizedBox(  
157 -// width: constraints.maxWidth,  
158 -// height: constraints.maxHeight,  
159 -// child: Preview(  
160 -// previewDetails: details.data!,  
161 -// targetWidth: constraints.maxWidth,  
162 -// targetHeight: constraints.maxHeight,  
163 -// fit: widget.fit,  
164 -// ),  
165 -// );  
166 -//  
167 -// if (widget.child != null) {  
168 -// return Stack(  
169 -// children: [  
170 -// preview,  
171 -// widget.child!,  
172 -// ],  
173 -// );  
174 -// }  
175 -// return preview;  
176 -//  
177 -// default:  
178 -// throw AssertionError("${details.connectionState} not supported.");  
179 -// }  
180 -// },  
181 -// );  
182 -// });  
183 -// }  
184 -// } 1 +import 'package:flutter/material.dart';
  2 +import 'package:mobile_scanner/mobile_scanner.dart';
  3 +
  4 +import 'mobile_scanner_arguments.dart';
  5 +
  6 +/// A widget showing a live camera preview.
  7 +class MobileScanner extends StatefulWidget {
  8 + /// The controller of the camera.
  9 + final MobileScannerController? controller;
  10 + final Function(Barcode barcode, MobileScannerArguments args)? onDetect;
  11 + final bool fitScreen;
  12 + final bool fitWidth;
  13 +
  14 + /// Create a [MobileScanner] with a [controller], the [controller] must has been initialized.
  15 + const MobileScanner(
  16 + {Key? key, this.onDetect, this.controller, this.fitScreen = true, this.fitWidth = true})
  17 + : super(key: key);
  18 +
  19 + @override
  20 + State<MobileScanner> createState() => _MobileScannerState();
  21 +}
  22 +
  23 +class _MobileScannerState extends State<MobileScanner>
  24 + with WidgetsBindingObserver {
  25 + bool onScreen = true;
  26 + MobileScannerController? controller;
  27 +
  28 + @override
  29 + void didChangeAppLifecycleState(AppLifecycleState state) {
  30 + if (state == AppLifecycleState.resumed) {
  31 + setState(() => onScreen = true);
  32 + } else {
  33 + if (controller != null && onScreen) {
  34 + controller!.stop();
  35 + }
  36 + setState(() {
  37 + onScreen = false;
  38 + controller = null;
  39 + });
  40 + }
  41 + }
  42 +
  43 + @override
  44 + Widget build(BuildContext context) {
  45 + return LayoutBuilder(builder: (context, BoxConstraints constraints) {
  46 + final media = MediaQuery.of(context);
  47 +
  48 + controller ??= MobileScannerController(context,
  49 + width: constraints.maxWidth, height: constraints.maxHeight);
  50 + if (!onScreen) return const Text("Camera Paused.");
  51 + return ValueListenableBuilder(
  52 + valueListenable: controller!.args,
  53 + builder: (context, value, child) {
  54 + value = value as MobileScannerArguments?;
  55 + if (value == null) {
  56 + return Container(color: Colors.black);
  57 + } else {
  58 + controller!.barcodes.listen(
  59 + (a) => widget.onDetect!(a, value as MobileScannerArguments));
  60 + // Texture(textureId: value.textureId)
  61 + return ClipRect(
  62 + child: FittedBox(
  63 + fit: BoxFit.cover,
  64 + child: SizedBox(
  65 + width: value.size.width,
  66 + height: value.size.height,
  67 + child: Texture(textureId: value.textureId),
  68 + ),
  69 + ),
  70 + );
  71 + }
  72 + });
  73 + });
  74 + }
  75 +
  76 + @override
  77 + void dispose() {
  78 + controller?.dispose();
  79 + super.dispose();
  80 + }
  81 +}
  82 +
  83 +extension on Size {
  84 + double fill(Size targetSize) {
  85 + if (targetSize.aspectRatio < aspectRatio) {
  86 + return targetSize.height * aspectRatio / targetSize.width;
  87 + } else {
  88 + return targetSize.width / aspectRatio / targetSize.height;
  89 + }
  90 + }
  91 +}
1 import 'package:flutter/material.dart'; 1 import 'package:flutter/material.dart';
2 2
3 /// Camera args for [CameraView]. 3 /// Camera args for [CameraView].
4 -class CameraArgs { 4 +class MobileScannerArguments {
5 /// The texture id. 5 /// The texture id.
6 final int textureId; 6 final int textureId;
7 7
8 /// Size of the texture. 8 /// Size of the texture.
9 final Size size; 9 final Size size;
10 10
11 - /// Create a [CameraArgs].  
12 - CameraArgs(this.textureId, this.size); 11 + /// Size of the texture.
  12 + final Size? wantedSize;
  13 +
  14 + /// Create a [MobileScannerArguments].
  15 + MobileScannerArguments({required this.textureId,required this.size, this.wantedSize});
13 } 16 }
@@ -3,10 +3,9 @@ import 'dart:async'; @@ -3,10 +3,9 @@ import 'dart:async';
3 import 'package:flutter/cupertino.dart'; 3 import 'package:flutter/cupertino.dart';
4 import 'package:flutter/services.dart'; 4 import 'package:flutter/services.dart';
5 5
6 -import 'camera_args.dart'; 6 +import 'mobile_scanner_arguments.dart';
7 import 'objects/barcode.dart'; 7 import 'objects/barcode.dart';
8 -import 'torch_state.dart';  
9 -import 'util.dart'; 8 +import 'objects/barcode_utility.dart';
10 9
11 /// The facing of a camera. 10 /// The facing of a camera.
12 enum CameraFacing { 11 enum CameraFacing {
@@ -17,45 +16,60 @@ enum CameraFacing { @@ -17,45 +16,60 @@ enum CameraFacing {
17 back, 16 back,
18 } 17 }
19 18
  19 +enum MobileScannerState {
  20 + undetermined,
  21 + authorized,
  22 + denied
  23 +}
20 24
21 -/// A camera controller.  
22 -abstract class CameraController {  
23 - /// Arguments for [CameraView].  
24 - ValueNotifier<CameraArgs?> get args;  
25 -  
26 - /// Torch state of the camera.  
27 - ValueNotifier<TorchState> get torchState;  
28 -  
29 - /// A stream of barcodes.  
30 - Stream<Barcode> get barcodes; 25 +/// The state of torch.
  26 +enum TorchState {
  27 + /// Torch is off.
  28 + off,
31 29
32 - /// Create a [CameraController].  
33 - ///  
34 - /// [facing] target facing used to select camera.  
35 - ///  
36 - /// [formats] the barcode formats for image analyzer.  
37 - factory CameraController([CameraFacing facing = CameraFacing.back]) =>  
38 - _CameraController(facing); 30 + /// Torch is on.
  31 + on,
  32 +}
39 33
40 - /// Start the camera asynchronously.  
41 - Future<void> startAsync();  
42 34
43 - /// Switch the torch's state.  
44 - void torch();  
45 35
46 - /// Release the resources of the camera.  
47 - void dispose();  
48 -} 36 +// /// A camera controller.
  37 +// abstract class CameraController {
  38 +// /// Arguments for [CameraView].
  39 +// ValueNotifier<CameraArgs?> get args;
  40 +//
  41 +// /// Torch state of the camera.
  42 +// ValueNotifier<TorchState> get torchState;
  43 +//
  44 +// /// A stream of barcodes.
  45 +// Stream<Barcode> get barcodes;
  46 +//
  47 +// /// Create a [CameraController].
  48 +// ///
  49 +// /// [facing] target facing used to select camera.
  50 +// ///
  51 +// /// [formats] the barcode formats for image analyzer.
  52 +// factory CameraController([CameraFacing facing = CameraFacing.back] ) =>
  53 +// _CameraController(facing);
  54 +//
  55 +// /// Start the camera asynchronously.
  56 +// Future<void> start();
  57 +//
  58 +// /// Switch the torch's state.
  59 +// void torch();
  60 +//
  61 +// /// Release the resources of the camera.
  62 +// void dispose();
  63 +// }
  64 +
  65 +class MobileScannerController {
49 66
50 -class _CameraController implements CameraController {  
51 static const MethodChannel method = 67 static const MethodChannel method =
52 MethodChannel('dev.steenbakker.mobile_scanner/scanner/method'); 68 MethodChannel('dev.steenbakker.mobile_scanner/scanner/method');
53 static const EventChannel event = 69 static const EventChannel event =
54 EventChannel('dev.steenbakker.mobile_scanner/scanner/event'); 70 EventChannel('dev.steenbakker.mobile_scanner/scanner/event');
55 71
56 - static const undetermined = 0;  
57 - static const authorized = 1;  
58 - static const denied = 2; 72 +
59 73
60 static const analyze_none = 0; 74 static const analyze_none = 0;
61 static const analyze_barcode = 1; 75 static const analyze_barcode = 1;
@@ -64,18 +78,15 @@ class _CameraController implements CameraController { @@ -64,18 +78,15 @@ class _CameraController implements CameraController {
64 static StreamSubscription? subscription; 78 static StreamSubscription? subscription;
65 79
66 final CameraFacing facing; 80 final CameraFacing facing;
67 - @override  
68 - final ValueNotifier<CameraArgs?> args;  
69 - @override 81 + final ValueNotifier<MobileScannerArguments?> args;
70 final ValueNotifier<TorchState> torchState; 82 final ValueNotifier<TorchState> torchState;
71 83
72 bool torchable; 84 bool torchable;
73 late StreamController<Barcode> barcodesController; 85 late StreamController<Barcode> barcodesController;
74 86
75 - @override  
76 Stream<Barcode> get barcodes => barcodesController.stream; 87 Stream<Barcode> get barcodes => barcodesController.stream;
77 88
78 - _CameraController(this.facing) 89 + MobileScannerController(BuildContext context, {required num width, required num height, this.facing = CameraFacing.back})
79 : args = ValueNotifier(null), 90 : args = ValueNotifier(null),
80 torchState = ValueNotifier(TorchState.off), 91 torchState = ValueNotifier(TorchState.off),
81 torchable = false { 92 torchable = false {
@@ -89,7 +100,13 @@ class _CameraController implements CameraController { @@ -89,7 +100,13 @@ class _CameraController implements CameraController {
89 onListen: () => tryAnalyze(analyze_barcode), 100 onListen: () => tryAnalyze(analyze_barcode),
90 onCancel: () => tryAnalyze(analyze_none), 101 onCancel: () => tryAnalyze(analyze_none),
91 ); 102 );
92 - startAsync(); 103 +
  104 + final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
  105 +
  106 +
  107 + start(
  108 + width: (devicePixelRatio * width.toInt()).ceil(),
  109 + height: (devicePixelRatio * height.toInt()).ceil());
93 // Listen event handler. 110 // Listen event handler.
94 subscription = 111 subscription =
95 event.receiveBroadcastStream().listen((data) => handleEvent(data)); 112 event.receiveBroadcastStream().listen((data) => handleEvent(data));
@@ -119,39 +136,53 @@ class _CameraController implements CameraController { @@ -119,39 +136,53 @@ class _CameraController implements CameraController {
119 method.invokeMethod('analyze', mode); 136 method.invokeMethod('analyze', mode);
120 } 137 }
121 138
122 - @override  
123 - Future<void> startAsync() async { 139 + Future<void> start({
  140 + int? width,
  141 + int? height,
  142 + // List<BarcodeFormats>? formats = _defaultBarcodeFormats,
  143 + }) async {
124 ensure('startAsync'); 144 ensure('startAsync');
125 // Check authorization state. 145 // Check authorization state.
126 - var state = await method.invokeMethod('state');  
127 - if (state == undetermined) {  
128 - final result = await method.invokeMethod('request');  
129 - state = result ? authorized : denied;  
130 - }  
131 - if (state != authorized) { 146 + MobileScannerState state = MobileScannerState.values[await method.invokeMethod('state')];
  147 + switch (state) {
  148 + case MobileScannerState.undetermined:
  149 + final bool result = await method.invokeMethod('request');
  150 + state = result ? MobileScannerState.authorized : MobileScannerState.denied;
  151 + break;
  152 + case MobileScannerState.authorized:
  153 + break;
  154 + case MobileScannerState.denied:
132 throw PlatformException(code: 'NO ACCESS'); 155 throw PlatformException(code: 'NO ACCESS');
133 } 156 }
  157 +
  158 + debugPrint('TARGET RESOLUTION $width, $height');
134 // Start camera. 159 // Start camera.
135 final answer = 160 final answer =
136 - await method.invokeMapMethod<String, dynamic>('start', facing.index); 161 + await method.invokeMapMethod<String, dynamic>('start', {
  162 + 'targetWidth': width,
  163 + 'targetHeight': height,
  164 + 'facing': facing.index
  165 + });
137 final textureId = answer?['textureId']; 166 final textureId = answer?['textureId'];
138 - final size = toSize(answer?['size']);  
139 - args.value = CameraArgs(textureId, size); 167 + final Size size = toSize(answer?['size']);
  168 + debugPrint('RECEIVED SIZE: ${size.width} ${size.height}');
  169 + if (width != null && height != null) {
  170 + args.value = MobileScannerArguments(textureId: textureId, size: size, wantedSize: Size(width.toDouble(), height.toDouble()));
  171 + } else {
  172 + args.value = MobileScannerArguments(textureId: textureId, size: size);
  173 + }
  174 +
140 torchable = answer?['torchable']; 175 torchable = answer?['torchable'];
141 } 176 }
142 177
143 - @override  
144 void torch() { 178 void torch() {
145 ensure('torch'); 179 ensure('torch');
146 - if (!torchable) {  
147 - return;  
148 - } 180 + if (!torchable) return;
149 var state = 181 var state =
150 torchState.value == TorchState.off ? TorchState.on : TorchState.off; 182 torchState.value == TorchState.off ? TorchState.on : TorchState.off;
151 method.invokeMethod('torch', state.index); 183 method.invokeMethod('torch', state.index);
152 } 184 }
153 185
154 - @override  
155 void dispose() { 186 void dispose() {
156 if (hashCode == id) { 187 if (hashCode == id) {
157 stop(); 188 stop();
1 -import 'dart:async';  
2 -import 'package:flutter/material.dart';  
3 -import 'package:flutter/services.dart';  
4 -import 'package:mobile_scanner/src/objects/preview_details.dart';  
5 -  
6 -import 'objects/barcode_formats.dart';  
7 -  
8 -enum FrameRotation { none, ninetyCC, oneeighty, twoseventyCC }  
9 -  
10 -const _defaultBarcodeFormats = [  
11 - BarcodeFormats.ALL_FORMATS,  
12 -];  
13 -  
14 -typedef QRCodeHandler = void Function(String? qr);  
15 -  
16 -class MobileScannerHandler {  
17 - static const MethodChannel _channel =  
18 - MethodChannel('dev.steenbakker.mobile_scanner/scanner');  
19 -  
20 - static Future<String?> get platformVersion async {  
21 - final String? version = await _channel.invokeMethod('getPlatformVersion');  
22 - return version;  
23 - }  
24 -  
25 - static bool rearLens = true;  
26 - static bool manualFocus = false;  
27 -  
28 - //Set target size before starting  
29 - static Future<PreviewDetails> start({  
30 - required int width,  
31 - required int height,  
32 - required QRCodeHandler qrCodeHandler,  
33 - List<BarcodeFormats>? formats = _defaultBarcodeFormats,  
34 - }) async {  
35 - final _formats = formats ?? _defaultBarcodeFormats;  
36 - assert(_formats.isNotEmpty);  
37 -  
38 - List<String> formatStrings = _formats  
39 - .map((format) => format.toString().split('.')[1])  
40 - .toList(growable: false);  
41 -  
42 - _channel.setMethodCallHandler((MethodCall call) async {  
43 - switch (call.method) {  
44 - case 'qrRead':  
45 - assert(call.arguments is String);  
46 - qrCodeHandler(call.arguments);  
47 - break;  
48 - default:  
49 - debugPrint("QrChannelHandler: unknown method call received at "  
50 - "${call.method}");  
51 - }  
52 - });  
53 -  
54 - var details = await _channel.invokeMethod('start', {  
55 - 'targetWidth': width,  
56 - 'targetHeight': height,  
57 - 'heartbeatTimeout': 0,  
58 - 'formats': formatStrings,  
59 - 'rearLens': rearLens,  
60 - 'manualFocus': manualFocus  
61 - });  
62 -  
63 - assert(details is Map<dynamic, dynamic>);  
64 -  
65 - int? textureId = details["textureId"];  
66 - num? orientation = details["surfaceOrientation"];  
67 - num? surfaceHeight = details["surfaceHeight"];  
68 - num? surfaceWidth = details["surfaceWidth"];  
69 -  
70 - return PreviewDetails(surfaceWidth, surfaceHeight, orientation, textureId);  
71 - }  
72 -  
73 - static Future switchCamera() {  
74 - return _channel.invokeMethod('switch').catchError(print);  
75 - }  
76 -  
77 - static Future toggleFlash() {  
78 - return _channel.invokeMethod('toggleFlash').catchError(print);  
79 - }  
80 -  
81 - static Future stop() {  
82 - _channel.setMethodCallHandler(null);  
83 - return _channel.invokeMethod('stop').catchError(print);  
84 - }  
85 -  
86 - static Future heartbeat() {  
87 - return _channel.invokeMethod('heartbeat').catchError(print);  
88 - }  
89 -  
90 - static Future<List<List<int>>?> getSupportedSizes() {  
91 - return _channel.invokeMethod('getSupportedSizes').catchError(print)  
92 - as Future<List<List<int>>?>;  
93 - }  
94 -}  
1 -// import 'dart:async';  
2 -//  
3 -// import 'package:flutter/material.dart';  
4 -// import 'package:mobile_scanner/src/objects/preview_details.dart';  
5 -//  
6 -// class Preview extends StatefulWidget {  
7 -// final double width, height;  
8 -// final double targetWidth, targetHeight;  
9 -// final int? textureId;  
10 -// final int? sensorOrientation;  
11 -// final BoxFit fit;  
12 -//  
13 -// Preview({  
14 -// Key? key,  
15 -// required PreviewDetails previewDetails,  
16 -// required this.targetWidth,  
17 -// required this.targetHeight,  
18 -// required this.fit,  
19 -// }) : textureId = previewDetails.textureId,  
20 -// width = previewDetails.width!.toDouble(),  
21 -// height = previewDetails.height!.toDouble(),  
22 -// sensorOrientation = previewDetails.sensorOrientation as int?,  
23 -// super(key: key);  
24 -//  
25 -// @override  
26 -// State<Preview> createState() => _PreviewState();  
27 -// }  
28 -//  
29 -// class _PreviewState extends State<Preview> {  
30 -//  
31 -// final _streamSubscriptions = <StreamSubscription<dynamic>>[];  
32 -// bool landscapeLeft = false;  
33 -//  
34 -// @override  
35 -// void initState() {  
36 -// super.initState();  
37 -// _streamSubscriptions.add(  
38 -// magnetometerEvents.listen(  
39 -// (MagnetometerEvent event) {  
40 -// if (event.x <= 0) {  
41 -// landscapeLeft = true;  
42 -// } else {  
43 -// landscapeLeft = false;  
44 -// }  
45 -// },  
46 -// ),  
47 -// );  
48 -// }  
49 -//  
50 -// @override  
51 -// void dispose() {  
52 -// super.dispose();  
53 -// for (final subscription in _streamSubscriptions) {  
54 -// subscription.cancel();  
55 -// }  
56 -// }  
57 -//  
58 -//  
59 -// int _getRotationCompensation(NativeDeviceOrientation nativeOrientation) {  
60 -// int nativeRotation = 0;  
61 -// switch (nativeOrientation) {  
62 -// case NativeDeviceOrientation.portraitUp:  
63 -// nativeRotation = 0;  
64 -// break;  
65 -// case NativeDeviceOrientation.landscapeRight:  
66 -// nativeRotation = 90;  
67 -// break;  
68 -// case NativeDeviceOrientation.portraitDown:  
69 -// nativeRotation = 180;  
70 -// break;  
71 -// case NativeDeviceOrientation.landscapeLeft:  
72 -// nativeRotation = 270;  
73 -// break;  
74 -// case NativeDeviceOrientation.unknown:  
75 -// default:  
76 -// break;  
77 -// }  
78 -//  
79 -// return ((nativeRotation - widget.sensorOrientation! + 450) % 360) ~/ 90;  
80 -// }  
81 -//  
82 -// @override  
83 -// Widget build(BuildContext context) {  
84 -// final orientation = MediaQuery.of(context).orientation;  
85 -// double frameHeight = widget.width;  
86 -// double frameWidth = widget.height;  
87 -//  
88 -// return ClipRect(  
89 -// child: FittedBox(  
90 -// fit: widget.fit,  
91 -// child: RotatedBox(  
92 -// quarterTurns: orientation == Orientation.landscape ? landscapeLeft ? 1 : 3 : 0,  
93 -// child: SizedBox(  
94 -// width: frameWidth,  
95 -// height: frameHeight,  
96 -// child: Texture(textureId: widget.textureId!),  
97 -// ),  
98 -// ),  
99 -// ),  
100 -// );  
101 -//  
102 -// return NativeDeviceOrientationReader(  
103 -// builder: (context) {  
104 -// var nativeOrientation =  
105 -// NativeDeviceOrientationReader.orientation(context);  
106 -//  
107 -// double frameHeight = widget.width;  
108 -// double frameWidth = widget.height;  
109 -//  
110 -// return ClipRect(  
111 -// child: FittedBox(  
112 -// fit: widget.fit,  
113 -// child: RotatedBox(  
114 -// quarterTurns: _getRotationCompensation(nativeOrientation),  
115 -// child: SizedBox(  
116 -// width: frameWidth,  
117 -// height: frameHeight,  
118 -// child: Texture(textureId: widget.textureId!),  
119 -// ),  
120 -// ),  
121 -// ),  
122 -// );  
123 -// },  
124 -// );  
125 -// }  
126 -// }  
1 import 'dart:typed_data'; 1 import 'dart:typed_data';
2 import 'dart:ui'; 2 import 'dart:ui';
3 3
4 -import '../util.dart'; 4 +import 'barcode_utility.dart';
5 5
6 /// Represents a single recognized barcode and its value. 6 /// Represents a single recognized barcode and its value.
7 class Barcode { 7 class Barcode {
1 -enum BarcodeFormats {  
2 - ALL_FORMATS,  
3 - AZTEC,  
4 - CODE_128,  
5 - CODE_39,  
6 - CODE_93,  
7 - CODABAR,  
8 - DATA_MATRIX,  
9 - EAN_13,  
10 - EAN_8,  
11 - ITF,  
12 - PDF417,  
13 - QR_CODE,  
14 - UPC_A,  
15 - UPC_E,  
16 -}  
1 import 'package:flutter/material.dart'; 1 import 'package:flutter/material.dart';
2 2
3 -import 'objects/barcode.dart'; 3 +import 'barcode.dart';
4 4
5 Size toSize(Map<dynamic, dynamic> data) { 5 Size toSize(Map<dynamic, dynamic> data) {
6 final width = data['width']; 6 final width = data['width'];
1 -class PreviewDetails {  
2 - num? width;  
3 - num? height;  
4 - num? sensorOrientation;  
5 - int? textureId;  
6 -  
7 - PreviewDetails(this.width, this.height, this.sensorOrientation, this.textureId);  
8 -}  
1 -/// The state of torch.  
2 -enum TorchState {  
3 - /// Torch is off.  
4 - off,  
5 -  
6 - /// Torch is on.  
7 - on,  
8 -}