Julian Steenbakker

Merge branch 'master' into dependabot/github_actions/subosito/flutter-action-2.3.0

1 ## 0.0.1 1 ## 0.0.1
  2 +Initial release!
  3 +Things working on Android:
  4 +* Scanning barcodes using the latest version of MLKit and CameraX!
  5 +* Switching camera's
  6 +* Toggling of the torch (flash)
2 7
3 -* TODO: Describe initial release. 8 +Things working on iOS:
  9 +* Scanning barcodes using the latest version of MLKit and AVFoundation!
1 -TODO: Add your license here. 1 +BSD 3-Clause License
  2 +
  3 +Copyright (c) 2022, Julian Steenbakker
  4 +All rights reserved.
  5 +
  6 +Redistribution and use in source and binary forms, with or without
  7 +modification, are permitted provided that the following conditions are met:
  8 +
  9 +1. Redistributions of source code must retain the above copyright notice, this
  10 + list of conditions and the following disclaimer.
  11 +
  12 +2. Redistributions in binary form must reproduce the above copyright notice,
  13 + this list of conditions and the following disclaimer in the documentation
  14 + and/or other materials provided with the distribution.
  15 +
  16 +3. Neither the name of the copyright holder nor the names of its
  17 + contributors may be used to endorse or promote products derived from
  18 + this software without specific prior written permission.
  19 +
  20 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21 +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22 +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  23 +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  24 +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25 +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  26 +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  27 +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28 +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -6,7 +6,7 @@ buildscript { @@ -6,7 +6,7 @@ buildscript {
6 } 6 }
7 7
8 dependencies { 8 dependencies {
9 - classpath 'com.android.tools.build:gradle:7.1.0' 9 + classpath 'com.android.tools.build:gradle:7.1.1'
10 classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 10 classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 } 11 }
12 } 12 }
1 -#Tue Feb 08 10:35:11 CET 2022 1 +#Tue Feb 15 22:11:04 CET 2022
2 distributionBase=GRADLE_USER_HOME 2 distributionBase=GRADLE_USER_HOME
3 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip 3 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
4 distributionPath=wrapper/dists 4 distributionPath=wrapper/dists
5 zipStorePath=wrapper/dists 5 zipStorePath=wrapper/dists
6 zipStoreBase=GRADLE_USER_HOME 6 zipStoreBase=GRADLE_USER_HOME
1 -import 'dart:ui';  
2 -  
3 import 'package:flutter/material.dart'; 1 import 'package:flutter/material.dart';
4 import 'package:mobile_scanner/mobile_scanner.dart'; 2 import 'package:mobile_scanner/mobile_scanner.dart';
5 3
@@ -18,8 +16,10 @@ class _AnalyzeViewState extends State<AnalyzeView> @@ -18,8 +16,10 @@ class _AnalyzeViewState extends State<AnalyzeView>
18 with SingleTickerProviderStateMixin { 16 with SingleTickerProviderStateMixin {
19 String? barcode; 17 String? barcode;
20 18
21 - MobileScannerController controller = MobileScannerController(torchEnabled: true,  
22 - facing: CameraFacing.front,); 19 + MobileScannerController controller = MobileScannerController(
  20 + torchEnabled: true,
  21 + facing: CameraFacing.front,
  22 + );
23 23
24 @override 24 @override
25 Widget build(BuildContext context) { 25 Widget build(BuildContext context) {
@@ -30,7 +30,7 @@ class _AnalyzeViewState extends State<AnalyzeView> @@ -30,7 +30,7 @@ class _AnalyzeViewState extends State<AnalyzeView>
30 return Stack( 30 return Stack(
31 children: [ 31 children: [
32 MobileScanner( 32 MobileScanner(
33 - controller: controller, 33 + controller: controller,
34 fit: BoxFit.contain, 34 fit: BoxFit.contain,
35 // controller: MobileScannerController( 35 // controller: MobileScannerController(
36 // torchEnabled: true, 36 // torchEnabled: true,
@@ -55,20 +55,22 @@ class _AnalyzeViewState extends State<AnalyzeView> @@ -55,20 +55,22 @@ class _AnalyzeViewState extends State<AnalyzeView>
55 children: [ 55 children: [
56 IconButton( 56 IconButton(
57 color: Colors.white, 57 color: Colors.white,
58 - icon: ValueListenableBuilder(  
59 - valueListenable: controller.torchState,  
60 - builder: (context, state, child) {  
61 - switch (state as TorchState) {  
62 - case TorchState.off:  
63 - return const Icon(Icons.flash_off, color: Colors.grey);  
64 - case TorchState.on:  
65 - return const Icon(Icons.flash_on, color: Colors.yellow);  
66 - }  
67 - },  
68 - ),  
69 - iconSize: 32.0,  
70 - onPressed: () => controller.toggleTorch(), 58 + icon: ValueListenableBuilder(
  59 + valueListenable: controller.torchState,
  60 + builder: (context, state, child) {
  61 + switch (state as TorchState) {
  62 + case TorchState.off:
  63 + return const Icon(Icons.flash_off,
  64 + color: Colors.grey);
  65 + case TorchState.on:
  66 + return const Icon(Icons.flash_on,
  67 + color: Colors.yellow);
  68 + }
  69 + },
71 ), 70 ),
  71 + iconSize: 32.0,
  72 + onPressed: () => controller.toggleTorch(),
  73 + ),
72 Center( 74 Center(
73 child: SizedBox( 75 child: SizedBox(
74 width: MediaQuery.of(context).size.width - 120, 76 width: MediaQuery.of(context).size.width - 120,
@@ -20,7 +20,7 @@ class _AnalyzeViewState extends State<AnalyzeView> @@ -20,7 +20,7 @@ class _AnalyzeViewState extends State<AnalyzeView>
20 20
21 // CameraController cameraController = CameraController(context, width: 320, height: 150); 21 // CameraController cameraController = CameraController(context, width: 320, height: 150);
22 22
23 - String? barcode = null; 23 + String? barcode;
24 24
25 @override 25 @override
26 Widget build(BuildContext context) { 26 Widget build(BuildContext context) {
@@ -30,40 +30,41 @@ class _AnalyzeViewState extends State<AnalyzeView> @@ -30,40 +30,41 @@ class _AnalyzeViewState extends State<AnalyzeView>
30 return Stack( 30 return Stack(
31 children: [ 31 children: [
32 MobileScanner( 32 MobileScanner(
33 - // fitScreen: false, 33 + // fitScreen: false,
34 // controller: cameraController, 34 // controller: cameraController,
35 onDetect: (barcode, args) { 35 onDetect: (barcode, args) {
36 - if (this.barcode != barcode.rawValue) {  
37 - this.barcode = barcode.rawValue;  
38 - if (barcode.corners != null) {  
39 - ScaffoldMessenger.of(context).showSnackBar(SnackBar(  
40 - content: Text('${barcode.rawValue}'),  
41 - duration: const Duration(milliseconds: 200),  
42 - animation: null,  
43 - ));  
44 - setState(() {  
45 - final List<Offset> points = [];  
46 - double factorWidth = args.size.width / 520;  
47 - // double factorHeight = wanted / args.size.height;  
48 - final size = MediaQuery.of(context).devicePixelRatio;  
49 - debugPrint('Size: ${barcode.corners}');  
50 - for (var point in barcode.corners!) {  
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));  
60 - }  
61 - this.points = points;  
62 - }); 36 + if (this.barcode != barcode.rawValue) {
  37 + this.barcode = barcode.rawValue;
  38 + if (barcode.corners != null) {
  39 + ScaffoldMessenger.of(context).showSnackBar(SnackBar(
  40 + content: Text(barcode.rawValue),
  41 + duration: const Duration(milliseconds: 200),
  42 + animation: null,
  43 + ));
  44 + setState(() {
  45 + final List<Offset> points = [];
  46 + // double factorWidth = args.size.width / 520;
  47 + // double factorHeight = wanted / args.size.height;
  48 + final size = MediaQuery.of(context).devicePixelRatio;
  49 + debugPrint('Size: ${barcode.corners}');
  50 + for (var point in barcode.corners!) {
  51 + final adjustedWith = point.dx;
  52 + final adjustedHeight = point.dy;
  53 + points.add(
  54 + Offset(adjustedWith / size, adjustedHeight / size));
  55 + // points.add(Offset((point.dx ) / size,
  56 + // (point.dy) / size));
  57 + // final differenceWidth = (args.wantedSize!.width - args.size.width) / 2;
  58 + // final differenceHeight = (args.wantedSize!.height - args.size.height) / 2;
  59 + // points.add(Offset((point.dx + differenceWidth) / size,
  60 + // (point.dy + differenceHeight) / size));
63 } 61 }
64 - }  
65 - // Default 640 x480  
66 - }), 62 + this.points = points;
  63 + });
  64 + }
  65 + }
  66 + // Default 640 x480
  67 + }),
67 CustomPaint( 68 CustomPaint(
68 painter: OpenPainter(points), 69 painter: OpenPainter(points),
69 ), 70 ),
@@ -108,7 +109,7 @@ class OpenPainter extends CustomPainter { @@ -108,7 +109,7 @@ class OpenPainter extends CustomPainter {
108 @override 109 @override
109 void paint(Canvas canvas, Size size) { 110 void paint(Canvas canvas, Size size) {
110 var paint1 = Paint() 111 var paint1 = Paint()
111 - ..color = Color(0xff63aa65) 112 + ..color = const Color(0xff63aa65)
112 ..strokeWidth = 10; 113 ..strokeWidth = 10;
113 //draw points on canvas 114 //draw points on canvas
114 canvas.drawPoints(PointMode.points, points, paint1); 115 canvas.drawPoints(PointMode.points, points, paint1);
@@ -5,11 +5,8 @@ @@ -5,11 +5,8 @@
5 // gestures. You can also use WidgetTester to find child widgets in the widget 5 // gestures. You can also use WidgetTester to find child widgets in the widget
6 // tree, read text, and verify that the values of widget properties are correct. 6 // tree, read text, and verify that the values of widget properties are correct.
7 7
8 -import 'package:flutter/material.dart';  
9 import 'package:flutter_test/flutter_test.dart'; 8 import 'package:flutter_test/flutter_test.dart';
10 9
11 -import 'package:mobile_scanner_example/main.dart';  
12 -  
13 void main() { 10 void main() {
14 testWidgets('Verify Platform version', (WidgetTester tester) async { 11 testWidgets('Verify Platform version', (WidgetTester tester) async {
15 // Build our app and trigger a frame. 12 // Build our app and trigger a frame.
  1 +//
  2 +// SwiftMobileScanner.swift
  3 +// mobile_scanner
  4 +//
  5 +// Created by Julian Steenbakker on 15/02/2022.
  6 +//
  7 +
  8 +import Foundation
@@ -5,61 +5,77 @@ import MLKitBarcodeScanning @@ -5,61 +5,77 @@ import MLKitBarcodeScanning
5 5
6 public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, FlutterTexture, AVCaptureVideoDataOutputSampleBufferDelegate { 6 public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHandler, FlutterTexture, AVCaptureVideoDataOutputSampleBufferDelegate {
7 7
8 - public static func register(with registrar: FlutterPluginRegistrar) {  
9 - let instance = SwiftMobileScannerPlugin(registrar.textures())  
10 -  
11 - let method = FlutterMethodChannel(name: "dev.steenbakker.mobile_scanner/scanner/method", binaryMessenger: registrar.messenger())  
12 - registrar.addMethodCallDelegate(instance, channel: method)  
13 -  
14 - let event = FlutterEventChannel(name: "dev.steenbakker.mobile_scanner/scanner/event", binaryMessenger: registrar.messenger())  
15 - event.setStreamHandler(instance)  
16 - }  
17 -  
18 let registry: FlutterTextureRegistry 8 let registry: FlutterTextureRegistry
  9 +
  10 + // Sink for publishing event changes
19 var sink: FlutterEventSink! 11 var sink: FlutterEventSink!
  12 +
  13 + // Texture id of the camera preview
20 var textureId: Int64! 14 var textureId: Int64!
  15 +
  16 + // Capture session of the camera
21 var captureSession: AVCaptureSession! 17 var captureSession: AVCaptureSession!
  18 +
  19 + // The selected camera
22 var device: AVCaptureDevice! 20 var device: AVCaptureDevice!
  21 +
  22 + // Image to be sent to the texture
23 var latestBuffer: CVImageBuffer! 23 var latestBuffer: CVImageBuffer!
24 - var analyzeMode: Int  
25 - var analyzing: Bool 24 +
  25 +
  26 + var analyzeMode: Int = 0
  27 + var analyzing: Bool = false
  28 + var position = AVCaptureDevice.Position.back
  29 +
  30 + public static func register(with registrar: FlutterPluginRegistrar) {
  31 + let instance = SwiftMobileScannerPlugin(registrar.textures())
  32 +
  33 + let method = FlutterMethodChannel(name:
  34 + "dev.steenbakker.mobile_scanner/scanner/method", binaryMessenger: registrar.messenger())
  35 + let event = FlutterEventChannel(name:
  36 + "dev.steenbakker.mobile_scanner/scanner/event", binaryMessenger: registrar.messenger())
  37 + registrar.addMethodCallDelegate(instance, channel: method)
  38 + event.setStreamHandler(instance)
  39 + }
26 40
27 init(_ registry: FlutterTextureRegistry) { 41 init(_ registry: FlutterTextureRegistry) {
28 self.registry = registry 42 self.registry = registry
29 - analyzeMode = 0  
30 - analyzing = false  
31 super.init() 43 super.init()
32 } 44 }
33 45
  46 +
34 public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 47 public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
35 switch call.method { 48 switch call.method {
36 case "state": 49 case "state":
37 - stateNative(call, result) 50 + checkPermission(call, result)
38 case "request": 51 case "request":
39 - requestNative(call, result) 52 + requestPermission(call, result)
40 case "start": 53 case "start":
41 - startNative(call, result) 54 + start(call, result)
42 case "torch": 55 case "torch":
43 - torchNative(call, result) 56 + switchTorch(call, result)
44 case "analyze": 57 case "analyze":
45 - analyzeNative(call, result) 58 + switchAnalyzeMode(call, result)
46 case "stop": 59 case "stop":
47 - stopNative(result) 60 + stop(result)
48 default: 61 default:
49 result(FlutterMethodNotImplemented) 62 result(FlutterMethodNotImplemented)
50 } 63 }
51 } 64 }
52 65
  66 + // FlutterStreamHandler
53 public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { 67 public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
54 sink = events 68 sink = events
55 return nil 69 return nil
56 } 70 }
57 71
  72 + // FlutterStreamHandler
58 public func onCancel(withArguments arguments: Any?) -> FlutterError? { 73 public func onCancel(withArguments arguments: Any?) -> FlutterError? {
59 sink = nil 74 sink = nil
60 return nil 75 return nil
61 } 76 }
62 77
  78 + // FlutterTexture
63 public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? { 79 public func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
64 if latestBuffer == nil { 80 if latestBuffer == nil {
65 return nil 81 return nil
@@ -67,11 +83,12 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -67,11 +83,12 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
67 return Unmanaged<CVPixelBuffer>.passRetained(latestBuffer) 83 return Unmanaged<CVPixelBuffer>.passRetained(latestBuffer)
68 } 84 }
69 85
  86 + // Gets called when a new image is added to the buffer
70 public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { 87 public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
71 - 88 +
72 latestBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) 89 latestBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
73 registry.textureFrameAvailable(textureId) 90 registry.textureFrameAvailable(textureId)
74 - 91 +
75 switch analyzeMode { 92 switch analyzeMode {
76 case 1: // barcode 93 case 1: // barcode
77 if analyzing { 94 if analyzing {
@@ -84,7 +101,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -84,7 +101,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
84 deviceOrientation: UIDevice.current.orientation, 101 deviceOrientation: UIDevice.current.orientation,
85 defaultOrientation: .portrait 102 defaultOrientation: .portrait
86 ) 103 )
87 - 104 +
88 let scanner = BarcodeScanner.barcodeScanner() 105 let scanner = BarcodeScanner.barcodeScanner()
89 scanner.process(image) { [self] barcodes, error in 106 scanner.process(image) { [self] barcodes, error in
90 if error == nil && barcodes != nil { 107 if error == nil && barcodes != nil {
@@ -120,7 +137,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -120,7 +137,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
120 } 137 }
121 } 138 }
122 139
123 - func stateNative(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 140 + func checkPermission(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
124 let status = AVCaptureDevice.authorizationStatus(for: .video) 141 let status = AVCaptureDevice.authorizationStatus(for: .video)
125 switch status { 142 switch status {
126 case .notDetermined: 143 case .notDetermined:
@@ -132,34 +149,45 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -132,34 +149,45 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
132 } 149 }
133 } 150 }
134 151
135 - func requestNative(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 152 + func requestPermission(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
136 AVCaptureDevice.requestAccess(for: .video, completionHandler: { result($0) }) 153 AVCaptureDevice.requestAccess(for: .video, completionHandler: { result($0) })
137 } 154 }
138 -  
139 - var position = AVCaptureDevice.Position.back  
140 -  
141 - func startNative(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 155 +
  156 + func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
142 textureId = registry.register(self) 157 textureId = registry.register(self)
143 captureSession = AVCaptureSession() 158 captureSession = AVCaptureSession()
144 159
145 let argReader = MapArgumentReader(call.arguments as? [String: Any]) 160 let argReader = MapArgumentReader(call.arguments as? [String: Any])
146 161
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 - 162 +// let ratio: Int = argReader.int(key: "ratio")
  163 + let torch: Bool = argReader.bool(key: "torch") ?? false
  164 + let facing: Int = argReader.int(key: "facing") ?? 1
  165 +
  166 + // Set the camera to use
154 position = facing == 0 ? AVCaptureDevice.Position.front : .back 167 position = facing == 0 ? AVCaptureDevice.Position.front : .back
  168 +
  169 + // Open the camera device
155 if #available(iOS 10.0, *) { 170 if #available(iOS 10.0, *) {
156 device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: position).devices.first 171 device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: position).devices.first
157 } else { 172 } else {
158 device = AVCaptureDevice.devices(for: .video).filter({$0.position == position}).first 173 device = AVCaptureDevice.devices(for: .video).filter({$0.position == position}).first
159 } 174 }
  175 +
  176 + // Enable the torch if parameter is set and torch is available
  177 + if (device.hasTorch && device.isTorchAvailable) {
  178 + do {
  179 + try device.lockForConfiguration()
  180 + device.torchMode = torch ? .on : .off
  181 + device.unlockForConfiguration()
  182 + } catch {
  183 + error.throwNative(result)
  184 + }
  185 + }
  186 +
160 device.addObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode), options: .new, context: nil) 187 device.addObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode), options: .new, context: nil)
161 captureSession.beginConfiguration() 188 captureSession.beginConfiguration()
162 - // Add device input. 189 +
  190 + // Add device input
163 do { 191 do {
164 let input = try AVCaptureDeviceInput(device: device) 192 let input = try AVCaptureDeviceInput(device: device)
165 captureSession.addInput(input) 193 captureSession.addInput(input)
@@ -191,7 +219,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -191,7 +219,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
191 result(answer) 219 result(answer)
192 } 220 }
193 221
194 - func torchNative(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 222 + func switchTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
195 do { 223 do {
196 try device.lockForConfiguration() 224 try device.lockForConfiguration()
197 device.torchMode = call.arguments as! Int == 1 ? .on : .off 225 device.torchMode = call.arguments as! Int == 1 ? .on : .off
@@ -202,12 +230,12 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -202,12 +230,12 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
202 } 230 }
203 } 231 }
204 232
205 - func analyzeNative(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { 233 + func switchAnalyzeMode(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
206 analyzeMode = call.arguments as! Int 234 analyzeMode = call.arguments as! Int
207 result(nil) 235 result(nil)
208 } 236 }
209 237
210 - func stopNative(_ result: FlutterResult) { 238 + func stop(_ result: FlutterResult) {
211 captureSession.stopRunning() 239 captureSession.stopRunning()
212 for input in captureSession.inputs { 240 for input in captureSession.inputs {
213 captureSession.removeInput(input) 241 captureSession.removeInput(input)
@@ -227,6 +255,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -227,6 +255,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
227 result(nil) 255 result(nil)
228 } 256 }
229 257
  258 + // Observer for torch state
230 public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 259 public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
231 switch keyPath { 260 switch keyPath {
232 case "torchMode": 261 case "torchMode":
@@ -255,6 +284,10 @@ class MapArgumentReader { @@ -255,6 +284,10 @@ class MapArgumentReader {
255 func int(key: String) -> Int? { 284 func int(key: String) -> Int? {
256 return (args?[key] as? NSNumber)?.intValue 285 return (args?[key] as? NSNumber)?.intValue
257 } 286 }
  287 +
  288 + func bool(key: String) -> Bool? {
  289 + return (args?[key] as? NSNumber)?.boolValue
  290 + }
258 291
259 func stringArray(key: String) -> [String]? { 292 func stringArray(key: String) -> [String]? {
260 return args?[key] as? [String] 293 return args?[key] as? [String]
@@ -2,4 +2,4 @@ library mobile_scanner; @@ -2,4 +2,4 @@ library mobile_scanner;
2 2
3 export 'src/mobile_scanner.dart'; 3 export 'src/mobile_scanner.dart';
4 export 'src/mobile_scanner_controller.dart'; 4 export 'src/mobile_scanner_controller.dart';
5 -export 'src/objects/barcode.dart';  
  5 +export 'src/objects/barcode.dart';
@@ -3,10 +3,7 @@ import 'package:mobile_scanner/mobile_scanner.dart'; @@ -3,10 +3,7 @@ import 'package:mobile_scanner/mobile_scanner.dart';
3 3
4 import 'mobile_scanner_arguments.dart'; 4 import 'mobile_scanner_arguments.dart';
5 5
6 -enum Ratio {  
7 - ratio_4_3,  
8 - ratio_16_9  
9 -} 6 +enum Ratio { ratio_4_3, ratio_16_9 }
10 7
11 /// A widget showing a live camera preview. 8 /// A widget showing a live camera preview.
12 class MobileScanner extends StatefulWidget { 9 class MobileScanner extends StatefulWidget {
@@ -18,7 +15,8 @@ class MobileScanner extends StatefulWidget { @@ -18,7 +15,8 @@ class MobileScanner extends StatefulWidget {
18 /// Create a [MobileScanner] with a [controller], the [controller] must has been initialized. 15 /// Create a [MobileScanner] with a [controller], the [controller] must has been initialized.
19 const MobileScanner( 16 const MobileScanner(
20 {Key? key, this.onDetect, this.controller, this.fit = BoxFit.cover}) 17 {Key? key, this.onDetect, this.controller, this.fit = BoxFit.cover})
21 - : assert((controller != null )), super(key: key); 18 + : assert((controller != null)),
  19 + super(key: key);
22 20
23 @override 21 @override
24 State<MobileScanner> createState() => _MobileScannerState(); 22 State<MobileScanner> createState() => _MobileScannerState();
@@ -91,4 +89,4 @@ class _MobileScannerState extends State<MobileScanner> @@ -91,4 +89,4 @@ class _MobileScannerState extends State<MobileScanner>
91 controller.dispose(); 89 controller.dispose();
92 super.dispose(); 90 super.dispose();
93 } 91 }
94 -}  
  92 +}
@@ -11,5 +11,6 @@ class MobileScannerArguments { @@ -11,5 +11,6 @@ class MobileScannerArguments {
11 final bool hasTorch; 11 final bool hasTorch;
12 12
13 /// Create a [MobileScannerArguments]. 13 /// Create a [MobileScannerArguments].
14 - MobileScannerArguments({required this.textureId,required this.size, required this.hasTorch}); 14 + MobileScannerArguments(
  15 + {required this.textureId, required this.size, required this.hasTorch});
15 } 16 }
@@ -38,7 +38,6 @@ class MobileScannerController { @@ -38,7 +38,6 @@ class MobileScannerController {
38 int? _controllerHashcode; 38 int? _controllerHashcode;
39 StreamSubscription? events; 39 StreamSubscription? events;
40 40
41 -  
42 final ValueNotifier<MobileScannerArguments?> args = ValueNotifier(null); 41 final ValueNotifier<MobileScannerArguments?> args = ValueNotifier(null);
43 final ValueNotifier<TorchState> torchState = ValueNotifier(TorchState.off); 42 final ValueNotifier<TorchState> torchState = ValueNotifier(TorchState.off);
44 late final ValueNotifier<CameraFacing> cameraFacingState; 43 late final ValueNotifier<CameraFacing> cameraFacingState;
@@ -107,7 +106,8 @@ class MobileScannerController { @@ -107,7 +106,8 @@ class MobileScannerController {
107 106
108 setAnalyzeMode(AnalyzeMode.barcode.index); 107 setAnalyzeMode(AnalyzeMode.barcode.index);
109 // Check authorization status 108 // Check authorization status
110 - MobileScannerState state = MobileScannerState.values[await methodChannel.invokeMethod('state')]; 109 + MobileScannerState state =
  110 + MobileScannerState.values[await methodChannel.invokeMethod('state')];
111 switch (state) { 111 switch (state) {
112 case MobileScannerState.undetermined: 112 case MobileScannerState.undetermined:
113 final bool result = await methodChannel.invokeMethod('request'); 113 final bool result = await methodChannel.invokeMethod('request');
@@ -129,13 +129,18 @@ class MobileScannerController { @@ -129,13 +129,18 @@ class MobileScannerController {
129 if (torchEnabled != null) arguments['torch'] = torchEnabled; 129 if (torchEnabled != null) arguments['torch'] = torchEnabled;
130 130
131 // Start the camera with arguments 131 // Start the camera with arguments
132 - final Map<String, dynamic>? startResult = await methodChannel.invokeMapMethod<String, dynamic>(  
133 - 'start', arguments); 132 + final Map<String, dynamic>? startResult = await methodChannel
  133 + .invokeMapMethod<String, dynamic>('start', arguments);
134 134
135 - if (startResult == null) throw PlatformException(code: 'INITIALIZATION ERROR'); 135 + if (startResult == null) {
  136 + throw PlatformException(code: 'INITIALIZATION ERROR');
  137 + }
136 138
137 hasTorch = startResult['torchable']; 139 hasTorch = startResult['torchable'];
138 - args.value = MobileScannerArguments(textureId: startResult['textureId'], size: toSize(startResult['size']), hasTorch: hasTorch); 140 + args.value = MobileScannerArguments(
  141 + textureId: startResult['textureId'],
  142 + size: toSize(startResult['size']),
  143 + hasTorch: hasTorch);
139 } 144 }
140 145
141 Future<void> stop() async => await methodChannel.invokeMethod('stop'); 146 Future<void> stop() async => await methodChannel.invokeMethod('stop');
@@ -157,7 +162,8 @@ class MobileScannerController { @@ -157,7 +162,8 @@ class MobileScannerController {
157 Future<void> switchCamera() async { 162 Future<void> switchCamera() async {
158 ensure('switchCamera'); 163 ensure('switchCamera');
159 await stop(); 164 await stop();
160 - facing = facing == CameraFacing.back ? CameraFacing.front : CameraFacing.back; 165 + facing =
  166 + facing == CameraFacing.back ? CameraFacing.front : CameraFacing.back;
161 start(); 167 start();
162 } 168 }
163 169
@@ -490,7 +490,7 @@ enum BarcodeFormat { @@ -490,7 +490,7 @@ enum BarcodeFormat {
490 /// Barcode format constant for Data Matrix. 490 /// Barcode format constant for Data Matrix.
491 /// 491 ///
492 /// Constant Value: 16 492 /// Constant Value: 16
493 - data_matrix, 493 + dataMatrix,
494 494
495 /// Barcode format constant for EAN-13. 495 /// Barcode format constant for EAN-13.
496 /// 496 ///
@@ -510,17 +510,17 @@ enum BarcodeFormat { @@ -510,17 +510,17 @@ enum BarcodeFormat {
510 /// Barcode format constant for QR Code. 510 /// Barcode format constant for QR Code.
511 /// 511 ///
512 /// Constant Value: 256 512 /// Constant Value: 256
513 - qr_code, 513 + qrCode,
514 514
515 /// Barcode format constant for UPC-A. 515 /// Barcode format constant for UPC-A.
516 /// 516 ///
517 /// Constant Value: 512 517 /// Constant Value: 512
518 - upc_a, 518 + upcA,
519 519
520 /// Barcode format constant for UPC-E. 520 /// Barcode format constant for UPC-E.
521 /// 521 ///
522 /// Constant Value: 1024 522 /// Constant Value: 1024
523 - upc_e, 523 + upcE,
524 524
525 /// Barcode format constant for PDF-417. 525 /// Barcode format constant for PDF-417.
526 /// 526 ///
@@ -29,7 +29,7 @@ BarcodeFormat toFormat(int value) { @@ -29,7 +29,7 @@ BarcodeFormat toFormat(int value) {
29 case 8: 29 case 8:
30 return BarcodeFormat.codebar; 30 return BarcodeFormat.codebar;
31 case 16: 31 case 16:
32 - return BarcodeFormat.data_matrix; 32 + return BarcodeFormat.dataMatrix;
33 case 32: 33 case 32:
34 return BarcodeFormat.ean13; 34 return BarcodeFormat.ean13;
35 case 64: 35 case 64:
@@ -37,11 +37,11 @@ BarcodeFormat toFormat(int value) { @@ -37,11 +37,11 @@ BarcodeFormat toFormat(int value) {
37 case 128: 37 case 128:
38 return BarcodeFormat.itf; 38 return BarcodeFormat.itf;
39 case 256: 39 case 256:
40 - return BarcodeFormat.qr_code; 40 + return BarcodeFormat.qrCode;
41 case 512: 41 case 512:
42 - return BarcodeFormat.upc_a; 42 + return BarcodeFormat.upcA;
43 case 1024: 43 case 1024:
44 - return BarcodeFormat.upc_e; 44 + return BarcodeFormat.upcE;
45 case 2048: 45 case 2048:
46 return BarcodeFormat.pdf417; 46 return BarcodeFormat.pdf417;
47 case 4096: 47 case 4096:
@@ -23,6 +23,4 @@ flutter: @@ -23,6 +23,4 @@ flutter:
23 package: dev.steenbakker.mobile_scanner 23 package: dev.steenbakker.mobile_scanner
24 pluginClass: MobileScannerPlugin 24 pluginClass: MobileScannerPlugin
25 ios: 25 ios:
26 - pluginClass: MobileScannerPlugin  
27 - macos:  
28 - pluginClass: MobileScannerPlugin 26 + pluginClass: MobileScannerPlugin
1 import 'package:flutter/services.dart'; 1 import 'package:flutter/services.dart';
2 import 'package:flutter_test/flutter_test.dart'; 2 import 'package:flutter_test/flutter_test.dart';
3 -import 'package:mobile_scanner/src/mobile_scanner.dart';  
4 3
5 void main() { 4 void main() {
6 const MethodChannel channel = MethodChannel('mobile_scanner'); 5 const MethodChannel channel = MethodChannel('mobile_scanner');
@@ -16,5 +15,4 @@ void main() { @@ -16,5 +15,4 @@ void main() {
16 tearDown(() { 15 tearDown(() {
17 channel.setMockMethodCallHandler(null); 16 channel.setMockMethodCallHandler(null);
18 }); 17 });
19 -  
20 } 18 }