Committed by
GitHub
Merge branch 'master' into master
Showing
34 changed files
with
265 additions
and
183 deletions
| @@ -11,7 +11,7 @@ jobs: | @@ -11,7 +11,7 @@ jobs: | ||
| 11 | analysis: | 11 | analysis: |
| 12 | runs-on: ubuntu-latest | 12 | runs-on: ubuntu-latest |
| 13 | steps: | 13 | steps: |
| 14 | - - uses: actions/checkout@v3.5.2 | 14 | + - uses: actions/checkout@v3.5.3 |
| 15 | - uses: actions/setup-java@v3.11.0 | 15 | - uses: actions/setup-java@v3.11.0 |
| 16 | with: | 16 | with: |
| 17 | java-version: 11 | 17 | java-version: 11 |
| @@ -28,7 +28,7 @@ jobs: | @@ -28,7 +28,7 @@ jobs: | ||
| 28 | formatting: | 28 | formatting: |
| 29 | runs-on: ubuntu-latest | 29 | runs-on: ubuntu-latest |
| 30 | steps: | 30 | steps: |
| 31 | - - uses: actions/checkout@v3.5.2 | 31 | + - uses: actions/checkout@v3.5.3 |
| 32 | - uses: actions/setup-java@v3.11.0 | 32 | - uses: actions/setup-java@v3.11.0 |
| 33 | with: | 33 | with: |
| 34 | java-version: 11 | 34 | java-version: 11 |
| @@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
| 7 | release-please: | 7 | release-please: |
| 8 | runs-on: ubuntu-latest | 8 | runs-on: ubuntu-latest |
| 9 | steps: | 9 | steps: |
| 10 | - - uses: GoogleCloudPlatform/release-please-action@v3.7.9 | 10 | + - uses: GoogleCloudPlatform/release-please-action@v3.7.10 |
| 11 | with: | 11 | with: |
| 12 | token: ${{ secrets.GITHUB_TOKEN }} | 12 | token: ${{ secrets.GITHUB_TOKEN }} |
| 13 | release-type: simple | 13 | release-type: simple |
| 1 | # This file tracks properties of this Flutter project. | 1 | # This file tracks properties of this Flutter project. |
| 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. | 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. |
| 3 | # | 3 | # |
| 4 | -# This file should be version controlled and should not be manually edited. | 4 | +# This file should be version controlled. |
| 5 | 5 | ||
| 6 | version: | 6 | version: |
| 7 | - revision: 5f105a6ca7a5ac7b8bc9b241f4c2d86f4188cf5c | 7 | + revision: 796c8ef79279f9c774545b3771238c3098dbefab |
| 8 | channel: stable | 8 | channel: stable |
| 9 | 9 | ||
| 10 | project_type: plugin | 10 | project_type: plugin |
| 11 | + | ||
| 12 | +# Tracks metadata for the flutter migrate command | ||
| 13 | +migration: | ||
| 14 | + platforms: | ||
| 15 | + - platform: root | ||
| 16 | + create_revision: 796c8ef79279f9c774545b3771238c3098dbefab | ||
| 17 | + base_revision: 796c8ef79279f9c774545b3771238c3098dbefab | ||
| 18 | + - platform: android | ||
| 19 | + create_revision: 796c8ef79279f9c774545b3771238c3098dbefab | ||
| 20 | + base_revision: 796c8ef79279f9c774545b3771238c3098dbefab | ||
| 21 | + - platform: ios | ||
| 22 | + create_revision: 796c8ef79279f9c774545b3771238c3098dbefab | ||
| 23 | + base_revision: 796c8ef79279f9c774545b3771238c3098dbefab | ||
| 24 | + - platform: macos | ||
| 25 | + create_revision: 796c8ef79279f9c774545b3771238c3098dbefab | ||
| 26 | + base_revision: 796c8ef79279f9c774545b3771238c3098dbefab | ||
| 27 | + - platform: web | ||
| 28 | + create_revision: 796c8ef79279f9c774545b3771238c3098dbefab | ||
| 29 | + base_revision: 796c8ef79279f9c774545b3771238c3098dbefab | ||
| 30 | + | ||
| 31 | + # User provided section | ||
| 32 | + | ||
| 33 | + # List of Local paths (relative to this file) that should be | ||
| 34 | + # ignored by the migrate tool. | ||
| 35 | + # | ||
| 36 | + # Files that are not part of the templates will be ignored by default. | ||
| 37 | + unmanaged_files: | ||
| 38 | + - 'lib/main.dart' | ||
| 39 | + - 'ios/Runner.xcodeproj/project.pbxproj' |
| 1 | -## 3.2.1 | 1 | +## 3.3.0 |
| 2 | Bugs fixed: | 2 | Bugs fixed: |
| 3 | +* Fixed bug where onDetect method was being called multiple times | ||
| 3 | * [Android] Fix Gradle 8 compatibility by adding the `namespace` attribute to the build.gradle. | 4 | * [Android] Fix Gradle 8 compatibility by adding the `namespace` attribute to the build.gradle. |
| 4 | 5 | ||
| 6 | +Improvements: | ||
| 7 | +* [Android] Upgraded camera2 dependency | ||
| 8 | +* Added zoomScale value notifier in MobileScannerController for the application to know the zoom scale value set actually. | ||
| 9 | + The value is notified from the native SDK(CameraX/AVFoundation). | ||
| 10 | +* Added resetZoomScale() in MobileScannerController to reset zoom ratio with 1x. | ||
| 11 | + Both Android and iOS, if the device have ultra-wide camera, calling setZoomScale with small value causes to use ultra-wide camera and may be diffcult to detect barcodes. | ||
| 12 | + resetZoomScale() is useful to use standard camera with zoom 1x. | ||
| 13 | + setZoomScale() with the specific value can realize same effect, but added resetZoomScale for avoiding floating point errors. | ||
| 14 | + The application can know what zoom scale value is selected actually by subscribing zoomScale above after calling resetZoomScale. | ||
| 15 | +* [iOS] Call resetZoomScale while starting scan. | ||
| 16 | + Android camera is initialized with a zoom of 1x, whereas iOS is initialized with the minimum zoom value, which causes to select the ultra-wide camera unintentionally ([iOS] Impossible to focus and scan the QR code due to picking the wide back camera #554). | ||
| 17 | + Fixed this issue by calling resetZoomScale | ||
| 18 | +* [iOS] Remove zoom animation with ramp function to match Android behavior. | ||
| 19 | + | ||
| 5 | ## 3.2.0 | 20 | ## 3.2.0 |
| 6 | Improvements: | 21 | Improvements: |
| 7 | * [iOS] Updated GoogleMLKit/BarcodeScanning to 4.0.0 | 22 | * [iOS] Updated GoogleMLKit/BarcodeScanning to 4.0.0 |
| @@ -54,6 +54,12 @@ Ensure that you granted camera permission in XCode -> Signing & Capabilities: | @@ -54,6 +54,12 @@ Ensure that you granted camera permission in XCode -> Signing & Capabilities: | ||
| 54 | 54 | ||
| 55 | <img width="696" alt="Screenshot of XCode where Camera is checked" src="https://user-images.githubusercontent.com/24459435/193464115-d76f81d0-6355-4cb2-8bee-538e413a3ad0.png"> | 55 | <img width="696" alt="Screenshot of XCode where Camera is checked" src="https://user-images.githubusercontent.com/24459435/193464115-d76f81d0-6355-4cb2-8bee-538e413a3ad0.png"> |
| 56 | 56 | ||
| 57 | +## Web | ||
| 58 | +This package uses ZXing on web to read barcodes so it needs to be included in `index.html` as script. | ||
| 59 | +```html | ||
| 60 | +<script src="https://unpkg.com/@zxing/library@0.19.1" type="application/javascript"></script> | ||
| 61 | +``` | ||
| 62 | + | ||
| 57 | ## Usage | 63 | ## Usage |
| 58 | 64 | ||
| 59 | Import `package:mobile_scanner/mobile_scanner.dart`, and use the widget with or without the controller. | 65 | Import `package:mobile_scanner/mobile_scanner.dart`, and use the widget with or without the controller. |
| @@ -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.7.22' | 5 | + ext.kotlin_version = '1.8.22' |
| 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:8.0.0' | 12 | + classpath 'com.android.tools.build:gradle:8.0.2' |
| 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" |
| 14 | } | 14 | } |
| 15 | } | 15 | } |
| @@ -56,5 +56,6 @@ dependencies { | @@ -56,5 +56,6 @@ dependencies { | ||
| 56 | // implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.1.0' | 56 | // implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.1.0' |
| 57 | 57 | ||
| 58 | implementation 'androidx.camera:camera-camera2:1.2.2' | 58 | implementation 'androidx.camera:camera-camera2:1.2.2' |
| 59 | - implementation 'androidx.camera:camera-lifecycle:1.2.2' | 59 | + implementation 'androidx.camera:camera-lifecycle:1.2.3' |
| 60 | + implementation 'androidx.camera:camera-camera2:1.2.3' | ||
| 60 | } | 61 | } |
| 1 | buildscript { | 1 | buildscript { |
| 2 | - ext.kotlin_version = '1.7.22' | 2 | + ext.kotlin_version = '1.8.22' |
| 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:8.0.0' | 9 | + classpath 'com.android.tools.build:gradle:8.0.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 | } |
| @@ -26,6 +26,6 @@ subprojects { | @@ -26,6 +26,6 @@ subprojects { | ||
| 26 | project.evaluationDependsOn(':app') | 26 | project.evaluationDependsOn(':app') |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | -task clean(type: Delete) { | 29 | +tasks.register("clean", Delete) { |
| 30 | delete rootProject.buildDir | 30 | delete rootProject.buildDir |
| 31 | } | 31 | } |
| 1 | -#Tue Aug 23 15:51:00 CEST 2022 | 1 | +#Tue Jun 27 18:47:05 CEST 2023 |
| 2 | distributionBase=GRADLE_USER_HOME | 2 | distributionBase=GRADLE_USER_HOME |
| 3 | -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip | ||
| 4 | distributionPath=wrapper/dists | 3 | distributionPath=wrapper/dists |
| 5 | -zipStorePath=wrapper/dists | 4 | +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip |
| 6 | zipStoreBase=GRADLE_USER_HOME | 5 | zipStoreBase=GRADLE_USER_HOME |
| 6 | +zipStorePath=wrapper/dists |
| @@ -50,6 +50,7 @@ class _BarcodeListScannerWithControllerState | @@ -50,6 +50,7 @@ class _BarcodeListScannerWithControllerState | ||
| 50 | @override | 50 | @override |
| 51 | Widget build(BuildContext context) { | 51 | Widget build(BuildContext context) { |
| 52 | return Scaffold( | 52 | return Scaffold( |
| 53 | + appBar: AppBar(title: const Text('With ValueListenableBuilder')), | ||
| 53 | backgroundColor: Colors.black, | 54 | backgroundColor: Colors.black, |
| 54 | body: Builder( | 55 | body: Builder( |
| 55 | builder: (context) { | 56 | builder: (context) { |
| @@ -50,6 +50,7 @@ class _BarcodeScannerWithControllerState | @@ -50,6 +50,7 @@ class _BarcodeScannerWithControllerState | ||
| 50 | @override | 50 | @override |
| 51 | Widget build(BuildContext context) { | 51 | Widget build(BuildContext context) { |
| 52 | return Scaffold( | 52 | return Scaffold( |
| 53 | + appBar: AppBar(title: const Text('With controller')), | ||
| 53 | backgroundColor: Colors.black, | 54 | backgroundColor: Colors.black, |
| 54 | body: Builder( | 55 | body: Builder( |
| 55 | builder: (context) { | 56 | builder: (context) { |
| @@ -70,6 +70,7 @@ class _BarcodeScannerPageViewState extends State<BarcodeScannerPageView> | @@ -70,6 +70,7 @@ class _BarcodeScannerPageViewState extends State<BarcodeScannerPageView> | ||
| 70 | @override | 70 | @override |
| 71 | Widget build(BuildContext context) { | 71 | Widget build(BuildContext context) { |
| 72 | return Scaffold( | 72 | return Scaffold( |
| 73 | + appBar: AppBar(title: const Text('With PageView')), | ||
| 73 | backgroundColor: Colors.black, | 74 | backgroundColor: Colors.black, |
| 74 | body: PageView( | 75 | body: PageView( |
| 75 | children: [ | 76 | children: [ |
| @@ -52,6 +52,7 @@ class _BarcodeScannerReturningImageState | @@ -52,6 +52,7 @@ class _BarcodeScannerReturningImageState | ||
| 52 | @override | 52 | @override |
| 53 | Widget build(BuildContext context) { | 53 | Widget build(BuildContext context) { |
| 54 | return Scaffold( | 54 | return Scaffold( |
| 55 | + appBar: AppBar(title: const Text('Returning image')), | ||
| 55 | body: SafeArea( | 56 | body: SafeArea( |
| 56 | child: Column( | 57 | child: Column( |
| 57 | children: [ | 58 | children: [ |
| @@ -3,6 +3,8 @@ import 'dart:io'; | @@ -3,6 +3,8 @@ import 'dart:io'; | ||
| 3 | import 'package:flutter/material.dart'; | 3 | import 'package:flutter/material.dart'; |
| 4 | import 'package:mobile_scanner/mobile_scanner.dart'; | 4 | import 'package:mobile_scanner/mobile_scanner.dart'; |
| 5 | 5 | ||
| 6 | +import 'package:mobile_scanner_example/scanner_error_widget.dart'; | ||
| 7 | + | ||
| 6 | class BarcodeScannerWithScanWindow extends StatefulWidget { | 8 | class BarcodeScannerWithScanWindow extends StatefulWidget { |
| 7 | const BarcodeScannerWithScanWindow({Key? key}) : super(key: key); | 9 | const BarcodeScannerWithScanWindow({Key? key}) : super(key: key); |
| 8 | 10 | ||
| @@ -32,6 +34,7 @@ class _BarcodeScannerWithScanWindowState | @@ -32,6 +34,7 @@ class _BarcodeScannerWithScanWindowState | ||
| 32 | height: 200, | 34 | height: 200, |
| 33 | ); | 35 | ); |
| 34 | return Scaffold( | 36 | return Scaffold( |
| 37 | + appBar: AppBar(title: const Text('With Scan window')), | ||
| 35 | backgroundColor: Colors.black, | 38 | backgroundColor: Colors.black, |
| 36 | body: Builder( | 39 | body: Builder( |
| 37 | builder: (context) { | 40 | builder: (context) { |
| @@ -47,6 +50,9 @@ class _BarcodeScannerWithScanWindowState | @@ -47,6 +50,9 @@ class _BarcodeScannerWithScanWindowState | ||
| 47 | this.arguments = arguments; | 50 | this.arguments = arguments; |
| 48 | }); | 51 | }); |
| 49 | }, | 52 | }, |
| 53 | + errorBuilder: (context, error, child) { | ||
| 54 | + return ScannerErrorWidget(error: error); | ||
| 55 | + }, | ||
| 50 | onDetect: onDetect, | 56 | onDetect: onDetect, |
| 51 | ), | 57 | ), |
| 52 | if (barcode != null && | 58 | if (barcode != null && |
| @@ -18,6 +18,7 @@ class _BarcodeScannerWithoutControllerState | @@ -18,6 +18,7 @@ class _BarcodeScannerWithoutControllerState | ||
| 18 | @override | 18 | @override |
| 19 | Widget build(BuildContext context) { | 19 | Widget build(BuildContext context) { |
| 20 | return Scaffold( | 20 | return Scaffold( |
| 21 | + appBar: AppBar(title: const Text('Without controller')), | ||
| 21 | backgroundColor: Colors.black, | 22 | backgroundColor: Colors.black, |
| 22 | body: Builder( | 23 | body: Builder( |
| 23 | builder: (context) { | 24 | builder: (context) { |
| @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; | @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; | ||
| 2 | import 'package:image_picker/image_picker.dart'; | 2 | import 'package:image_picker/image_picker.dart'; |
| 3 | import 'package:mobile_scanner/mobile_scanner.dart'; | 3 | import 'package:mobile_scanner/mobile_scanner.dart'; |
| 4 | 4 | ||
| 5 | +import 'package:mobile_scanner_example/scanner_error_widget.dart'; | ||
| 6 | + | ||
| 5 | class BarcodeScannerWithZoom extends StatefulWidget { | 7 | class BarcodeScannerWithZoom extends StatefulWidget { |
| 6 | const BarcodeScannerWithZoom({Key? key}) : super(key: key); | 8 | const BarcodeScannerWithZoom({Key? key}) : super(key: key); |
| 7 | 9 | ||
| @@ -23,6 +25,7 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> | @@ -23,6 +25,7 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> | ||
| 23 | @override | 25 | @override |
| 24 | Widget build(BuildContext context) { | 26 | Widget build(BuildContext context) { |
| 25 | return Scaffold( | 27 | return Scaffold( |
| 28 | + appBar: AppBar(title: const Text('With zoom slider')), | ||
| 26 | backgroundColor: Colors.black, | 29 | backgroundColor: Colors.black, |
| 27 | body: Builder( | 30 | body: Builder( |
| 28 | builder: (context) { | 31 | builder: (context) { |
| @@ -31,6 +34,9 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> | @@ -31,6 +34,9 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> | ||
| 31 | MobileScanner( | 34 | MobileScanner( |
| 32 | controller: controller, | 35 | controller: controller, |
| 33 | fit: BoxFit.contain, | 36 | fit: BoxFit.contain, |
| 37 | + errorBuilder: (context, error, child) { | ||
| 38 | + return ScannerErrorWidget(error: error); | ||
| 39 | + }, | ||
| 34 | onDetect: (barcode) { | 40 | onDetect: (barcode) { |
| 35 | setState(() { | 41 | setState(() { |
| 36 | this.barcode = barcode; | 42 | this.barcode = barcode; |
| @@ -17,6 +17,9 @@ class ScannerErrorWidget extends StatelessWidget { | @@ -17,6 +17,9 @@ class ScannerErrorWidget extends StatelessWidget { | ||
| 17 | case MobileScannerErrorCode.permissionDenied: | 17 | case MobileScannerErrorCode.permissionDenied: |
| 18 | errorMessage = 'Permission denied'; | 18 | errorMessage = 'Permission denied'; |
| 19 | break; | 19 | break; |
| 20 | + case MobileScannerErrorCode.unsupported: | ||
| 21 | + errorMessage = 'Scanning is unsupported on this device'; | ||
| 22 | + break; | ||
| 20 | default: | 23 | default: |
| 21 | errorMessage = 'Generic Error'; | 24 | errorMessage = 'Generic Error'; |
| 22 | break; | 25 | break; |
| @@ -3,12 +3,12 @@ description: Demonstrates how to use the mobile_scanner plugin. | @@ -3,12 +3,12 @@ description: Demonstrates how to use the mobile_scanner plugin. | ||
| 3 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev | 3 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev |
| 4 | 4 | ||
| 5 | environment: | 5 | environment: |
| 6 | - sdk: ">=2.12.0 <3.0.0" | 6 | + sdk: ">=2.12.0 <4.0.0" |
| 7 | 7 | ||
| 8 | dependencies: | 8 | dependencies: |
| 9 | flutter: | 9 | flutter: |
| 10 | sdk: flutter | 10 | sdk: flutter |
| 11 | - image_picker: ^0.8.7 | 11 | + image_picker: ^1.0.0 |
| 12 | 12 | ||
| 13 | mobile_scanner: | 13 | mobile_scanner: |
| 14 | path: ../ | 14 | path: ../ |
example/web/icons/Icon-maskable-192.png
0 → 100644
5.46 KB
example/web/icons/Icon-maskable-512.png
0 → 100644
20.5 KB
| @@ -8,38 +8,53 @@ | @@ -8,38 +8,53 @@ | ||
| 8 | The path provided below has to start and end with a slash "/" in order for | 8 | The path provided below has to start and end with a slash "/" in order for |
| 9 | it to work correctly. | 9 | it to work correctly. |
| 10 | 10 | ||
| 11 | - Fore more details: | 11 | + For more details: |
| 12 | * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base | 12 | * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base |
| 13 | + | ||
| 14 | + This is a placeholder for base href that will be replaced by the value of | ||
| 15 | + the `--base-href` argument provided to `flutter build`. | ||
| 13 | --> | 16 | --> |
| 14 | - <base href="/"> | 17 | + <base href="$FLUTTER_BASE_HREF"> |
| 15 | 18 | ||
| 16 | <meta charset="UTF-8"> | 19 | <meta charset="UTF-8"> |
| 17 | <meta content="IE=Edge" http-equiv="X-UA-Compatible"> | 20 | <meta content="IE=Edge" http-equiv="X-UA-Compatible"> |
| 18 | - <meta name="description" content="A new Flutter project."> | 21 | + <meta name="description" content="Demonstrates how to use the mobile_scanner plugin."> |
| 19 | 22 | ||
| 20 | <!-- iOS meta tags & icons --> | 23 | <!-- iOS meta tags & icons --> |
| 21 | <meta name="apple-mobile-web-app-capable" content="yes"> | 24 | <meta name="apple-mobile-web-app-capable" content="yes"> |
| 22 | <meta name="apple-mobile-web-app-status-bar-style" content="black"> | 25 | <meta name="apple-mobile-web-app-status-bar-style" content="black"> |
| 23 | - <meta name="apple-mobile-web-app-title" content="example"> | 26 | + <meta name="apple-mobile-web-app-title" content="mobile_scanner_example"> |
| 24 | <link rel="apple-touch-icon" href="icons/Icon-192.png"> | 27 | <link rel="apple-touch-icon" href="icons/Icon-192.png"> |
| 25 | 28 | ||
| 26 | <!-- Favicon --> | 29 | <!-- Favicon --> |
| 27 | <link rel="icon" type="image/png" href="favicon.png"/> | 30 | <link rel="icon" type="image/png" href="favicon.png"/> |
| 28 | 31 | ||
| 29 | - <title>example</title> | 32 | + <title>mobile_scanner_example</title> |
| 30 | <link rel="manifest" href="manifest.json"> | 33 | <link rel="manifest" href="manifest.json"> |
| 34 | + | ||
| 35 | + <script> | ||
| 36 | + // The value below is injected by flutter build, do not touch. | ||
| 37 | + var serviceWorkerVersion = null; | ||
| 38 | + </script> | ||
| 39 | + <!-- This script adds the flutter initialization JS code --> | ||
| 40 | + <script src="flutter.js" defer></script> | ||
| 31 | </head> | 41 | </head> |
| 32 | <body> | 42 | <body> |
| 33 | - <!-- This script installs service_worker.js to provide PWA functionality to | ||
| 34 | - application. For more information, see: | ||
| 35 | - https://developers.google.com/web/fundamentals/primers/service-workers --> | 43 | + <script src="https://unpkg.com/@zxing/library@0.19.1" type="application/javascript"></script> |
| 36 | <script> | 44 | <script> |
| 37 | - if ('serviceWorker' in navigator) { | ||
| 38 | - window.addEventListener('flutter-first-frame', function () { | ||
| 39 | - navigator.serviceWorker.register('flutter_service_worker.js'); | 45 | + window.addEventListener('load', function(ev) { |
| 46 | + // Download main.dart.js | ||
| 47 | + _flutter.loader.loadEntrypoint({ | ||
| 48 | + serviceWorker: { | ||
| 49 | + serviceWorkerVersion: serviceWorkerVersion, | ||
| 50 | + }, | ||
| 51 | + onEntrypointLoaded: function(engineInitializer) { | ||
| 52 | + engineInitializer.initializeEngine().then(function(appRunner) { | ||
| 53 | + appRunner.runApp(); | ||
| 54 | + }); | ||
| 55 | + } | ||
| 40 | }); | 56 | }); |
| 41 | - } | 57 | + }); |
| 42 | </script> | 58 | </script> |
| 43 | - <script src="main.dart.js" type="application/javascript"></script> | ||
| 44 | </body> | 59 | </body> |
| 45 | </html> | 60 | </html> |
| 1 | { | 1 | { |
| 2 | - "name": "Mobile Scanner Example", | 2 | + "name": "mobile_scanner_example", |
| 3 | "short_name": "mobile_scanner_example", | 3 | "short_name": "mobile_scanner_example", |
| 4 | "start_url": ".", | 4 | "start_url": ".", |
| 5 | "display": "standalone", | 5 | "display": "standalone", |
| 6 | "background_color": "#0175C2", | 6 | "background_color": "#0175C2", |
| 7 | "theme_color": "#0175C2", | 7 | "theme_color": "#0175C2", |
| 8 | - "description": "A barcode and qr code scanner example.", | 8 | + "description": "Demonstrates how to use the mobile_scanner plugin.", |
| 9 | "orientation": "portrait-primary", | 9 | "orientation": "portrait-primary", |
| 10 | "prefer_related_applications": false, | 10 | "prefer_related_applications": false, |
| 11 | "icons": [ | 11 | "icons": [ |
| @@ -18,6 +18,18 @@ | @@ -18,6 +18,18 @@ | ||
| 18 | "src": "icons/Icon-512.png", | 18 | "src": "icons/Icon-512.png", |
| 19 | "sizes": "512x512", | 19 | "sizes": "512x512", |
| 20 | "type": "image/png" | 20 | "type": "image/png" |
| 21 | + }, | ||
| 22 | + { | ||
| 23 | + "src": "icons/Icon-maskable-192.png", | ||
| 24 | + "sizes": "192x192", | ||
| 25 | + "type": "image/png", | ||
| 26 | + "purpose": "maskable" | ||
| 27 | + }, | ||
| 28 | + { | ||
| 29 | + "src": "icons/Icon-maskable-512.png", | ||
| 30 | + "sizes": "512x512", | ||
| 31 | + "type": "image/png", | ||
| 32 | + "purpose": "maskable" | ||
| 21 | } | 33 | } |
| 22 | ] | 34 | ] |
| 23 | } | 35 | } |
| @@ -227,6 +227,8 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -227,6 +227,8 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 227 | for output in captureSession.outputs { | 227 | for output in captureSession.outputs { |
| 228 | captureSession.removeOutput(output) | 228 | captureSession.removeOutput(output) |
| 229 | } | 229 | } |
| 230 | + | ||
| 231 | + latestBuffer = nil | ||
| 230 | device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode)) | 232 | device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode)) |
| 231 | device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.videoZoomFactor)) | 233 | device.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.videoZoomFactor)) |
| 232 | registry?.unregisterTexture(textureId) | 234 | registry?.unregisterTexture(textureId) |
| @@ -8,7 +8,6 @@ import 'package:mobile_scanner/mobile_scanner_web.dart'; | @@ -8,7 +8,6 @@ import 'package:mobile_scanner/mobile_scanner_web.dart'; | ||
| 8 | import 'package:mobile_scanner/src/barcode_utility.dart'; | 8 | import 'package:mobile_scanner/src/barcode_utility.dart'; |
| 9 | import 'package:mobile_scanner/src/enums/camera_facing.dart'; | 9 | import 'package:mobile_scanner/src/enums/camera_facing.dart'; |
| 10 | import 'package:mobile_scanner/src/objects/barcode.dart'; | 10 | import 'package:mobile_scanner/src/objects/barcode.dart'; |
| 11 | -import 'package:mobile_scanner/src/web/utils.dart'; | ||
| 12 | 11 | ||
| 13 | /// This plugin is the web implementation of mobile_scanner. | 12 | /// This plugin is the web implementation of mobile_scanner. |
| 14 | /// It only supports QR codes. | 13 | /// It only supports QR codes. |
| @@ -26,8 +25,6 @@ class MobileScannerWebPlugin { | @@ -26,8 +25,6 @@ class MobileScannerWebPlugin { | ||
| 26 | ); | 25 | ); |
| 27 | final MobileScannerWebPlugin instance = MobileScannerWebPlugin(); | 26 | final MobileScannerWebPlugin instance = MobileScannerWebPlugin(); |
| 28 | 27 | ||
| 29 | - _jsLibrariesLoadingFuture = injectJSLibraries(barCodeReader.jsLibraries); | ||
| 30 | - | ||
| 31 | channel.setMethodCallHandler(instance.handleMethodCall); | 28 | channel.setMethodCallHandler(instance.handleMethodCall); |
| 32 | event.setController(instance.controller); | 29 | event.setController(instance.controller); |
| 33 | } | 30 | } |
| @@ -55,11 +52,8 @@ class MobileScannerWebPlugin { | @@ -55,11 +52,8 @@ class MobileScannerWebPlugin { | ||
| 55 | ZXingBarcodeReader(videoContainer: vidDiv); | 52 | ZXingBarcodeReader(videoContainer: vidDiv); |
| 56 | StreamSubscription? _barCodeStreamSubscription; | 53 | StreamSubscription? _barCodeStreamSubscription; |
| 57 | 54 | ||
| 58 | - static late Future _jsLibrariesLoadingFuture; | ||
| 59 | - | ||
| 60 | /// Handle incomming messages | 55 | /// Handle incomming messages |
| 61 | Future<dynamic> handleMethodCall(MethodCall call) async { | 56 | Future<dynamic> handleMethodCall(MethodCall call) async { |
| 62 | - await _jsLibrariesLoadingFuture; | ||
| 63 | switch (call.method) { | 57 | switch (call.method) { |
| 64 | case 'start': | 58 | case 'start': |
| 65 | return _start(call.arguments as Map); | 59 | return _start(call.arguments as Map); |
| @@ -67,6 +61,8 @@ class MobileScannerWebPlugin { | @@ -67,6 +61,8 @@ class MobileScannerWebPlugin { | ||
| 67 | return _torch(call.arguments); | 61 | return _torch(call.arguments); |
| 68 | case 'stop': | 62 | case 'stop': |
| 69 | return cancel(); | 63 | return cancel(); |
| 64 | + case 'updateScanWindow': | ||
| 65 | + return Future<void>.value(); | ||
| 70 | default: | 66 | default: |
| 71 | throw PlatformException( | 67 | throw PlatformException( |
| 72 | code: 'Unimplemented', | 68 | code: 'Unimplemented', |
| @@ -117,12 +113,14 @@ class MobileScannerWebPlugin { | @@ -117,12 +113,14 @@ class MobileScannerWebPlugin { | ||
| 117 | .map((e) => toFormat(e)) | 113 | .map((e) => toFormat(e)) |
| 118 | .toList(); | 114 | .toList(); |
| 119 | } | 115 | } |
| 116 | + | ||
| 120 | final Duration? detectionTimeout; | 117 | final Duration? detectionTimeout; |
| 121 | if (arguments.containsKey('timeout')) { | 118 | if (arguments.containsKey('timeout')) { |
| 122 | detectionTimeout = Duration(milliseconds: arguments['timeout'] as int); | 119 | detectionTimeout = Duration(milliseconds: arguments['timeout'] as int); |
| 123 | } else { | 120 | } else { |
| 124 | detectionTimeout = null; | 121 | detectionTimeout = null; |
| 125 | } | 122 | } |
| 123 | + | ||
| 126 | await barCodeReader.start( | 124 | await barCodeReader.start( |
| 127 | cameraFacing: cameraFacing, | 125 | cameraFacing: cameraFacing, |
| 128 | formats: formats, | 126 | formats: formats, |
| @@ -132,20 +130,31 @@ class MobileScannerWebPlugin { | @@ -132,20 +130,31 @@ class MobileScannerWebPlugin { | ||
| 132 | _barCodeStreamSubscription = | 130 | _barCodeStreamSubscription = |
| 133 | barCodeReader.detectBarcodeContinuously().listen((code) { | 131 | barCodeReader.detectBarcodeContinuously().listen((code) { |
| 134 | if (code != null) { | 132 | if (code != null) { |
| 133 | + final List<Offset>? corners = code.corners; | ||
| 134 | + | ||
| 135 | controller.add({ | 135 | controller.add({ |
| 136 | 'name': 'barcodeWeb', | 136 | 'name': 'barcodeWeb', |
| 137 | 'data': { | 137 | 'data': { |
| 138 | 'rawValue': code.rawValue, | 138 | 'rawValue': code.rawValue, |
| 139 | 'rawBytes': code.rawBytes, | 139 | 'rawBytes': code.rawBytes, |
| 140 | 'format': code.format.rawValue, | 140 | 'format': code.format.rawValue, |
| 141 | + 'displayValue': code.displayValue, | ||
| 142 | + 'type': code.type.index, | ||
| 143 | + if (corners != null && corners.isNotEmpty) | ||
| 144 | + 'corners': corners | ||
| 145 | + .map( | ||
| 146 | + (Offset c) => <Object?, Object?>{'x': c.dx, 'y': c.dy}, | ||
| 147 | + ) | ||
| 148 | + .toList(), | ||
| 141 | }, | 149 | }, |
| 142 | }); | 150 | }); |
| 143 | } | 151 | } |
| 144 | }); | 152 | }); |
| 153 | + | ||
| 145 | final hasTorch = await barCodeReader.hasTorch(); | 154 | final hasTorch = await barCodeReader.hasTorch(); |
| 146 | 155 | ||
| 147 | if (hasTorch && arguments.containsKey('torch')) { | 156 | if (hasTorch && arguments.containsKey('torch')) { |
| 148 | - barCodeReader.toggleTorch(enabled: arguments['torch'] as bool); | 157 | + await barCodeReader.toggleTorch(enabled: arguments['torch'] as bool); |
| 149 | } | 158 | } |
| 150 | 159 | ||
| 151 | return { | 160 | return { |
| @@ -154,8 +163,12 @@ class MobileScannerWebPlugin { | @@ -154,8 +163,12 @@ class MobileScannerWebPlugin { | ||
| 154 | 'videoHeight': barCodeReader.videoHeight, | 163 | 'videoHeight': barCodeReader.videoHeight, |
| 155 | 'torchable': hasTorch, | 164 | 'torchable': hasTorch, |
| 156 | }; | 165 | }; |
| 157 | - } catch (e) { | ||
| 158 | - throw PlatformException(code: 'MobileScannerWeb', message: '$e'); | 166 | + } catch (e, stackTrace) { |
| 167 | + throw PlatformException( | ||
| 168 | + code: 'MobileScannerWeb', | ||
| 169 | + message: '$e', | ||
| 170 | + details: stackTrace.toString(), | ||
| 171 | + ); | ||
| 159 | } | 172 | } |
| 160 | } | 173 | } |
| 161 | 174 |
| @@ -9,14 +9,16 @@ Size toSize(Map data) { | @@ -9,14 +9,16 @@ Size toSize(Map data) { | ||
| 9 | return Size(width, height); | 9 | return Size(width, height); |
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | -List<Offset>? toCorners(List? data) { | ||
| 13 | - if (data != null) { | ||
| 14 | - return List.unmodifiable( | ||
| 15 | - data.map((e) => Offset((e as Map)['x'] as double, e['y'] as double)), | ||
| 16 | - ); | ||
| 17 | - } else { | 12 | +List<Offset>? toCorners(List<Map<Object?, Object?>>? data) { |
| 13 | + if (data == null) { | ||
| 18 | return null; | 14 | return null; |
| 19 | } | 15 | } |
| 16 | + | ||
| 17 | + return List.unmodifiable( | ||
| 18 | + data.map((Map<Object?, Object?> e) { | ||
| 19 | + return Offset(e['x']! as double, e['y']! as double); | ||
| 20 | + }), | ||
| 21 | + ); | ||
| 20 | } | 22 | } |
| 21 | 23 | ||
| 22 | BarcodeFormat toFormat(int value) { | 24 | BarcodeFormat toFormat(int value) { |
| @@ -11,4 +11,7 @@ enum MobileScannerErrorCode { | @@ -11,4 +11,7 @@ enum MobileScannerErrorCode { | ||
| 11 | 11 | ||
| 12 | /// The permission to use the camera was denied. | 12 | /// The permission to use the camera was denied. |
| 13 | permissionDenied, | 13 | permissionDenied, |
| 14 | + | ||
| 15 | + /// Scanning is unsupported on the current device. | ||
| 16 | + unsupported, | ||
| 14 | } | 17 | } |
| @@ -132,7 +132,6 @@ class _MobileScannerState extends State<MobileScanner> | @@ -132,7 +132,6 @@ class _MobileScannerState extends State<MobileScanner> | ||
| 132 | widget.onStart?.call(arguments); | 132 | widget.onStart?.call(arguments); |
| 133 | widget.onScannerStarted?.call(arguments); | 133 | widget.onScannerStarted?.call(arguments); |
| 134 | }).catchError((error) { | 134 | }).catchError((error) { |
| 135 | - debugPrint('mobile_scanner: $error'); | ||
| 136 | if (mounted) { | 135 | if (mounted) { |
| 137 | setState(() { | 136 | setState(() { |
| 138 | _startException = error as MobileScannerException; | 137 | _startException = error as MobileScannerException; |
| @@ -247,47 +246,43 @@ class _MobileScannerState extends State<MobileScanner> | @@ -247,47 +246,43 @@ class _MobileScannerState extends State<MobileScanner> | ||
| 247 | 246 | ||
| 248 | @override | 247 | @override |
| 249 | Widget build(BuildContext context) { | 248 | Widget build(BuildContext context) { |
| 250 | - return LayoutBuilder( | ||
| 251 | - builder: (context, constraints) { | ||
| 252 | - return ValueListenableBuilder<MobileScannerArguments?>( | ||
| 253 | - valueListenable: _controller.startArguments, | ||
| 254 | - builder: (context, value, child) { | ||
| 255 | - if (value == null) { | ||
| 256 | - return _buildPlaceholderOrError(context, child); | ||
| 257 | - } | ||
| 258 | - | ||
| 259 | - if (widget.scanWindow != null && scanWindow == null) { | ||
| 260 | - scanWindow = calculateScanWindowRelativeToTextureInPercentage( | ||
| 261 | - widget.fit, | ||
| 262 | - widget.scanWindow!, | ||
| 263 | - value.size, | ||
| 264 | - Size(constraints.maxWidth, constraints.maxHeight), | ||
| 265 | - ); | 249 | + final Size size = MediaQuery.sizeOf(context); |
| 250 | + | ||
| 251 | + return ValueListenableBuilder<MobileScannerArguments?>( | ||
| 252 | + valueListenable: _controller.startArguments, | ||
| 253 | + builder: (context, value, child) { | ||
| 254 | + if (value == null) { | ||
| 255 | + return _buildPlaceholderOrError(context, child); | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + if (widget.scanWindow != null && scanWindow == null) { | ||
| 259 | + scanWindow = calculateScanWindowRelativeToTextureInPercentage( | ||
| 260 | + widget.fit, | ||
| 261 | + widget.scanWindow!, | ||
| 262 | + value.size, | ||
| 263 | + size, | ||
| 264 | + ); | ||
| 265 | + | ||
| 266 | + _controller.updateScanWindow(scanWindow); | ||
| 267 | + } | ||
| 266 | 268 | ||
| 267 | - _controller.updateScanWindow(scanWindow); | ||
| 268 | - } | ||
| 269 | - | ||
| 270 | - return ClipRect( | ||
| 271 | - child: LayoutBuilder( | ||
| 272 | - builder: (_, constraints) { | ||
| 273 | - return SizedBox.fromSize( | ||
| 274 | - size: constraints.biggest, | ||
| 275 | - child: FittedBox( | ||
| 276 | - fit: widget.fit, | ||
| 277 | - // alignment: Alignment.topCenter, | ||
| 278 | - child: SizedBox( | ||
| 279 | - width: value.size.width, | ||
| 280 | - height: value.size.height, | ||
| 281 | - child: kIsWeb | ||
| 282 | - ? HtmlElementView(viewType: value.webId!) | ||
| 283 | - : Texture(textureId: value.textureId!), | ||
| 284 | - ), | ||
| 285 | - ), | ||
| 286 | - ); | ||
| 287 | - }, | ||
| 288 | - ), | ||
| 289 | - ); | ||
| 290 | - }, | 269 | + return ClipRect( |
| 270 | + child: LayoutBuilder( | ||
| 271 | + builder: (_, constraints) { | ||
| 272 | + return SizedBox.fromSize( | ||
| 273 | + size: constraints.biggest, | ||
| 274 | + child: FittedBox( | ||
| 275 | + fit: widget.fit, | ||
| 276 | + child: SizedBox.fromSize( | ||
| 277 | + size: value.size, | ||
| 278 | + child: kIsWeb | ||
| 279 | + ? HtmlElementView(viewType: value.webId!) | ||
| 280 | + : Texture(textureId: value.textureId!), | ||
| 281 | + ), | ||
| 282 | + ), | ||
| 283 | + ); | ||
| 284 | + }, | ||
| 285 | + ), | ||
| 291 | ); | 286 | ); |
| 292 | }, | 287 | }, |
| 293 | ); | 288 | ); |
| @@ -207,9 +207,21 @@ class MobileScannerController { | @@ -207,9 +207,21 @@ class MobileScannerController { | ||
| 207 | } on PlatformException catch (error) { | 207 | } on PlatformException catch (error) { |
| 208 | MobileScannerErrorCode errorCode = MobileScannerErrorCode.genericError; | 208 | MobileScannerErrorCode errorCode = MobileScannerErrorCode.genericError; |
| 209 | 209 | ||
| 210 | - if (error.code == "MobileScannerWeb") { | ||
| 211 | - errorCode = MobileScannerErrorCode.permissionDenied; | 210 | + final String? errorMessage = error.message; |
| 211 | + | ||
| 212 | + if (kIsWeb) { | ||
| 213 | + if (errorMessage == null) { | ||
| 214 | + errorCode = MobileScannerErrorCode.genericError; | ||
| 215 | + } else if (errorMessage.contains('NotFoundError') || | ||
| 216 | + errorMessage.contains('NotSupportedError')) { | ||
| 217 | + errorCode = MobileScannerErrorCode.unsupported; | ||
| 218 | + } else if (errorMessage.contains('NotAllowedError')) { | ||
| 219 | + errorCode = MobileScannerErrorCode.permissionDenied; | ||
| 220 | + } else { | ||
| 221 | + errorCode = MobileScannerErrorCode.genericError; | ||
| 222 | + } | ||
| 212 | } | 223 | } |
| 224 | + | ||
| 213 | isStarting = false; | 225 | isStarting = false; |
| 214 | 226 | ||
| 215 | throw MobileScannerException( | 227 | throw MobileScannerException( |
| @@ -388,6 +400,10 @@ class MobileScannerController { | @@ -388,6 +400,10 @@ class MobileScannerController { | ||
| 388 | rawValue: barcode['rawValue'] as String?, | 400 | rawValue: barcode['rawValue'] as String?, |
| 389 | rawBytes: barcode['rawBytes'] as Uint8List?, | 401 | rawBytes: barcode['rawBytes'] as Uint8List?, |
| 390 | format: toFormat(barcode['format'] as int), | 402 | format: toFormat(barcode['format'] as int), |
| 403 | + corners: toCorners( | ||
| 404 | + (barcode['corners'] as List<Object?>? ?? []) | ||
| 405 | + .cast<Map<Object?, Object?>>(), | ||
| 406 | + ), | ||
| 391 | ), | 407 | ), |
| 392 | ], | 408 | ], |
| 393 | ), | 409 | ), |
| @@ -92,7 +92,9 @@ class Barcode { | @@ -92,7 +92,9 @@ class Barcode { | ||
| 92 | 92 | ||
| 93 | /// Create a [Barcode] from native data. | 93 | /// Create a [Barcode] from native data. |
| 94 | Barcode.fromNative(Map data) | 94 | Barcode.fromNative(Map data) |
| 95 | - : corners = toCorners(data['corners'] as List?), | 95 | + : corners = toCorners( |
| 96 | + (data['corners'] as List?)?.cast<Map<Object?, Object?>>(), | ||
| 97 | + ), | ||
| 96 | format = toFormat(data['format'] as int), | 98 | format = toFormat(data['format'] as int), |
| 97 | rawBytes = data['rawBytes'] as Uint8List?, | 99 | rawBytes = data['rawBytes'] as Uint8List?, |
| 98 | rawValue = data['rawValue'] as String?, | 100 | rawValue = data['rawValue'] as String?, |
| @@ -201,18 +203,20 @@ class ContactInfo { | @@ -201,18 +203,20 @@ class ContactInfo { | ||
| 201 | /// Create a [ContactInfo] from native data. | 203 | /// Create a [ContactInfo] from native data. |
| 202 | ContactInfo.fromNative(Map data) | 204 | ContactInfo.fromNative(Map data) |
| 203 | : addresses = List.unmodifiable( | 205 | : addresses = List.unmodifiable( |
| 204 | - (data['addresses'] as List).map((e) => Address.fromNative(e as Map)), | 206 | + (data['addresses'] as List? ?? []) |
| 207 | + .cast<Map>() | ||
| 208 | + .map(Address.fromNative), | ||
| 205 | ), | 209 | ), |
| 206 | emails = List.unmodifiable( | 210 | emails = List.unmodifiable( |
| 207 | - (data['emails'] as List).map((e) => Email.fromNative(e as Map)), | 211 | + (data['emails'] as List? ?? []).cast<Map>().map(Email.fromNative), |
| 208 | ), | 212 | ), |
| 209 | name = toName(data['name'] as Map?), | 213 | name = toName(data['name'] as Map?), |
| 210 | organization = data['organization'] as String?, | 214 | organization = data['organization'] as String?, |
| 211 | phones = List.unmodifiable( | 215 | phones = List.unmodifiable( |
| 212 | - (data['phones'] as List).map((e) => Phone.fromNative(e as Map)), | 216 | + (data['phones'] as List? ?? []).cast<Map>().map(Phone.fromNative), |
| 213 | ), | 217 | ), |
| 214 | title = data['title'] as String?, | 218 | title = data['title'] as String?, |
| 215 | - urls = List.unmodifiable(data['urls'] as List); | 219 | + urls = List.unmodifiable((data['urls'] as List? ?? []).cast<String>()); |
| 216 | } | 220 | } |
| 217 | 221 | ||
| 218 | /// An address. | 222 | /// An address. |
| @@ -227,7 +231,9 @@ class Address { | @@ -227,7 +231,9 @@ class Address { | ||
| 227 | 231 | ||
| 228 | /// Create a [Address] from native data. | 232 | /// Create a [Address] from native data. |
| 229 | Address.fromNative(Map data) | 233 | Address.fromNative(Map data) |
| 230 | - : addressLines = List.unmodifiable(data['addressLines'] as List), | 234 | + : addressLines = List.unmodifiable( |
| 235 | + (data['addressLines'] as List? ?? []).cast<String>(), | ||
| 236 | + ), | ||
| 231 | type = AddressType.values[data['type'] as int]; | 237 | type = AddressType.values[data['type'] as int]; |
| 232 | } | 238 | } |
| 233 | 239 |
| @@ -39,9 +39,6 @@ abstract class WebBarcodeReaderBase { | @@ -39,9 +39,6 @@ abstract class WebBarcodeReaderBase { | ||
| 39 | int get videoWidth; | 39 | int get videoWidth; |
| 40 | int get videoHeight; | 40 | int get videoHeight; |
| 41 | 41 | ||
| 42 | - /// JS libraries to be injected into html page. | ||
| 43 | - List<JsLibrary> get jsLibraries; | ||
| 44 | - | ||
| 45 | /// Starts streaming video | 42 | /// Starts streaming video |
| 46 | Future<void> start({ | 43 | Future<void> start({ |
| 47 | required CameraFacing cameraFacing, | 44 | required CameraFacing cameraFacing, |
| @@ -128,10 +125,8 @@ mixin InternalTorchDetection on InternalStreamCreation { | @@ -128,10 +125,8 @@ mixin InternalTorchDetection on InternalStreamCreation { | ||
| 128 | final photoCapabilities = await promiseToFuture<PhotoCapabilities>( | 125 | final photoCapabilities = await promiseToFuture<PhotoCapabilities>( |
| 129 | imageCapture.getPhotoCapabilities(), | 126 | imageCapture.getPhotoCapabilities(), |
| 130 | ); | 127 | ); |
| 131 | - final fillLightMode = photoCapabilities.fillLightMode; | ||
| 132 | - if (fillLightMode != null) { | ||
| 133 | - return fillLightMode; | ||
| 134 | - } | 128 | + |
| 129 | + return photoCapabilities.fillLightMode; | ||
| 135 | } | 130 | } |
| 136 | } catch (e) { | 131 | } catch (e) { |
| 137 | // ImageCapture is not supported by some browsers: | 132 | // ImageCapture is not supported by some browsers: |
| @@ -165,9 +160,16 @@ class Promise<T> {} | @@ -165,9 +160,16 @@ class Promise<T> {} | ||
| 165 | 160 | ||
| 166 | @JS() | 161 | @JS() |
| 167 | @anonymous | 162 | @anonymous |
| 168 | -class PhotoCapabilities { | 163 | +@staticInterop |
| 164 | +class PhotoCapabilities {} | ||
| 165 | + | ||
| 166 | +extension PhotoCapabilitiesExtension on PhotoCapabilities { | ||
| 167 | + @JS('fillLightMode') | ||
| 168 | + external List<dynamic>? get _fillLightMode; | ||
| 169 | + | ||
| 169 | /// Returns an array of available fill light options. Options include auto, off, or flash. | 170 | /// Returns an array of available fill light options. Options include auto, off, or flash. |
| 170 | - external List<String>? get fillLightMode; | 171 | + List<String> get fillLightMode => |
| 172 | + _fillLightMode?.cast<String>() ?? <String>[]; | ||
| 171 | } | 173 | } |
| 172 | 174 | ||
| 173 | @JS('ImageCapture') | 175 | @JS('ImageCapture') |
| @@ -20,12 +20,6 @@ class Code { | @@ -20,12 +20,6 @@ class Code { | ||
| 20 | external Uint8ClampedList get binaryData; | 20 | external Uint8ClampedList get binaryData; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | -const jsqrLibrary = JsLibrary( | ||
| 24 | - contextName: 'jsQR', | ||
| 25 | - url: 'https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js', | ||
| 26 | - usesRequireJs: true, | ||
| 27 | -); | ||
| 28 | - | ||
| 29 | /// Barcode reader that uses jsQR library. | 23 | /// Barcode reader that uses jsQR library. |
| 30 | /// jsQR supports only QR codes format. | 24 | /// jsQR supports only QR codes format. |
| 31 | class JsQrCodeReader extends WebBarcodeReaderBase | 25 | class JsQrCodeReader extends WebBarcodeReaderBase |
| @@ -36,9 +30,6 @@ class JsQrCodeReader extends WebBarcodeReaderBase | @@ -36,9 +30,6 @@ class JsQrCodeReader extends WebBarcodeReaderBase | ||
| 36 | bool get isStarted => localMediaStream != null; | 30 | bool get isStarted => localMediaStream != null; |
| 37 | 31 | ||
| 38 | @override | 32 | @override |
| 39 | - List<JsLibrary> get jsLibraries => [jsqrLibrary]; | ||
| 40 | - | ||
| 41 | - @override | ||
| 42 | Future<void> start({ | 33 | Future<void> start({ |
| 43 | required CameraFacing cameraFacing, | 34 | required CameraFacing cameraFacing, |
| 44 | List<BarcodeFormat>? formats, | 35 | List<BarcodeFormat>? formats, |
lib/src/web/utils.dart
deleted
100644 → 0
| 1 | -import 'dart:async'; | ||
| 2 | -import 'dart:html' as html; | ||
| 3 | -import 'dart:js' show JsObject, context; | ||
| 4 | - | ||
| 5 | -import 'package:mobile_scanner/src/web/base.dart'; | ||
| 6 | - | ||
| 7 | -Future<void> loadScript(JsLibrary library) async { | ||
| 8 | - dynamic amd; | ||
| 9 | - dynamic define; | ||
| 10 | - // ignore: avoid_dynamic_calls | ||
| 11 | - if (library.usesRequireJs && context['define']?['amd'] != null) { | ||
| 12 | - // In dev, requireJs is loaded in. Disable it here. | ||
| 13 | - // see https://github.com/dart-lang/sdk/issues/33979 | ||
| 14 | - define = JsObject.fromBrowserObject(context['define'] as Object); | ||
| 15 | - // ignore: avoid_dynamic_calls | ||
| 16 | - amd = define['amd']; | ||
| 17 | - // ignore: avoid_dynamic_calls | ||
| 18 | - define['amd'] = false; | ||
| 19 | - } | ||
| 20 | - try { | ||
| 21 | - await loadScriptUsingScriptTag(library.url); | ||
| 22 | - } finally { | ||
| 23 | - if (amd != null) { | ||
| 24 | - // Re-enable requireJs | ||
| 25 | - // ignore: avoid_dynamic_calls | ||
| 26 | - define['amd'] = amd; | ||
| 27 | - } | ||
| 28 | - } | ||
| 29 | -} | ||
| 30 | - | ||
| 31 | -Future<void> loadScriptUsingScriptTag(String url) { | ||
| 32 | - final script = html.ScriptElement() | ||
| 33 | - ..async = true | ||
| 34 | - ..defer = false | ||
| 35 | - ..crossOrigin = 'anonymous' | ||
| 36 | - ..type = 'text/javascript' | ||
| 37 | - // ignore: unsafe_html | ||
| 38 | - ..src = url; | ||
| 39 | - | ||
| 40 | - html.document.head!.append(script); | ||
| 41 | - | ||
| 42 | - return script.onLoad.first; | ||
| 43 | -} | ||
| 44 | - | ||
| 45 | -/// Injects JS [libraries] | ||
| 46 | -/// | ||
| 47 | -/// Returns a [Future] that resolves when all of the `script` tags `onLoad` events trigger. | ||
| 48 | -Future<void> injectJSLibraries(List<JsLibrary> libraries) { | ||
| 49 | - final List<Future<void>> loading = []; | ||
| 50 | - | ||
| 51 | - for (final library in libraries) { | ||
| 52 | - final future = loadScript(library); | ||
| 53 | - loading.add(future); | ||
| 54 | - } | ||
| 55 | - | ||
| 56 | - return Future.wait(loading); | ||
| 57 | -} |
| 1 | import 'dart:async'; | 1 | import 'dart:async'; |
| 2 | import 'dart:html'; | 2 | import 'dart:html'; |
| 3 | import 'dart:typed_data'; | 3 | import 'dart:typed_data'; |
| 4 | +import 'dart:ui'; | ||
| 4 | 5 | ||
| 5 | import 'package:js/js.dart'; | 6 | import 'package:js/js.dart'; |
| 6 | import 'package:mobile_scanner/src/enums/camera_facing.dart'; | 7 | import 'package:mobile_scanner/src/enums/camera_facing.dart'; |
| @@ -19,6 +20,16 @@ class JsZXingBrowserMultiFormatReader { | @@ -19,6 +20,16 @@ class JsZXingBrowserMultiFormatReader { | ||
| 19 | 20 | ||
| 20 | @JS() | 21 | @JS() |
| 21 | @anonymous | 22 | @anonymous |
| 23 | +abstract class ResultPoint { | ||
| 24 | + /// The x coordinate of the point. | ||
| 25 | + external double get x; | ||
| 26 | + | ||
| 27 | + /// The y coordinate of the point. | ||
| 28 | + external double get y; | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +@JS() | ||
| 32 | +@anonymous | ||
| 22 | abstract class Result { | 33 | abstract class Result { |
| 23 | /// raw text encoded by the barcode | 34 | /// raw text encoded by the barcode |
| 24 | external String get text; | 35 | external String get text; |
| @@ -28,15 +39,24 @@ abstract class Result { | @@ -28,15 +39,24 @@ abstract class Result { | ||
| 28 | 39 | ||
| 29 | /// Representing the format of the barcode that was decoded | 40 | /// Representing the format of the barcode that was decoded |
| 30 | external int? format; | 41 | external int? format; |
| 42 | + | ||
| 43 | + /// Returns the result points of the barcode. These points represent the corners of the barcode. | ||
| 44 | + external List<Object?> get resultPoints; | ||
| 31 | } | 45 | } |
| 32 | 46 | ||
| 33 | extension ResultExt on Result { | 47 | extension ResultExt on Result { |
| 34 | Barcode toBarcode() { | 48 | Barcode toBarcode() { |
| 49 | + final corners = resultPoints | ||
| 50 | + .cast<ResultPoint>() | ||
| 51 | + .map((ResultPoint rp) => Offset(rp.x, rp.y)) | ||
| 52 | + .toList(); | ||
| 53 | + | ||
| 35 | final rawBytes = this.rawBytes; | 54 | final rawBytes = this.rawBytes; |
| 36 | return Barcode( | 55 | return Barcode( |
| 37 | rawValue: text, | 56 | rawValue: text, |
| 38 | rawBytes: rawBytes != null ? Uint8List.fromList(rawBytes) : null, | 57 | rawBytes: rawBytes != null ? Uint8List.fromList(rawBytes) : null, |
| 39 | format: barcodeFormat, | 58 | format: barcodeFormat, |
| 59 | + corners: corners, | ||
| 40 | ); | 60 | ); |
| 41 | } | 61 | } |
| 42 | 62 | ||
| @@ -168,12 +188,6 @@ extension JsZXingBrowserMultiFormatReaderExt | @@ -168,12 +188,6 @@ extension JsZXingBrowserMultiFormatReaderExt | ||
| 168 | external MediaStream? stream; | 188 | external MediaStream? stream; |
| 169 | } | 189 | } |
| 170 | 190 | ||
| 171 | -const zxingJsLibrary = JsLibrary( | ||
| 172 | - contextName: 'ZXing', | ||
| 173 | - url: 'https://unpkg.com/@zxing/library@0.19.1', | ||
| 174 | - usesRequireJs: true, | ||
| 175 | -); | ||
| 176 | - | ||
| 177 | /// Barcode reader that uses zxing-js library. | 191 | /// Barcode reader that uses zxing-js library. |
| 178 | class ZXingBarcodeReader extends WebBarcodeReaderBase | 192 | class ZXingBarcodeReader extends WebBarcodeReaderBase |
| 179 | with InternalStreamCreation, InternalTorchDetection { | 193 | with InternalStreamCreation, InternalTorchDetection { |
| @@ -185,9 +199,6 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase | @@ -185,9 +199,6 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase | ||
| 185 | bool get isStarted => localMediaStream != null; | 199 | bool get isStarted => localMediaStream != null; |
| 186 | 200 | ||
| 187 | @override | 201 | @override |
| 188 | - List<JsLibrary> get jsLibraries => [zxingJsLibrary]; | ||
| 189 | - | ||
| 190 | - @override | ||
| 191 | Future<void> start({ | 202 | Future<void> start({ |
| 192 | required CameraFacing cameraFacing, | 203 | required CameraFacing cameraFacing, |
| 193 | List<BarcodeFormat>? formats, | 204 | List<BarcodeFormat>? formats, |
| 1 | name: mobile_scanner | 1 | name: mobile_scanner |
| 2 | description: A universal barcode and QR code scanner for Flutter based on MLKit. Uses CameraX on Android, AVFoundation on iOS and Apple Vision & AVFoundation on macOS. | 2 | description: A universal barcode and QR code scanner for Flutter based on MLKit. Uses CameraX on Android, AVFoundation on iOS and Apple Vision & AVFoundation on macOS. |
| 3 | -version: 3.2.1 | 3 | +version: 3.3.0 |
| 4 | repository: https://github.com/juliansteenbakker/mobile_scanner | 4 | repository: https://github.com/juliansteenbakker/mobile_scanner |
| 5 | 5 | ||
| 6 | environment: | 6 | environment: |
| 7 | - sdk: ">=2.17.0 <3.0.0" | 7 | + sdk: ">=2.17.0 <4.0.0" |
| 8 | flutter: ">=3.0.0" | 8 | flutter: ">=3.0.0" |
| 9 | 9 | ||
| 10 | dependencies: | 10 | dependencies: |
| @@ -14,7 +14,6 @@ dependencies: | @@ -14,7 +14,6 @@ dependencies: | ||
| 14 | sdk: flutter | 14 | sdk: flutter |
| 15 | js: ^0.6.3 | 15 | js: ^0.6.3 |
| 16 | 16 | ||
| 17 | - | ||
| 18 | dev_dependencies: | 17 | dev_dependencies: |
| 19 | flutter_test: | 18 | flutter_test: |
| 20 | sdk: flutter | 19 | sdk: flutter |
-
Please register or login to post a comment