Julian Steenbakker
Committed by GitHub

Merge branch 'master' into master

  1 +# These owners will be the default owners for everything in
  2 +# the repo. Unless a later match takes precedence,
  3 +# review when someone opens a pull request.
  4 +# For more on how to customize the CODEOWNERS file - https://help.github.com/en/articles/about-code-owners
  5 +
  6 +* @juliansteenbakker
@@ -6,15 +6,33 @@ updates: @@ -6,15 +6,33 @@ updates:
6 interval: "weekly" 6 interval: "weekly"
7 reviewers: 7 reviewers:
8 - "juliansteenbakker" 8 - "juliansteenbakker"
  9 + commit-message:
  10 + prefix: "chore"
  11 + include: "scope"
9 - package-ecosystem: gradle 12 - package-ecosystem: gradle
10 directory: "/android" 13 directory: "/android"
11 schedule: 14 schedule:
12 interval: "weekly" 15 interval: "weekly"
13 reviewers: 16 reviewers:
14 - "juliansteenbakker" 17 - "juliansteenbakker"
  18 + commit-message:
  19 + prefix: "chore"
  20 + include: "scope"
15 - package-ecosystem: gradle 21 - package-ecosystem: gradle
16 directory: "/example/android" 22 directory: "/example/android"
17 schedule: 23 schedule:
18 interval: "weekly" 24 interval: "weekly"
19 reviewers: 25 reviewers:
20 - - "juliansteenbakker"  
  26 + - "juliansteenbakker"
  27 + commit-message:
  28 + prefix: "chore"
  29 + include: "scope"
  30 + - package-ecosystem: "pub"
  31 + directory: "/"
  32 + schedule:
  33 + interval: "weekly"
  34 + commit-message:
  35 + prefix: "chore"
  36 + include: "scope"
  37 + reviewers:
  38 + - "juliansteenbakker"
  1 +# .github/workflows/auto-author-assign.yml
  2 +name: 'Auto Author Assign'
  3 +
  4 +on:
  5 + pull_request_target:
  6 + types: [opened, reopened]
  7 +
  8 +permissions:
  9 + pull-requests: write
  10 +
  11 +jobs:
  12 + assign-author:
  13 + runs-on: ubuntu-latest
  14 + steps:
  15 + - uses: toshimaru/auto-author-assign@v1.6.1
1 name: code analysis & formatting 1 name: code analysis & formatting
2 2
3 -on: [push, pull_request] 3 +on:
  4 + push:
  5 + branches:
  6 + - master
  7 + pull_request:
  8 + types: [ opened, labeled, unlabeled, synchronize ]
4 9
5 jobs: 10 jobs:
6 analysis: 11 analysis:
7 runs-on: ubuntu-latest 12 runs-on: ubuntu-latest
8 steps: 13 steps:
9 - - uses: actions/checkout@v3  
10 - - uses: actions/setup-java@v3 14 + - uses: actions/checkout@v3.0.2
  15 + - uses: actions/setup-java@v3.4.1
11 with: 16 with:
12 - distribution: 'temurin'  
13 - java-version: '11'  
14 - - uses: subosito/flutter-action@v2.4.0 17 + java-version: 11
  18 + distribution: temurin
  19 + - uses: subosito/flutter-action@v2.6.1
  20 + with:
  21 + cache: true
15 - name: Version 22 - name: Version
16 run: flutter doctor -v 23 run: flutter doctor -v
17 - name: Install dependencies 24 - name: Install dependencies
@@ -21,11 +28,13 @@ jobs: @@ -21,11 +28,13 @@ jobs:
21 formatting: 28 formatting:
22 runs-on: ubuntu-latest 29 runs-on: ubuntu-latest
23 steps: 30 steps:
24 - - uses: actions/checkout@v3  
25 - - uses: actions/setup-java@v3 31 + - uses: actions/checkout@v3.0.2
  32 + - uses: actions/setup-java@v3.4.1
  33 + with:
  34 + java-version: 11
  35 + distribution: temurin
  36 + - uses: subosito/flutter-action@v2.6.1
26 with: 37 with:
27 - distribution: 'temurin'  
28 - java-version: '11'  
29 - - uses: subosito/flutter-action@v2.4.0 38 + cache: true
30 - name: Format 39 - name: Format
31 - run: flutter format -n --set-exit-if-changed .  
  40 + run: flutter format -n --set-exit-if-changed .
  1 + name: Run release-please
  2 + on:
  3 + push:
  4 + branches:
  5 + - master
  6 + jobs:
  7 + release-please:
  8 + runs-on: ubuntu-latest
  9 + steps:
  10 + - uses: GoogleCloudPlatform/release-please-action@v3.5.0
  11 + with:
  12 + token: ${{ secrets.GITHUB_TOKEN }}
  13 + release-type: simple
  1 +name: "Semantic PRs"
  2 +
  3 +on:
  4 + pull_request_target:
  5 + types:
  6 + - opened
  7 + - edited
  8 + - synchronize
  9 +
  10 +jobs:
  11 + main:
  12 + name: Validate PR title
  13 + runs-on: ubuntu-latest
  14 + steps:
  15 + - uses: amannn/action-semantic-pull-request@v4
  16 + env:
  17 + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  1 +## 3.0.0
  2 +Breaking changes:
  3 +* [Android] SDK updated to SDK 33.
  4 +
  5 +Other changes:
  6 +* [Android] Revert camera2 dependency to stable release
  7 +* [iOS] Update barcode scanning library to latest version
  8 +
1 ## 2.0.0 9 ## 2.0.0
2 Breaking changes: 10 Breaking changes:
3 This version is only compatible with flutter 3.0.0 and later. 11 This version is only compatible with flutter 3.0.0 and later.
@@ -169,3 +169,41 @@ import 'package:mobile_scanner/mobile_scanner.dart'; @@ -169,3 +169,41 @@ import 'package:mobile_scanner/mobile_scanner.dart';
169 })); 169 }));
170 } 170 }
171 ``` 171 ```
  172 +
  173 +Example with controller and returning images
  174 +
  175 +```dart
  176 +import 'package:mobile_scanner/mobile_scanner.dart';
  177 +
  178 + @override
  179 + Widget build(BuildContext context) {
  180 + return Scaffold(
  181 + appBar: AppBar(title: const Text('Mobile Scanner')),
  182 + body: MobileScanner(
  183 + controller: MobileScannerController(
  184 + facing: CameraFacing.front,
  185 + torchEnabled: true,
  186 + returnImage: true,
  187 + ),
  188 + onDetect: (barcode, args) {
  189 + if (barcode.rawValue == null) {
  190 + debugPrint('Failed to scan Barcode');
  191 + } else {
  192 + final String code = barcode.rawValue!;
  193 + debugPrint('Barcode found! $code');
  194 +
  195 + debugPrint(
  196 + 'Image returned! length: ${barcode.image!.lengthInBytes}b');
  197 + showDialog(
  198 + context: context,
  199 + builder: (context) => Image(image: MemoryImage(barcode.image!)),
  200 + );
  201 + Future.delayed(const Duration(seconds: 2), () {
  202 + Navigator.pop(context);
  203 + });
  204 + }
  205 + },
  206 + ),
  207 + );
  208 + }
  209 +```
@@ -2,14 +2,14 @@ group 'dev.steenbakker.mobile_scanner' @@ -2,14 +2,14 @@ group 'dev.steenbakker.mobile_scanner'
2 version '1.0-SNAPSHOT' 2 version '1.0-SNAPSHOT'
3 3
4 buildscript { 4 buildscript {
5 - ext.kotlin_version = '1.6.21' 5 + ext.kotlin_version = '1.7.10'
6 repositories { 6 repositories {
7 google() 7 google()
8 mavenCentral() 8 mavenCentral()
9 } 9 }
10 10
11 dependencies { 11 dependencies {
12 - classpath 'com.android.tools.build:gradle:7.2.1' 12 + classpath 'com.android.tools.build:gradle:7.2.2'
13 } 13 }
14 } 14 }
15 15
@@ -24,7 +24,7 @@ apply plugin: 'com.android.library' @@ -24,7 +24,7 @@ apply plugin: 'com.android.library'
24 apply plugin: 'kotlin-android' 24 apply plugin: 'kotlin-android'
25 25
26 android { 26 android {
27 - compileSdkVersion 32 27 + compileSdkVersion 33
28 28
29 compileOptions { 29 compileOptions {
30 sourceCompatibility JavaVersion.VERSION_1_8 30 sourceCompatibility JavaVersion.VERSION_1_8
@@ -52,16 +52,6 @@ dependencies { @@ -52,16 +52,6 @@ dependencies {
52 // Use this dependency to use the dynamically downloaded model in Google Play Services 52 // Use this dependency to use the dynamically downloaded model in Google Play Services
53 // implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.0.0' 53 // implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.0.0'
54 54
55 - implementation "androidx.camera:camera-camera2:1.2.0-alpha01"  
56 - implementation 'androidx.camera:camera-lifecycle:1.2.0-alpha01'  
57 -  
58 -// // The following line is optional, as the core library is included indirectly by camera-camera2  
59 -// implementation "androidx.camera:camera-core:1.1.0-alpha11"  
60 -// implementation "androidx.camera:camera-camera2:1.1.0-alpha11"  
61 -// // If you want to additionally use the CameraX Lifecycle library  
62 -// implementation "androidx.camera:camera-lifecycle:1.1.0-alpha11"  
63 -// // If you want to additionally use the CameraX View class  
64 -// implementation "androidx.camera:camera-view:1.0.0-alpha31"  
65 -// // If you want to additionally use the CameraX Extensions library  
66 -// implementation "androidx.camera:camera-extensions:1.0.0-alpha31" 55 + implementation 'androidx.camera:camera-camera2:1.1.0'
  56 + implementation 'androidx.camera:camera-lifecycle:1.1.0'
67 } 57 }
@@ -26,7 +26,7 @@ apply plugin: 'kotlin-android' @@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
26 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 27
28 android { 28 android {
29 - compileSdkVersion 32 29 + compileSdkVersion 33
30 30
31 compileOptions { 31 compileOptions {
32 sourceCompatibility JavaVersion.VERSION_1_8 32 sourceCompatibility JavaVersion.VERSION_1_8
@@ -45,7 +45,7 @@ android { @@ -45,7 +45,7 @@ android {
45 // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
46 applicationId "dev.steenbakker.mobile_scanner_example" 46 applicationId "dev.steenbakker.mobile_scanner_example"
47 minSdkVersion 21 47 minSdkVersion 21
48 - targetSdkVersion 32 48 + targetSdkVersion 33
49 versionCode flutterVersionCode.toInteger() 49 versionCode flutterVersionCode.toInteger()
50 versionName flutterVersionName 50 versionName flutterVersionName
51 } 51 }
1 buildscript { 1 buildscript {
2 - ext.kotlin_version = '1.6.21' 2 + ext.kotlin_version = '1.7.10'
3 repositories { 3 repositories {
4 google() 4 google()
5 mavenCentral() 5 mavenCentral()
6 } 6 }
7 7
8 dependencies { 8 dependencies {
9 - classpath 'com.android.tools.build:gradle:7.2.1' 9 + classpath 'com.android.tools.build:gradle:7.2.2'
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 May 31 10:34:01 CEST 2022 1 +#Tue Aug 23 15:51:00 CEST 2022
2 distributionBase=GRADLE_USER_HOME 2 distributionBase=GRADLE_USER_HOME
3 -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip 3 +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-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
@@ -13,7 +13,7 @@ @@ -13,7 +13,7 @@
13 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 13 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
14 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 14 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
15 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 15 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
16 - C80F46710D9B9F4F17AD4E3D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E133769572782C32D37D8AC /* Pods_Runner.framework */; }; 16 + A5A2C2B73A9F26060DE9FB22 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54E006799E73DEAB41FD3623 /* Pods_Runner.framework */; };
17 /* End PBXBuildFile section */ 17 /* End PBXBuildFile section */
18 18
19 /* Begin PBXCopyFilesBuildPhase section */ 19 /* Begin PBXCopyFilesBuildPhase section */
@@ -32,9 +32,9 @@ @@ -32,9 +32,9 @@
32 /* Begin PBXFileReference section */ 32 /* Begin PBXFileReference section */
33 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; 33 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
34 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; 34 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
35 - 1CD9C88F6BFEF6CB7CA6746B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; 35 + 32FD382A786B3A0080FE63FD /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
36 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; 36 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
37 - 5E133769572782C32D37D8AC /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 37 + 54E006799E73DEAB41FD3623 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
38 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; 38 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
39 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 39 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
40 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; 40 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
@@ -45,8 +45,8 @@ @@ -45,8 +45,8 @@
45 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 45 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
46 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 46 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
47 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 47 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
48 - E29A089CD1D61281C49DBB79 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };  
49 - E33BE6AC5C06F7A45470ADE0 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; 48 + D5B36FCD262B39F867CFDEEE /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
  49 + F0D5742F0690BE32D07B033A /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
50 /* End PBXFileReference section */ 50 /* End PBXFileReference section */
51 51
52 /* Begin PBXFrameworksBuildPhase section */ 52 /* Begin PBXFrameworksBuildPhase section */
@@ -54,27 +54,19 @@ @@ -54,27 +54,19 @@
54 isa = PBXFrameworksBuildPhase; 54 isa = PBXFrameworksBuildPhase;
55 buildActionMask = 2147483647; 55 buildActionMask = 2147483647;
56 files = ( 56 files = (
57 - C80F46710D9B9F4F17AD4E3D /* Pods_Runner.framework in Frameworks */, 57 + A5A2C2B73A9F26060DE9FB22 /* Pods_Runner.framework in Frameworks */,
58 ); 58 );
59 runOnlyForDeploymentPostprocessing = 0; 59 runOnlyForDeploymentPostprocessing = 0;
60 }; 60 };
61 /* End PBXFrameworksBuildPhase section */ 61 /* End PBXFrameworksBuildPhase section */
62 62
63 /* Begin PBXGroup section */ 63 /* Begin PBXGroup section */
64 - 0F766276E0F46921DEBF581B /* Frameworks */ = {  
65 - isa = PBXGroup;  
66 - children = (  
67 - 5E133769572782C32D37D8AC /* Pods_Runner.framework */,  
68 - );  
69 - name = Frameworks;  
70 - sourceTree = "<group>";  
71 - };  
72 203D5C95A734778D93D18369 /* Pods */ = { 64 203D5C95A734778D93D18369 /* Pods */ = {
73 isa = PBXGroup; 65 isa = PBXGroup;
74 children = ( 66 children = (
75 - E33BE6AC5C06F7A45470ADE0 /* Pods-Runner.debug.xcconfig */,  
76 - 1CD9C88F6BFEF6CB7CA6746B /* Pods-Runner.release.xcconfig */,  
77 - E29A089CD1D61281C49DBB79 /* Pods-Runner.profile.xcconfig */, 67 + D5B36FCD262B39F867CFDEEE /* Pods-Runner.debug.xcconfig */,
  68 + 32FD382A786B3A0080FE63FD /* Pods-Runner.release.xcconfig */,
  69 + F0D5742F0690BE32D07B033A /* Pods-Runner.profile.xcconfig */,
78 ); 70 );
79 path = Pods; 71 path = Pods;
80 sourceTree = "<group>"; 72 sourceTree = "<group>";
@@ -97,7 +89,7 @@ @@ -97,7 +89,7 @@
97 97C146F01CF9000F007C117D /* Runner */, 89 97C146F01CF9000F007C117D /* Runner */,
98 97C146EF1CF9000F007C117D /* Products */, 90 97C146EF1CF9000F007C117D /* Products */,
99 203D5C95A734778D93D18369 /* Pods */, 91 203D5C95A734778D93D18369 /* Pods */,
100 - 0F766276E0F46921DEBF581B /* Frameworks */, 92 + FF36E403CAC9E06A5A96BB9F /* Frameworks */,
101 ); 93 );
102 sourceTree = "<group>"; 94 sourceTree = "<group>";
103 }; 95 };
@@ -124,6 +116,14 @@ @@ -124,6 +116,14 @@
124 path = Runner; 116 path = Runner;
125 sourceTree = "<group>"; 117 sourceTree = "<group>";
126 }; 118 };
  119 + FF36E403CAC9E06A5A96BB9F /* Frameworks */ = {
  120 + isa = PBXGroup;
  121 + children = (
  122 + 54E006799E73DEAB41FD3623 /* Pods_Runner.framework */,
  123 + );
  124 + name = Frameworks;
  125 + sourceTree = "<group>";
  126 + };
127 /* End PBXGroup section */ 127 /* End PBXGroup section */
128 128
129 /* Begin PBXNativeTarget section */ 129 /* Begin PBXNativeTarget section */
@@ -131,14 +131,14 @@ @@ -131,14 +131,14 @@
131 isa = PBXNativeTarget; 131 isa = PBXNativeTarget;
132 buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 132 buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
133 buildPhases = ( 133 buildPhases = (
134 - 1C759CA63421B131D22BB688 /* [CP] Check Pods Manifest.lock */, 134 + B086C54F5791A4E759CB6822 /* [CP] Check Pods Manifest.lock */,
135 9740EEB61CF901F6004384FC /* Run Script */, 135 9740EEB61CF901F6004384FC /* Run Script */,
136 97C146EA1CF9000F007C117D /* Sources */, 136 97C146EA1CF9000F007C117D /* Sources */,
137 97C146EB1CF9000F007C117D /* Frameworks */, 137 97C146EB1CF9000F007C117D /* Frameworks */,
138 97C146EC1CF9000F007C117D /* Resources */, 138 97C146EC1CF9000F007C117D /* Resources */,
139 9705A1C41CF9048500538489 /* Embed Frameworks */, 139 9705A1C41CF9048500538489 /* Embed Frameworks */,
140 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 140 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
141 - EE97B31B239E017B5516C6AD /* [CP] Embed Pods Frameworks */, 141 + F825A499E8C466DB9DC6247D /* [CP] Embed Pods Frameworks */,
142 ); 142 );
143 buildRules = ( 143 buildRules = (
144 ); 144 );
@@ -197,57 +197,57 @@ @@ -197,57 +197,57 @@
197 /* End PBXResourcesBuildPhase section */ 197 /* End PBXResourcesBuildPhase section */
198 198
199 /* Begin PBXShellScriptBuildPhase section */ 199 /* Begin PBXShellScriptBuildPhase section */
200 - 1C759CA63421B131D22BB688 /* [CP] Check Pods Manifest.lock */ = { 200 + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
201 isa = PBXShellScriptBuildPhase; 201 isa = PBXShellScriptBuildPhase;
202 buildActionMask = 2147483647; 202 buildActionMask = 2147483647;
203 files = ( 203 files = (
204 ); 204 );
205 - inputFileListPaths = (  
206 - );  
207 inputPaths = ( 205 inputPaths = (
208 - "${PODS_PODFILE_DIR_PATH}/Podfile.lock",  
209 - "${PODS_ROOT}/Manifest.lock",  
210 - );  
211 - name = "[CP] Check Pods Manifest.lock";  
212 - outputFileListPaths = (  
213 ); 206 );
  207 + name = "Thin Binary";
214 outputPaths = ( 208 outputPaths = (
215 - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",  
216 ); 209 );
217 runOnlyForDeploymentPostprocessing = 0; 210 runOnlyForDeploymentPostprocessing = 0;
218 shellPath = /bin/sh; 211 shellPath = /bin/sh;
219 - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";  
220 - showEnvVarsInLog = 0; 212 + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
221 }; 213 };
222 - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 214 + 9740EEB61CF901F6004384FC /* Run Script */ = {
223 isa = PBXShellScriptBuildPhase; 215 isa = PBXShellScriptBuildPhase;
224 buildActionMask = 2147483647; 216 buildActionMask = 2147483647;
225 files = ( 217 files = (
226 ); 218 );
227 inputPaths = ( 219 inputPaths = (
228 ); 220 );
229 - name = "Thin Binary"; 221 + name = "Run Script";
230 outputPaths = ( 222 outputPaths = (
231 ); 223 );
232 runOnlyForDeploymentPostprocessing = 0; 224 runOnlyForDeploymentPostprocessing = 0;
233 shellPath = /bin/sh; 225 shellPath = /bin/sh;
234 - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 226 + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
235 }; 227 };
236 - 9740EEB61CF901F6004384FC /* Run Script */ = { 228 + B086C54F5791A4E759CB6822 /* [CP] Check Pods Manifest.lock */ = {
237 isa = PBXShellScriptBuildPhase; 229 isa = PBXShellScriptBuildPhase;
238 buildActionMask = 2147483647; 230 buildActionMask = 2147483647;
239 files = ( 231 files = (
240 ); 232 );
  233 + inputFileListPaths = (
  234 + );
241 inputPaths = ( 235 inputPaths = (
  236 + "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
  237 + "${PODS_ROOT}/Manifest.lock",
  238 + );
  239 + name = "[CP] Check Pods Manifest.lock";
  240 + outputFileListPaths = (
242 ); 241 );
243 - name = "Run Script";  
244 outputPaths = ( 242 outputPaths = (
  243 + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
245 ); 244 );
246 runOnlyForDeploymentPostprocessing = 0; 245 runOnlyForDeploymentPostprocessing = 0;
247 shellPath = /bin/sh; 246 shellPath = /bin/sh;
248 - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 247 + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
  248 + showEnvVarsInLog = 0;
249 }; 249 };
250 - EE97B31B239E017B5516C6AD /* [CP] Embed Pods Frameworks */ = { 250 + F825A499E8C466DB9DC6247D /* [CP] Embed Pods Frameworks */ = {
251 isa = PBXShellScriptBuildPhase; 251 isa = PBXShellScriptBuildPhase;
252 buildActionMask = 2147483647; 252 buildActionMask = 2147483647;
253 files = ( 253 files = (
@@ -355,14 +355,14 @@ @@ -355,14 +355,14 @@
355 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 355 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
356 CLANG_ENABLE_MODULES = YES; 356 CLANG_ENABLE_MODULES = YES;
357 CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 357 CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
358 - DEVELOPMENT_TEAM = 75Y2P2WSQQ; 358 + DEVELOPMENT_TEAM = RCH2VG82SH;
359 ENABLE_BITCODE = NO; 359 ENABLE_BITCODE = NO;
360 INFOPLIST_FILE = Runner/Info.plist; 360 INFOPLIST_FILE = Runner/Info.plist;
361 LD_RUNPATH_SEARCH_PATHS = ( 361 LD_RUNPATH_SEARCH_PATHS = (
362 "$(inherited)", 362 "$(inherited)",
363 "@executable_path/Frameworks", 363 "@executable_path/Frameworks",
364 ); 364 );
365 - PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScannerExample; 365 + PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScanner;
366 PRODUCT_NAME = "$(TARGET_NAME)"; 366 PRODUCT_NAME = "$(TARGET_NAME)";
367 SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 367 SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
368 SWIFT_VERSION = 5.0; 368 SWIFT_VERSION = 5.0;
@@ -484,14 +484,14 @@ @@ -484,14 +484,14 @@
484 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 484 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
485 CLANG_ENABLE_MODULES = YES; 485 CLANG_ENABLE_MODULES = YES;
486 CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 486 CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
487 - DEVELOPMENT_TEAM = 75Y2P2WSQQ; 487 + DEVELOPMENT_TEAM = RCH2VG82SH;
488 ENABLE_BITCODE = NO; 488 ENABLE_BITCODE = NO;
489 INFOPLIST_FILE = Runner/Info.plist; 489 INFOPLIST_FILE = Runner/Info.plist;
490 LD_RUNPATH_SEARCH_PATHS = ( 490 LD_RUNPATH_SEARCH_PATHS = (
491 "$(inherited)", 491 "$(inherited)",
492 "@executable_path/Frameworks", 492 "@executable_path/Frameworks",
493 ); 493 );
494 - PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScannerExample; 494 + PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScanner;
495 PRODUCT_NAME = "$(TARGET_NAME)"; 495 PRODUCT_NAME = "$(TARGET_NAME)";
496 SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 496 SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
497 SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 497 SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -507,14 +507,14 @@ @@ -507,14 +507,14 @@
507 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 507 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
508 CLANG_ENABLE_MODULES = YES; 508 CLANG_ENABLE_MODULES = YES;
509 CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 509 CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
510 - DEVELOPMENT_TEAM = 75Y2P2WSQQ; 510 + DEVELOPMENT_TEAM = RCH2VG82SH;
511 ENABLE_BITCODE = NO; 511 ENABLE_BITCODE = NO;
512 INFOPLIST_FILE = Runner/Info.plist; 512 INFOPLIST_FILE = Runner/Info.plist;
513 LD_RUNPATH_SEARCH_PATHS = ( 513 LD_RUNPATH_SEARCH_PATHS = (
514 "$(inherited)", 514 "$(inherited)",
515 "@executable_path/Frameworks", 515 "@executable_path/Frameworks",
516 ); 516 );
517 - PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScannerExample; 517 + PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScanner;
518 PRODUCT_NAME = "$(TARGET_NAME)"; 518 PRODUCT_NAME = "$(TARGET_NAME)";
519 SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 519 SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
520 SWIFT_VERSION = 5.0; 520 SWIFT_VERSION = 5.0;
@@ -2,10 +2,8 @@ @@ -2,10 +2,8 @@
2 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 2 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 <plist version="1.0"> 3 <plist version="1.0">
4 <dict> 4 <dict>
5 - <key>NSPhotoLibraryUsageDescription</key>  
6 - <string>We need access in order to open photos of barcodes</string>  
7 - <key>NSCameraUsageDescription</key>  
8 - <string>We use the camera to scan barcodes</string> 5 + <key>CADisableMinimumFrameDurationOnPhone</key>
  6 + <true/>
9 <key>CFBundleDevelopmentRegion</key> 7 <key>CFBundleDevelopmentRegion</key>
10 <string>$(DEVELOPMENT_LANGUAGE)</string> 8 <string>$(DEVELOPMENT_LANGUAGE)</string>
11 <key>CFBundleDisplayName</key> 9 <key>CFBundleDisplayName</key>
@@ -28,6 +26,10 @@ @@ -28,6 +26,10 @@
28 <string>$(FLUTTER_BUILD_NUMBER)</string> 26 <string>$(FLUTTER_BUILD_NUMBER)</string>
29 <key>LSRequiresIPhoneOS</key> 27 <key>LSRequiresIPhoneOS</key>
30 <true/> 28 <true/>
  29 + <key>NSCameraUsageDescription</key>
  30 + <string>We use the camera to scan barcodes</string>
  31 + <key>NSPhotoLibraryUsageDescription</key>
  32 + <string>We need access in order to open photos of barcodes</string>
31 <key>UILaunchStoryboardName</key> 33 <key>UILaunchStoryboardName</key>
32 <string>LaunchScreen</string> 34 <string>LaunchScreen</string>
33 <key>UIMainStoryboardFile</key> 35 <key>UIMainStoryboardFile</key>
@@ -47,7 +49,5 @@ @@ -47,7 +49,5 @@
47 </array> 49 </array>
48 <key>UIViewControllerBasedStatusBarAppearance</key> 50 <key>UIViewControllerBasedStatusBarAppearance</key>
49 <false/> 51 <false/>
50 - <key>CADisableMinimumFrameDurationOnPhone</key>  
51 - <true/>  
52 </dict> 52 </dict>
53 </plist> 53 </plist>
@@ -133,9 +133,9 @@ class _BarcodeScannerWithControllerState @@ -133,9 +133,9 @@ class _BarcodeScannerWithControllerState
133 icon: const Icon(Icons.image), 133 icon: const Icon(Icons.image),
134 iconSize: 32.0, 134 iconSize: 32.0,
135 onPressed: () async { 135 onPressed: () async {
136 - final ImagePicker _picker = ImagePicker(); 136 + final ImagePicker picker = ImagePicker();
137 // Pick an image 137 // Pick an image
138 - final XFile? image = await _picker.pickImage( 138 + final XFile? image = await picker.pickImage(
139 source: ImageSource.gallery, 139 source: ImageSource.gallery,
140 ); 140 );
141 if (image != null) { 141 if (image != null) {
  1 +import 'dart:typed_data';
  2 +
  3 +import 'package:flutter/material.dart';
  4 +import 'package:mobile_scanner/mobile_scanner.dart';
  5 +
  6 +class BarcodeScannerReturningImage extends StatefulWidget {
  7 + const BarcodeScannerReturningImage({Key? key}) : super(key: key);
  8 +
  9 + @override
  10 + _BarcodeScannerReturningImageState createState() =>
  11 + _BarcodeScannerReturningImageState();
  12 +}
  13 +
  14 +class _BarcodeScannerReturningImageState
  15 + extends State<BarcodeScannerReturningImage>
  16 + with SingleTickerProviderStateMixin {
  17 + String? barcode;
  18 + Uint8List? image;
  19 +
  20 + MobileScannerController controller = MobileScannerController(
  21 + torchEnabled: true,
  22 + returnImage: true,
  23 + // formats: [BarcodeFormat.qrCode]
  24 + // facing: CameraFacing.front,
  25 + );
  26 +
  27 + bool isStarted = true;
  28 +
  29 + @override
  30 + Widget build(BuildContext context) {
  31 + return Scaffold(
  32 + backgroundColor: Colors.black,
  33 + body: Builder(
  34 + builder: (context) {
  35 + return Stack(
  36 + children: [
  37 + MobileScanner(
  38 + controller: controller,
  39 + fit: BoxFit.contain,
  40 + // allowDuplicates: true,
  41 + // controller: MobileScannerController(
  42 + // torchEnabled: true,
  43 + // facing: CameraFacing.front,
  44 + // ),
  45 + onDetect: (barcode, args) {
  46 + setState(() {
  47 + this.barcode = barcode.rawValue;
  48 + showDialog(
  49 + context: context,
  50 + builder: (context) => Image(
  51 + image: MemoryImage(image!),
  52 + fit: BoxFit.contain,
  53 + ),
  54 + );
  55 + image = barcode.image;
  56 + });
  57 + },
  58 + ),
  59 + Align(
  60 + alignment: Alignment.bottomCenter,
  61 + child: Container(
  62 + alignment: Alignment.bottomCenter,
  63 + height: 100,
  64 + color: Colors.black.withOpacity(0.4),
  65 + child: Row(
  66 + mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  67 + children: [
  68 + IconButton(
  69 + color: Colors.white,
  70 + icon: ValueListenableBuilder(
  71 + valueListenable: controller.torchState,
  72 + builder: (context, state, child) {
  73 + if (state == null) {
  74 + return const Icon(
  75 + Icons.flash_off,
  76 + color: Colors.grey,
  77 + );
  78 + }
  79 + switch (state as TorchState) {
  80 + case TorchState.off:
  81 + return const Icon(
  82 + Icons.flash_off,
  83 + color: Colors.grey,
  84 + );
  85 + case TorchState.on:
  86 + return const Icon(
  87 + Icons.flash_on,
  88 + color: Colors.yellow,
  89 + );
  90 + }
  91 + },
  92 + ),
  93 + iconSize: 32.0,
  94 + onPressed: () => controller.toggleTorch(),
  95 + ),
  96 + IconButton(
  97 + color: Colors.white,
  98 + icon: isStarted
  99 + ? const Icon(Icons.stop)
  100 + : const Icon(Icons.play_arrow),
  101 + iconSize: 32.0,
  102 + onPressed: () => setState(() {
  103 + isStarted ? controller.stop() : controller.start();
  104 + isStarted = !isStarted;
  105 + }),
  106 + ),
  107 + Center(
  108 + child: SizedBox(
  109 + width: MediaQuery.of(context).size.width - 200,
  110 + height: 50,
  111 + child: FittedBox(
  112 + child: Text(
  113 + barcode ?? 'Scan something!',
  114 + overflow: TextOverflow.fade,
  115 + style: Theme.of(context)
  116 + .textTheme
  117 + .headline4!
  118 + .copyWith(color: Colors.white),
  119 + ),
  120 + ),
  121 + ),
  122 + ),
  123 + IconButton(
  124 + color: Colors.white,
  125 + icon: ValueListenableBuilder(
  126 + valueListenable: controller.cameraFacingState,
  127 + builder: (context, state, child) {
  128 + if (state == null) {
  129 + return const Icon(Icons.camera_front);
  130 + }
  131 + switch (state as CameraFacing) {
  132 + case CameraFacing.front:
  133 + return const Icon(Icons.camera_front);
  134 + case CameraFacing.back:
  135 + return const Icon(Icons.camera_rear);
  136 + }
  137 + },
  138 + ),
  139 + iconSize: 32.0,
  140 + onPressed: () => controller.switchCamera(),
  141 + ),
  142 + SizedBox(
  143 + width: 50,
  144 + height: 50,
  145 + child: image != null
  146 + ? Image(
  147 + image: MemoryImage(image!),
  148 + fit: BoxFit.contain,
  149 + )
  150 + : Container(),
  151 + ),
  152 + ],
  153 + ),
  154 + ),
  155 + ),
  156 + ],
  157 + );
  158 + },
  159 + ),
  160 + );
  161 + }
  162 +}
1 import 'package:flutter/material.dart'; 1 import 'package:flutter/material.dart';
2 import 'package:mobile_scanner_example/barcode_scanner_controller.dart'; 2 import 'package:mobile_scanner_example/barcode_scanner_controller.dart';
  3 +import 'package:mobile_scanner_example/barcode_scanner_returning_image.dart';
3 import 'package:mobile_scanner_example/barcode_scanner_without_controller.dart'; 4 import 'package:mobile_scanner_example/barcode_scanner_without_controller.dart';
4 5
5 void main() => runApp(const MaterialApp(home: MyHome())); 6 void main() => runApp(const MaterialApp(home: MyHome()));
@@ -31,6 +32,17 @@ class MyHome extends StatelessWidget { @@ -31,6 +32,17 @@ class MyHome extends StatelessWidget {
31 onPressed: () { 32 onPressed: () {
32 Navigator.of(context).push( 33 Navigator.of(context).push(
33 MaterialPageRoute( 34 MaterialPageRoute(
  35 + builder: (context) => const BarcodeScannerReturningImage(),
  36 + ),
  37 + );
  38 + },
  39 + child:
  40 + const Text('MobileScanner with Controller (returning image)'),
  41 + ),
  42 + ElevatedButton(
  43 + onPressed: () {
  44 + Navigator.of(context).push(
  45 + MaterialPageRoute(
34 builder: (context) => 46 builder: (context) =>
35 const BarcodeScannerWithoutController(), 47 const BarcodeScannerWithoutController(),
36 ), 48 ),
@@ -16,7 +16,7 @@ dependencies: @@ -16,7 +16,7 @@ dependencies:
16 dev_dependencies: 16 dev_dependencies:
17 flutter_test: 17 flutter_test:
18 sdk: flutter 18 sdk: flutter
19 - lint: ^1.8.2 19 + lint: ^1.10.0
20 20
21 flutter: 21 flutter:
22 uses-material-design: true 22 uses-material-design: true
@@ -21,6 +21,9 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -21,6 +21,9 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
21 21
22 // Image to be sent to the texture 22 // Image to be sent to the texture
23 var latestBuffer: CVImageBuffer! 23 var latestBuffer: CVImageBuffer!
  24 +
  25 + // Return image buffer with the Barcode event
  26 + var returnImage: Bool = false
24 27
25 // var analyzeMode: Int = 0 28 // var analyzeMode: Int = 0
26 var analyzing: Bool = false 29 var analyzing: Bool = false
@@ -61,6 +64,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -61,6 +64,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
61 stop(result) 64 stop(result)
62 case "analyzeImage": 65 case "analyzeImage":
63 analyzeImage(call, result) 66 analyzeImage(call, result)
  67 +
64 default: 68 default:
65 result(FlutterMethodNotImplemented) 69 result(FlutterMethodNotImplemented)
66 } 70 }
@@ -85,7 +89,17 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -85,7 +89,17 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
85 } 89 }
86 return Unmanaged<CVPixelBuffer>.passRetained(latestBuffer) 90 return Unmanaged<CVPixelBuffer>.passRetained(latestBuffer)
87 } 91 }
88 - 92 +
  93 + private func ciImageToJpeg(ciImage: CIImage) -> Data {
  94 +
  95 + // let ciImage = CIImage(cvPixelBuffer: latestBuffer)
  96 + let context:CIContext = CIContext.init(options: nil)
  97 + let cgImage:CGImage = context.createCGImage(ciImage, from: ciImage.extent)!
  98 + let uiImage:UIImage = UIImage(cgImage: cgImage, scale: 1, orientation: UIImage.Orientation.up)
  99 +
  100 + return uiImage.jpegData(compressionQuality: 0.8)!;
  101 + }
  102 +
89 // Gets called when a new image is added to the buffer 103 // Gets called when a new image is added to the buffer
90 public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { 104 public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
91 105
@@ -108,7 +122,13 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -108,7 +122,13 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
108 scanner.process(image) { [self] barcodes, error in 122 scanner.process(image) { [self] barcodes, error in
109 if error == nil && barcodes != nil { 123 if error == nil && barcodes != nil {
110 for barcode in barcodes! { 124 for barcode in barcodes! {
111 - let event: [String: Any?] = ["name": "barcode", "data": barcode.data] 125 +
  126 + var event: [String: Any?] = ["name": "barcode", "data": barcode.data]
  127 + if (returnImage && latestBuffer != nil) {
  128 + let image: CIImage = CIImage(cvPixelBuffer: latestBuffer)
  129 +
  130 + event["image"] = FlutterStandardTypedData(bytes: ciImageToJpeg(ciImage: image))
  131 + }
112 sink?(event) 132 sink?(event)
113 } 133 }
114 } 134 }
@@ -167,6 +187,8 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan @@ -167,6 +187,8 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin, FlutterStreamHan
167 captureSession = AVCaptureSession() 187 captureSession = AVCaptureSession()
168 188
169 let argReader = MapArgumentReader(call.arguments as? [String: Any]) 189 let argReader = MapArgumentReader(call.arguments as? [String: Any])
  190 +
  191 + returnImage = argReader.bool(key: "returnImage") ?? false
170 192
171 // let ratio: Int = argReader.int(key: "ratio") 193 // let ratio: Int = argReader.int(key: "ratio")
172 let torch: Bool = argReader.bool(key: "torch") ?? false 194 let torch: Bool = argReader.bool(key: "torch") ?? false
@@ -15,7 +15,7 @@ An universal scanner for Flutter based on MLKit. @@ -15,7 +15,7 @@ An universal scanner for Flutter based on MLKit.
15 s.source = { :path => '.' } 15 s.source = { :path => '.' }
16 s.source_files = 'Classes/**/*' 16 s.source_files = 'Classes/**/*'
17 s.dependency 'Flutter' 17 s.dependency 'Flutter'
18 - s.dependency 'GoogleMLKit/BarcodeScanning', '~> 2.6.0' 18 + s.dependency 'GoogleMLKit/BarcodeScanning', '~> 3.2.0'
19 s.platform = :ios, '10.0' 19 s.platform = :ios, '10.0'
20 s.static_framework = true 20 s.static_framework = true
21 # Flutter.framework does not contain a i386 slice. 21 # Flutter.framework does not contain a i386 slice.
@@ -200,7 +200,11 @@ class MobileScannerWebPlugin { @@ -200,7 +200,11 @@ class MobileScannerWebPlugin {
200 200
201 final code = jsQR(imgData.data, canvas.width, canvas.height); 201 final code = jsQR(imgData.data, canvas.width, canvas.height);
202 if (code != null) { 202 if (code != null) {
203 - controller.add({'name': 'barcodeWeb', 'data': code.data}); 203 + controller.add({
  204 + 'name': 'barcodeWeb',
  205 + 'data': code.data,
  206 + 'binaryData': code.binaryData,
  207 + });
204 } 208 }
205 } 209 }
206 } 210 }
@@ -32,13 +32,13 @@ class MobileScanner extends StatefulWidget { @@ -32,13 +32,13 @@ class MobileScanner extends StatefulWidget {
32 32
33 /// Create a [MobileScanner] with a [controller], the [controller] must has been initialized. 33 /// Create a [MobileScanner] with a [controller], the [controller] must has been initialized.
34 const MobileScanner({ 34 const MobileScanner({
35 - Key? key, 35 + super.key,
36 required this.onDetect, 36 required this.onDetect,
37 this.controller, 37 this.controller,
38 this.fit = BoxFit.cover, 38 this.fit = BoxFit.cover,
39 this.allowDuplicates = false, 39 this.allowDuplicates = false,
40 this.onPermissionSet, 40 this.onPermissionSet,
41 - }) : super(key: key); 41 + });
42 42
43 @override 43 @override
44 State<MobileScanner> createState() => _MobileScannerState(); 44 State<MobileScanner> createState() => _MobileScannerState();
@@ -53,13 +53,14 @@ class _MobileScannerState extends State<MobileScanner> @@ -53,13 +53,14 @@ class _MobileScannerState extends State<MobileScanner>
53 super.initState(); 53 super.initState();
54 WidgetsBinding.instance.addObserver(this); 54 WidgetsBinding.instance.addObserver(this);
55 controller = widget.controller ?? MobileScannerController(onPermissionSet: widget.onPermissionSet); 55 controller = widget.controller ?? MobileScannerController(onPermissionSet: widget.onPermissionSet);
  56 + if (!controller.isStarting) controller.start();
56 } 57 }
57 58
58 @override 59 @override
59 void didChangeAppLifecycleState(AppLifecycleState state) { 60 void didChangeAppLifecycleState(AppLifecycleState state) {
60 switch (state) { 61 switch (state) {
61 case AppLifecycleState.resumed: 62 case AppLifecycleState.resumed:
62 - if (!controller.isStarting) controller.start(); 63 + if (!controller.isStarting && controller.autoResume) controller.start();
63 break; 64 break;
64 case AppLifecycleState.inactive: 65 case AppLifecycleState.inactive:
65 case AppLifecycleState.paused: 66 case AppLifecycleState.paused:
@@ -73,44 +74,40 @@ class _MobileScannerState extends State<MobileScanner> @@ -73,44 +74,40 @@ class _MobileScannerState extends State<MobileScanner>
73 74
74 @override 75 @override
75 Widget build(BuildContext context) { 76 Widget build(BuildContext context) {
76 - return LayoutBuilder(  
77 - builder: (context, BoxConstraints constraints) {  
78 - return ValueListenableBuilder(  
79 - valueListenable: controller.args,  
80 - builder: (context, value, child) {  
81 - value = value as MobileScannerArguments?;  
82 - if (value == null) {  
83 - return Container(color: Colors.black); 77 + return ValueListenableBuilder(
  78 + valueListenable: controller.args,
  79 + builder: (context, value, child) {
  80 + value = value as MobileScannerArguments?;
  81 + if (value == null) {
  82 + return const ColoredBox(color: Colors.black);
  83 + } else {
  84 + controller.barcodes.listen((barcode) {
  85 + if (!widget.allowDuplicates) {
  86 + if (lastScanned != barcode.rawValue) {
  87 + lastScanned = barcode.rawValue;
  88 + widget.onDetect(barcode, value! as MobileScannerArguments);
  89 + }
84 } else { 90 } else {
85 - controller.barcodes.listen((barcode) {  
86 - if (!widget.allowDuplicates) {  
87 - if (lastScanned != barcode.rawValue) {  
88 - lastScanned = barcode.rawValue;  
89 - widget.onDetect(barcode, value! as MobileScannerArguments);  
90 - }  
91 - } else {  
92 - widget.onDetect(barcode, value! as MobileScannerArguments);  
93 - }  
94 - });  
95 - return ClipRect( 91 + widget.onDetect(barcode, value! as MobileScannerArguments);
  92 + }
  93 + });
  94 + return ClipRect(
  95 + child: SizedBox(
  96 + width: MediaQuery.of(context).size.width,
  97 + height: MediaQuery.of(context).size.height,
  98 + child: FittedBox(
  99 + fit: widget.fit,
96 child: SizedBox( 100 child: SizedBox(
97 - width: MediaQuery.of(context).size.width,  
98 - height: MediaQuery.of(context).size.height,  
99 - child: FittedBox(  
100 - fit: widget.fit,  
101 - child: SizedBox(  
102 - width: value.size.width,  
103 - height: value.size.height,  
104 - child: kIsWeb  
105 - ? HtmlElementView(viewType: value.webId!)  
106 - : Texture(textureId: value.textureId!),  
107 - ),  
108 - ), 101 + width: value.size.width,
  102 + height: value.size.height,
  103 + child: kIsWeb
  104 + ? HtmlElementView(viewType: value.webId!)
  105 + : Texture(textureId: value.textureId!),
109 ), 106 ),
110 - );  
111 - }  
112 - },  
113 - ); 107 + ),
  108 + ),
  109 + );
  110 + }
114 }, 111 },
115 ); 112 );
116 } 113 }
@@ -35,7 +35,8 @@ class MobileScannerController { @@ -35,7 +35,8 @@ class MobileScannerController {
35 EventChannel eventChannel = 35 EventChannel eventChannel =
36 const EventChannel('dev.steenbakker.mobile_scanner/scanner/event'); 36 const EventChannel('dev.steenbakker.mobile_scanner/scanner/event');
37 37
38 - int? _controllerHashcode; 38 + //Must be static to keep the same value on new instances
  39 + static int? _controllerHashcode;
39 StreamSubscription? events; 40 StreamSubscription? events;
40 41
41 Function(bool permissionGranted)? onPermissionSet; 42 Function(bool permissionGranted)? onPermissionSet;
@@ -44,6 +45,8 @@ class MobileScannerController { @@ -44,6 +45,8 @@ class MobileScannerController {
44 late final ValueNotifier<CameraFacing> cameraFacingState; 45 late final ValueNotifier<CameraFacing> cameraFacingState;
45 final Ratio? ratio; 46 final Ratio? ratio;
46 final bool? torchEnabled; 47 final bool? torchEnabled;
  48 + // Whether to return the image buffer with the Barcode event
  49 + final bool returnImage;
47 50
48 /// If provided, the scanner will only detect those specific formats. 51 /// If provided, the scanner will only detect those specific formats.
49 /// 52 ///
@@ -54,6 +57,9 @@ class MobileScannerController { @@ -54,6 +57,9 @@ class MobileScannerController {
54 bool hasTorch = false; 57 bool hasTorch = false;
55 late StreamController<Barcode> barcodesController; 58 late StreamController<Barcode> barcodesController;
56 59
  60 + /// Whether to automatically resume the camera when the application is resumed
  61 + bool autoResume;
  62 +
57 Stream<Barcode> get barcodes => barcodesController.stream; 63 Stream<Barcode> get barcodes => barcodesController.stream;
58 64
59 MobileScannerController({ 65 MobileScannerController({
@@ -62,6 +68,8 @@ class MobileScannerController { @@ -62,6 +68,8 @@ class MobileScannerController {
62 this.torchEnabled, 68 this.torchEnabled,
63 this.formats, 69 this.formats,
64 this.onPermissionSet, 70 this.onPermissionSet,
  71 + this.autoResume = true,
  72 + this.returnImage = false,
65 }) { 73 }) {
66 // In case a new instance is created before calling dispose() 74 // In case a new instance is created before calling dispose()
67 if (_controllerHashcode != null) { 75 if (_controllerHashcode != null) {
@@ -77,8 +85,6 @@ class MobileScannerController { @@ -77,8 +85,6 @@ class MobileScannerController {
77 // onCancel: () => setAnalyzeMode(AnalyzeMode.none.index), 85 // onCancel: () => setAnalyzeMode(AnalyzeMode.none.index),
78 ); 86 );
79 87
80 - start();  
81 -  
82 // Listen to events from the platform specific code 88 // Listen to events from the platform specific code
83 events = eventChannel 89 events = eventChannel
84 .receiveBroadcastStream() 90 .receiveBroadcastStream()
@@ -88,24 +94,32 @@ class MobileScannerController { @@ -88,24 +94,32 @@ class MobileScannerController {
88 void handleEvent(Map event) { 94 void handleEvent(Map event) {
89 final name = event['name']; 95 final name = event['name'];
90 final data = event['data']; 96 final data = event['data'];
  97 + final binaryData = event['binaryData'];
91 switch (name) { 98 switch (name) {
92 case 'torchState': 99 case 'torchState':
93 - final state = TorchState.values[data as int]; 100 + final state = TorchState.values[data as int? ?? 0];
94 torchState.value = state; 101 torchState.value = state;
95 break; 102 break;
96 case 'barcode': 103 case 'barcode':
97 - final barcode = Barcode.fromNative(data as Map); 104 + final image = returnImage ? event['image'] as Uint8List : null;
  105 + final barcode = Barcode.fromNative(data as Map? ?? {}, image);
98 barcodesController.add(barcode); 106 barcodesController.add(barcode);
99 break; 107 break;
100 case 'barcodeMac': 108 case 'barcodeMac':
101 barcodesController.add( 109 barcodesController.add(
102 Barcode( 110 Barcode(
103 - rawValue: (data as Map)['payload'] as String, 111 + rawValue: (data as Map)['payload'] as String?,
104 ), 112 ),
105 ); 113 );
106 break; 114 break;
107 case 'barcodeWeb': 115 case 'barcodeWeb':
108 - barcodesController.add(Barcode(rawValue: data as String)); 116 + final bytes = (binaryData as List).cast<int>();
  117 + barcodesController.add(
  118 + Barcode(
  119 + rawValue: data as String?,
  120 + rawBytes: Uint8List.fromList(bytes),
  121 + ),
  122 + );
109 break; 123 break;
110 default: 124 default:
111 throw UnimplementedError(); 125 throw UnimplementedError();
@@ -136,11 +150,11 @@ class MobileScannerController { @@ -136,11 +150,11 @@ class MobileScannerController {
136 // Check authorization status 150 // Check authorization status
137 if (!kIsWeb) { 151 if (!kIsWeb) {
138 MobileScannerState state = MobileScannerState 152 MobileScannerState state = MobileScannerState
139 - .values[await methodChannel.invokeMethod('state') as int]; 153 + .values[await methodChannel.invokeMethod('state') as int? ?? 0];
140 switch (state) { 154 switch (state) {
141 case MobileScannerState.undetermined: 155 case MobileScannerState.undetermined:
142 final bool result = 156 final bool result =
143 - await methodChannel.invokeMethod('request') as bool; 157 + await methodChannel.invokeMethod('request') as bool? ?? false;
144 state = result 158 state = result
145 ? MobileScannerState.authorized 159 ? MobileScannerState.authorized
146 : MobileScannerState.denied; 160 : MobileScannerState.denied;
@@ -171,6 +185,7 @@ class MobileScannerController { @@ -171,6 +185,7 @@ class MobileScannerController {
171 arguments['formats'] = formats!.map((e) => e.rawValue).toList(); 185 arguments['formats'] = formats!.map((e) => e.rawValue).toList();
172 } 186 }
173 } 187 }
  188 + arguments['returnImage'] = returnImage;
174 189
175 // Start the camera with arguments 190 // Start the camera with arguments
176 Map<String, dynamic>? startResult = {}; 191 Map<String, dynamic>? startResult = {};
@@ -194,7 +209,7 @@ class MobileScannerController { @@ -194,7 +209,7 @@ class MobileScannerController {
194 throw PlatformException(code: 'INITIALIZATION ERROR'); 209 throw PlatformException(code: 'INITIALIZATION ERROR');
195 } 210 }
196 211
197 - hasTorch = startResult['torchable'] as bool; 212 + hasTorch = startResult['torchable'] as bool? ?? false;
198 213
199 if (kIsWeb) { 214 if (kIsWeb) {
200 onPermissionSet?.call(true); // If we reach this line, it means camera permission has been granted 215 onPermissionSet?.call(true); // If we reach this line, it means camera permission has been granted
@@ -202,15 +217,15 @@ class MobileScannerController { @@ -202,15 +217,15 @@ class MobileScannerController {
202 args.value = MobileScannerArguments( 217 args.value = MobileScannerArguments(
203 webId: startResult['ViewID'] as String?, 218 webId: startResult['ViewID'] as String?,
204 size: Size( 219 size: Size(
205 - startResult['videoWidth'] as double,  
206 - startResult['videoHeight'] as double, 220 + startResult['videoWidth'] as double? ?? 0,
  221 + startResult['videoHeight'] as double? ?? 0,
207 ), 222 ),
208 hasTorch: hasTorch, 223 hasTorch: hasTorch,
209 ); 224 );
210 } else { 225 } else {
211 args.value = MobileScannerArguments( 226 args.value = MobileScannerArguments(
212 - textureId: startResult['textureId'] as int,  
213 - size: toSize(startResult['size'] as Map), 227 + textureId: startResult['textureId'] as int?,
  228 + size: toSize(startResult['size'] as Map? ?? {}),
214 hasTorch: hasTorch, 229 hasTorch: hasTorch,
215 ); 230 );
216 } 231 }
@@ -12,6 +12,11 @@ class Barcode { @@ -12,6 +12,11 @@ class Barcode {
12 /// Returns null if the corner points can not be determined. 12 /// Returns null if the corner points can not be determined.
13 final List<Offset>? corners; 13 final List<Offset>? corners;
14 14
  15 + /// Returns raw bytes of the image buffer
  16 + ///
  17 + /// Returns null if the image was not returned
  18 + final Uint8List? image;
  19 +
15 /// Returns barcode format 20 /// Returns barcode format
16 final BarcodeFormat format; 21 final BarcodeFormat format;
17 22
@@ -74,6 +79,7 @@ class Barcode { @@ -74,6 +79,7 @@ class Barcode {
74 79
75 Barcode({ 80 Barcode({
76 this.corners, 81 this.corners,
  82 + this.image,
77 this.format = BarcodeFormat.ean13, 83 this.format = BarcodeFormat.ean13,
78 this.rawBytes, 84 this.rawBytes,
79 this.type = BarcodeType.text, 85 this.type = BarcodeType.text,
@@ -91,7 +97,7 @@ class Barcode { @@ -91,7 +97,7 @@ class Barcode {
91 }); 97 });
92 98
93 /// Create a [Barcode] from native data. 99 /// Create a [Barcode] from native data.
94 - Barcode.fromNative(Map data) 100 + Barcode.fromNative(Map data, this.image)
95 : corners = toCorners(data['corners'] as List?), 101 : corners = toCorners(data['corners'] as List?),
96 format = toFormat(data['format'] as int), 102 format = toFormat(data['format'] as int),
97 rawBytes = data['rawBytes'] as Uint8List?, 103 rawBytes = data['rawBytes'] as Uint8List?,
1 @JS() 1 @JS()
2 library jsqr; 2 library jsqr;
3 3
  4 +import 'dart:typed_data';
  5 +
4 import 'package:js/js.dart'; 6 import 'package:js/js.dart';
5 7
6 @JS('jsQR') 8 @JS('jsQR')
@@ -9,4 +11,6 @@ external Code? jsQR(dynamic data, int? width, int? height); @@ -9,4 +11,6 @@ external Code? jsQR(dynamic data, int? width, int? height);
9 @JS() 11 @JS()
10 class Code { 12 class Code {
11 external String get data; 13 external String get data;
  14 +
  15 + external Uint8ClampedList get binaryData;
12 } 16 }
@@ -17,7 +17,7 @@ dependencies: @@ -17,7 +17,7 @@ dependencies:
17 dev_dependencies: 17 dev_dependencies:
18 flutter_test: 18 flutter_test:
19 sdk: flutter 19 sdk: flutter
20 - lint: ^1.8.2 20 + lint: ^1.10.0
21 21
22 flutter: 22 flutter:
23 plugin: 23 plugin: