diff --git a/.github/ISSUE_TEMPLATE/blank_issue.yaml b/.github/ISSUE_TEMPLATE/blank_issue.yaml
new file mode 100644
index 0000000..a12c58f
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/blank_issue.yaml
@@ -0,0 +1,27 @@
+name: "Other issue"
+description: "Open an issue, that is neither a bug or a feature/enhancement request"
+labels: ["other"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ # ⚠️ PLEASE KEEP IN MIND THAT GITHUB IS PUBLIC! DON'T SHARE ANY SENSITIVE OR INTERNAL INFORMATION! ⚠️
+ ## IF YOU POST YOUR CONFIGURATION REMOVE **AT LEAST** `serial-numbers`,`client_ids`,`client_secrets` and `urls`
+ - type: checkboxes
+ id: checklist
+ attributes:
+ label: "Issue checklist"
+ description: "To open an issue, all conditions below **need** to be met."
+ options:
+ - label: "This is **not** a bug or a feature/enhancement request."
+ required: true
+ - label: "I searched through the GitHub issues and this issue has not been opened before."
+ required: true
+ - type: textarea
+ id: text
+ attributes:
+ label: "Issue"
+ description: |
+ Any text you want, but please be kind :)
+ validations:
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
new file mode 100644
index 0000000..ca20e1a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -0,0 +1,91 @@
+name: "Bug report: mtrust-barcode-kit"
+description: "Report a bug in mtrust-barcode-kit"
+title: "[Bug]: "
+labels: ["bug"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ # ⚠️ PLEASE KEEP IN MIND THAT GITHUB IS PUBLIC! DON'T SHARE ANY SENSITIVE OR INTERNAL INFORMATION! ⚠️
+ ## IF YOU POST YOUR CONFIGURATION REMOVE **AT LEAST** `serial-numbers`,`client_ids`,`client_secrets` and `urls`
+ - type: checkboxes
+ id: checklist
+ attributes:
+ label: "Issue checklist"
+ description: "To open an issue, all conditions below **need** to be met."
+ options:
+ - label: "This is a **bug** in mtrust-barcode-kit and not a bug in another repository. It is also not an enhancement/feature request"
+ required: true
+ - label: "I searched through the GitHub issues and this issue has not been opened before."
+ required: true
+ - label: "I use the latest version of mtrust-barcode-kit and don't use an unsupported flutter, dart or pub version."
+ required: true
+ - label: "`flutter doctor` reports no issues."
+ required: true
+ - type: textarea
+ id: bug-description
+ attributes:
+ label: Description of the bug
+ description: |
+ Concise description of the bug.
+ Please provide as much info as possible, so we can help you better. Unfortunately we can't read minds (yet).
+ placeholder: |
+ For example:
+ When i use [...], the function [...] fails with this error [...]. It works when I use [...].
+ I provided a code snippet below, for you to reproduce it.
+ validations:
+ required: true
+ - type: textarea
+ id: bug-reproduce
+ attributes:
+ label: Steps to reproduce this bug.
+ description: |
+ To debug your issue, we need to know how we can reproduce this error on our machines.
+ This way we can try to solve the issue faster.
+ validations:
+ required: true
+ - type: textarea
+ id: bug-logs
+ attributes:
+ label: Log output
+ description: |
+ If there were warnings or errors during your build
+ or any other logs file that may help us, then please
+ paste them here.
+ > Please note that this textarea will automatically get formatted as `shell` for better readability.
+ - type: textarea
+ id: bug-context
+ attributes:
+ label: Additional context
+ description: |
+ Additional context that may help us, e.g. specific config options you set. pub_dev version, dart version, device serial number, device firmware version... Or anything else that doesn't fit any other category above.
+ > NOTE: Config options like `jwt`,`client_id`,`client_secret`,... which contain sensitive information, should **never** be posted!
+ placeholder: |
+ For example:
+ mtrust-barcode-kit version, SEC-000123 v1.0.0...
+ - type: dropdown
+ id: bug-os
+ attributes:
+ label: Operating System
+ multiple: false
+ options:
+ - Windows
+ - WSL
+ - Linux
+ - MacOS
+ - iOS
+ - Android
+ - Web
+ - Other
+ validations:
+ required: true
+ - type: input
+ id: bug-flutter-version
+ attributes:
+ label: Your flutter version
+ description: |
+ The flutter version you use to build mtrust-barcode-kit.
+ You can use the command `flutter --version` to find out your version.
+ placeholder: "e.g. Flutter 3.24.3"
+ validations:
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml
new file mode 100644
index 0000000..3ba13e0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yaml
@@ -0,0 +1 @@
+blank_issues_enabled: false
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml
new file mode 100644
index 0000000..9bab359
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yaml
@@ -0,0 +1,67 @@
+name: "Feature request"
+description: "Request a feature in mtrust-barcode-kit"
+title: "[Feature Request]: "
+labels: ["feature-request"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ # ⚠️ PLEASE KEEP IN MIND THAT GITHUB IS PUBLIC! DON'T SHARE ANY SENSITIVE OR INTERNAL INFORMATION! ⚠️
+ ## IF YOU POST YOUR CONFIGURATION REMOVE **AT LEAST** `serial-numbers`,`client_ids`,`client_secrets` and `urls`
+ - type: checkboxes
+ id: checklist
+ attributes:
+ label: "Issue checklist"
+ description: "To open an issue, all conditions below **need** to be met."
+ options:
+ - label: "This is a **feature request**/**enhancement**. And not a bug."
+ required: true
+ - label: "I searched through the GitHub issues and this feature/enhancement has not been requested before."
+ required: true
+ - label: "I use the latest version of mtrust-barcode-kit and don't use an unsupported flutter, dart or pub version."
+ required: true
+ - label: "Others could also benefit from this feature or enhancement and it is **not** a very specific use case."
+ required: true
+ - type: textarea
+ id: feature-request-use-case
+ attributes:
+ label: Feature use case
+ description: |
+ Describe the use case for the feature.
+ Why do you want this feature?
+ What problem does this solve? (If any)
+ placeholder: |
+ These are some examples (You don't need to use them, but as long as you include precise information, we are happy):
+ When people want to [...] they currently need to use [...].
+ validations:
+ required: true
+ - type: textarea
+ id: feature-request-description
+ attributes:
+ label: Description of the Feature
+ description: |
+ Please provide a short and concise description of the feature.
+ What do you want the feature to do?
+ If you already have ideas **how** it could be implemented, describe them too.
+ placeholder: |
+ These are some examples (You don't need to use them, but as long as you include precise information, we are happy):
+ The feature should behave like [...].
+ We would expect that [...].
+
+ This enhancement extends feature [...] with [...].
+ Currently the feature only [...]
+ validations:
+ required: true
+ - type: textarea
+ id: feature-request-alternatives
+ attributes:
+ label: Alternatives you considered
+ description: |
+ Are there alternatives, that you considered?
+ Could this be achieved by doing something else?
+ Provide details! (if any)
+ - type: textarea
+ id: feature-request-context
+ attributes:
+ label: Additional Context
+ description: Context that may help us, but doesn't fit in any other categories.
diff --git a/.github/pull_request_template.yaml b/.github/pull_request_template.yaml
new file mode 100644
index 0000000..c1c6d79
--- /dev/null
+++ b/.github/pull_request_template.yaml
@@ -0,0 +1,9 @@
+# Summary
+
+
+
+# Checklist
+
+- [ ] You agree with our [CLA](https://gist.githubusercontent.com/emdgroup-admin/16cc45ea4315c2ef29eb9d9afc36fcf5/raw/abb9c91f15278a62b9ac3e66144bcd27fa9485c2/CLA.md)
+- [ ] Included tests (or is not applicable).
+- [ ] Updated documentation (or is not applicable).
\ No newline at end of file
diff --git a/.github/workflows/build_dev.yaml b/.github/workflows/build_dev.yaml
new file mode 100644
index 0000000..887f9b8
--- /dev/null
+++ b/.github/workflows/build_dev.yaml
@@ -0,0 +1,98 @@
+name: Dev - 🚧 build package
+
+on:
+ push:
+ branches:
+ - dev
+
+jobs:
+ version:
+ name: Version & Build
+ runs-on: macos-latest
+ environment: release
+ permissions:
+ contents: write
+
+ outputs:
+ version: ${{ steps.get_new_version.outputs.result}}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ token: ${{secrets.ELEVATED_TOKEN}}
+
+ - name: 📇 Configure git
+ run: |
+ git fetch --prune --unshallow
+ git config --global user.name "GitHub Actions"
+ git config --global user.email "gh-actions@merckgroup.com"
+ shell: bash
+
+ # Retrieve the new version
+ - name: 🔂 Run standard-version
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const {execSync} = require('child_process');
+ execSync('npx standard-version --skip.tag --prerelease', {stdio: 'inherit'});
+
+ # Retrieve the new version
+ - name: ⏎ Get new version
+ uses: actions/github-script@v7
+ id: get_new_version
+ with:
+ result-encoding: string
+ script: |
+ const fs = require('fs');
+ const package = JSON.parse(fs.readFileSync('package.json', 'utf8'));
+ return package.version;
+
+ - name: Print new version
+ run: echo ${{ steps.get_new_version.outputs.result}}
+
+ # Bump the pubspec.yaml file
+ - name: ⬆️ Bump pubspec.yaml
+ uses: emdgroup/mtrust-urp/.github/actions/update-pubspec@main
+ with:
+ version: ${{ steps.get_new_version.outputs.result }}
+ directory: .
+
+ - name: 📝 Update version in readme
+ uses: emdgroup/mtrust-urp/.github/actions/update-pubspec-readme-version@main
+ with:
+ directory: .
+
+ - name: Setup Dart
+ uses: dart-lang/setup-dart@v1
+
+ - name: Prepare Flutter
+ uses: emdgroup/mtrust-urp/.github/actions/prepare-flutter@main
+ with:
+ directory: "."
+
+ - name: Validate Flutter
+ uses: emdgroup/mtrust-urp/.github/actions/validate-flutter@main
+ with:
+ directory: "."
+ is_package: true
+
+ - name: Check licenses
+ uses: emdgroup/mtrust-urp/.github/actions/check-dart-licenses@main
+ with:
+ directory: "."
+
+ # We first commit with proper message and add an empty commit to keep the files history clean
+ - name: Update repo versions
+ run: |
+ git add .
+ git commit -m "chore(release): ${{ steps.get_new_version.outputs.result }}"
+ git commit --allow-empty -m "chore(release): ${{ steps.get_new_version.outputs.result }} [skip ci]"
+ git push origin dev
+
+ # For this part it is important to not push a commit with [skip ci] before the tag release
+ - name: Push tag for pub.dev
+ run: |
+ git commit --allow-empty -m "chore(release): ${{ steps.get_new_version.outputs.result }}"
+ git tag -a v${{ steps.get_new_version.outputs.result }} -m "Pub.dev version ${{ steps.get_new_version.outputs.result }}"
+ git push origin v${{ steps.get_new_version.outputs.result }}
diff --git a/.github/workflows/build_main.yaml b/.github/workflows/build_main.yaml
new file mode 100644
index 0000000..046f299
--- /dev/null
+++ b/.github/workflows/build_main.yaml
@@ -0,0 +1,126 @@
+name: Main - 🚀 build package
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ version:
+ name: Version & Build
+ runs-on: macos-latest
+ environment: release
+ permissions:
+ contents: write
+
+ outputs:
+ version: ${{ steps.get_new_version.outputs.result}}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ token: ${{secrets.ELEVATED_TOKEN}}
+
+ - name: 📇 Configure git
+ run: |
+ git fetch --prune --unshallow
+ git config --global user.name "GitHub Actions"
+ git config --global user.email "gh-actions@merckgroup.com"
+ shell: bash
+
+ # Retrieve the new version
+ - name: 🔂 Run standard-version
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const {execSync} = require('child_process');
+ execSync('npx standard-version --skip.tag', {stdio: 'inherit'});
+
+ # Retrieve the new version
+ - name: ⏎ Get new version
+ uses: actions/github-script@v7
+ id: get_new_version
+ with:
+ result-encoding: string
+ script: |
+ const fs = require('fs');
+ const package = JSON.parse(fs.readFileSync('package.json', 'utf8'));
+ return package.version;
+
+ - name: Print new version
+ run: echo ${{ steps.get_new_version.outputs.result}}
+
+ # Bump the pubspec.yaml file
+ - name: ⬆️ Bump pubspec.yaml
+ uses: emdgroup/mtrust-urp/.github/actions/update-pubspec@main
+ with:
+ version: ${{ steps.get_new_version.outputs.result }}
+ directory: .
+
+ - name: 📝 Update version in readme
+ uses: emdgroup/mtrust-urp/.github/actions/update-pubspec-readme-version@main
+ with:
+ directory: .
+
+ - name: Setup Dart
+ uses: dart-lang/setup-dart@v1
+
+ - name: Prepare Flutter
+ uses: emdgroup/mtrust-urp/.github/actions/prepare-flutter@main
+ with:
+ directory: "."
+
+ - name: Validate Flutter
+ uses: emdgroup/mtrust-urp/.github/actions/validate-flutter@main
+ with:
+ directory: "."
+ is_package: true
+
+ - name: Check licenses
+ uses: emdgroup/mtrust-urp/.github/actions/check-dart-licenses@main
+ with:
+ directory: "."
+
+ # We first commit with proper message and add an empty commit to keep the files history clean
+ - name: Update repo versions
+ run: |
+ git add .
+ git commit -m "chore(release): ${{ steps.get_new_version.outputs.result }}"
+ git commit --allow-empty -m "chore(release): ${{ steps.get_new_version.outputs.result }} [skip ci]"
+ git push origin main
+
+ # For this part it is important to not push a commit with [skip ci] before the tag release
+ - name: Push tag for pub.dev
+ run: |
+ git commit --allow-empty -m "chore(release): ${{ steps.get_new_version.outputs.result }}"
+ git tag -a v${{ steps.get_new_version.outputs.result }} -m "Pub.dev version ${{ steps.get_new_version.outputs.result }}"
+ git push origin v${{ steps.get_new_version.outputs.result }}
+
+ rebase_dev:
+ name: Write changes to dev branch
+ needs: [version]
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ token: ${{secrets.ELEVATED_TOKEN}}
+
+ - name: Setup Git User
+ run: |
+ git config --global user.name "GitHub Actions"
+ git config --global user.email "gh-actions@merckgroup.com"
+
+ - name: Update Test Branch
+ run: |
+ git checkout main
+ git fetch origin
+ git checkout dev
+ git pull
+ git merge origin/main
+ git push origin dev
diff --git a/.github/workflows/pr_dev.yaml b/.github/workflows/pr_dev.yaml
new file mode 100644
index 0000000..e9f47aa
--- /dev/null
+++ b/.github/workflows/pr_dev.yaml
@@ -0,0 +1,68 @@
+name: Dev - ↩️ pull request
+
+on:
+ pull_request:
+ branches:
+ - dev
+
+jobs:
+ validate_pr:
+ permissions:
+ pull-requests: write
+ uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/semantic_pull_request.yml@v1
+
+ build_example:
+ name: Build Example
+ needs: validate_pr
+ runs-on: macos-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Dart
+ uses: dart-lang/setup-dart@v1
+
+ - name: Prepare Flutter
+ uses: emdgroup/mtrust-urp/.github/actions/prepare-flutter@main
+ with:
+ directory: example
+
+ - name: Run static analysis
+ run: |
+ cd example
+ flutter analyze --no-fatal-warnings
+ shell: bash
+
+ - name: Check licenses
+ uses: emdgroup/mtrust-urp/.github/actions/check-dart-licenses@main
+ with:
+ directory: example
+
+ build_library:
+ name: Build Library
+ needs: validate_pr
+ runs-on: macos-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Dart
+ uses: dart-lang/setup-dart@v1
+
+ - name: Prepare Flutter
+ uses: emdgroup/mtrust-urp/.github/actions/prepare-flutter@main
+ with:
+ directory: .
+
+ - name: Validate Flutter
+ uses: emdgroup/mtrust-urp/.github/actions/validate-flutter@main
+ with:
+ directory: .
+ is_package: true
+
+ - name: Check licenses
+ uses: emdgroup/mtrust-urp/.github/actions/check-dart-licenses@main
+ with:
+ directory: .
diff --git a/.github/workflows/pr_main.yaml b/.github/workflows/pr_main.yaml
new file mode 100644
index 0000000..72df269
--- /dev/null
+++ b/.github/workflows/pr_main.yaml
@@ -0,0 +1,68 @@
+name: Main - ↩️ pull request
+
+on:
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ validate_pr:
+ permissions:
+ pull-requests: write
+ uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/semantic_pull_request.yml@v1
+
+ build_example:
+ name: Build Example
+ needs: validate_pr
+ runs-on: macos-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Dart
+ uses: dart-lang/setup-dart@v1
+
+ - name: Prepare Flutter
+ uses: emdgroup/mtrust-urp/.github/actions/prepare-flutter@main
+ with:
+ directory: example
+
+ - name: Run static analysis
+ run: |
+ cd example
+ flutter analyze --no-fatal-warnings
+ shell: bash
+
+ - name: Check licenses
+ uses: emdgroup/mtrust-urp/.github/actions/check-dart-licenses@main
+ with:
+ directory: example
+
+ build_library:
+ name: Build Library
+ needs: validate_pr
+ runs-on: macos-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Dart
+ uses: dart-lang/setup-dart@v1
+
+ - name: Prepare Flutter
+ uses: emdgroup/mtrust-urp/.github/actions/prepare-flutter@main
+ with:
+ directory: .
+
+ - name: Validate Flutter
+ uses: emdgroup/mtrust-urp/.github/actions/validate-flutter@main
+ with:
+ directory: .
+ is_package: true
+
+ - name: Check licenses
+ uses: emdgroup/mtrust-urp/.github/actions/check-dart-licenses@main
+ with:
+ directory: .
diff --git a/.github/workflows/pr_origin_check.yaml b/.github/workflows/pr_origin_check.yaml
new file mode 100644
index 0000000..fd6ccf6
--- /dev/null
+++ b/.github/workflows/pr_origin_check.yaml
@@ -0,0 +1,21 @@
+name: Main - 🧐 Check PR Source Branch
+
+on:
+ pull_request:
+ branches:
+ - main
+ types: [opened, synchronize]
+
+jobs:
+ check-branch:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Display Source Branch
+ run: |
+ echo "Pull request is coming from branch: ${{ github.head_ref }}"
+
+ - name: Fail if source branch is not allowed
+ if: ${{ github.head_ref != 'dev' }}
+ run: |
+ echo "Error: Pull requests are only allowed from the 'dev' branch."
+ exit 1
diff --git a/.github/workflows/publish_dev.yaml b/.github/workflows/publish_dev.yaml
new file mode 100644
index 0000000..d0ff2fc
--- /dev/null
+++ b/.github/workflows/publish_dev.yaml
@@ -0,0 +1,25 @@
+name: Dev - 🚧 publish package
+
+on:
+ push:
+ tags:
+ - "v[0-9]+.[0-9]+.[0-9]+-[0-9]+"
+
+jobs:
+ publish:
+ permissions:
+ id-token: write
+ runs-on: ubuntu-latest
+ environment: "pub.dev"
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ token: ${{secrets.ELEVATED_TOKEN}}
+
+ - name: Publish mtrust_barcode_kit
+ uses: emdgroup/mtrust-urp/.github/actions/publish-pub-dev@main
+ with:
+ directory: "."
+ publish_mode: --force
diff --git a/.github/workflows/publish_main.yaml b/.github/workflows/publish_main.yaml
new file mode 100644
index 0000000..61154e7
--- /dev/null
+++ b/.github/workflows/publish_main.yaml
@@ -0,0 +1,25 @@
+name: Main - 🚀 publish package
+
+on:
+ push:
+ tags:
+ - "v[0-9]+.[0-9]+.[0-9]+"
+
+jobs:
+ publish:
+ permissions:
+ id-token: write
+ runs-on: ubuntu-latest
+ environment: "pub.dev"
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ token: ${{secrets.ELEVATED_TOKEN}}
+
+ - name: Publish mtrust_barcode_kit
+ uses: emdgroup/mtrust-urp/.github/actions/publish-pub-dev@main
+ with:
+ directory: "."
+ publish_mode: --force
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..813693d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+.packages
+build/
+node_modules/
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..2d7b687
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,61 @@
+# Changelog
+
+All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+### [2.0.1-0](v2.0.1-0) (2024-10-08)
+
+### [2.0.1](v2.0.1) (2024-10-08)
+
+## [2.0.0](v2.0.0) (2024-09-11)
+
+
+### ⚠ BREAKING CHANGES
+
+* OCR implementation & fix: pausing on android
+
+### Features
+
+* ocr for ios ([5de36d0](5de36d006af1debd7f4af8e11f7c876a84e6ead8))
+* OCR implementation & fix: pausing on android ([8bcb9bf](8bcb9bf809e7116cfb5128adea835672f726baff))
+
+### [1.0.9](v1.0.9) (2024-06-17)
+
+
+### Bug Fixes
+
+* error not rendered with priority ([d37df33](d37df3376062cde745a6c105f8aa35d09c9a9a11))
+
+### [1.0.8](v1.0.8) (2024-06-17)
+
+
+### Bug Fixes
+
+* ios simulator crashes ([46da62f](46da62f66dc008c7fef306c15a6f5d673fe171cc))
+
+### 1.0.7 (2023-10-09)
+
+### 1.0.6 (2023-09-29)
+
+### 1.0.5 (2023-08-31)
+
+### 1.0.4 (2023-07-21)
+
+### 1.0.3 (2023-07-02)
+
+
+### Bug Fixes
+
+* null pointer exception due to unnecessary restart ([7ce5010](7ce5010a34e2e90d3ed1be44b45136d3cb70d127))
+
+### 1.0.2 (2023-06-28)
+
+### 1.0.1 (2023-06-28)
+
+
+### Bug Fixes
+
+* standard-version configuration ([f426bfc](f426bfc54413a847e3db073cf63b06dbd1bc9c66))
+
+## 0.0.1
+
+* TODO: Describe initial release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..80e98ec
--- /dev/null
+++ b/README.md
@@ -0,0 +1,95 @@
+
+# Barcode-Kit
+
+
+
+[](https://docs.mtrust.io/sdks/barcode-kit/)
+
+
+[](https://pub.dev/packages/mtrust_barcode_kit)
+[](https://pub.dev/packages/mtrust_barcode_kit/score)
+[](https://pub.dev/packages/very_good_analysis)
+
+## Overview
+
+Barcode-Kit is a flutter package that allows you to read barcodes using the camera. It uses native textures to display the camera feed and the barcode overlay. It is built on top of the [Google ML Kit](https://developers.google.com/ml-kit) and [iOS Vision](https://developer.apple.com/documentation/vision) for barcode scanning.
+
+It ships as a single widget that handles most of the work for you.
+
+
+
+## Prerequisites
+
+- Flutter SDK installed on your machine.
+- Familiarity with Flutter development.
+
+## Installation
+
+Add the `mtrust_barcode_kit` to your Flutter project via the `pub add` command
+
+```
+flutter pub add mtrust_barcode_kit
+```
+or manually add it to your `pubspec.yaml`
+```yaml
+dependencies:
+ mtrust_barcode_kit: ^2.0.2
+```
+
+## Usage
+
+```dart
+BarcodeKitView(
+ // The barcode formats to scan
+ formats: _formats,
+ cameraFit: BoxFit.cover,
+ // The camera overlay mask
+ maskHeight: 200,
+ maskWidth: 200,
+ widgetAboveMask: Widget // Place something above the camera cutout,
+ widgetBelowMask: Widget // Place something below the camera cutout,
+
+ // Whether the camera feed is frozen and barcode scanning is paused
+ paused: true/false,
+
+ maskAdditionpauseOpacity: 0.2,
+
+ // Whether the camera is rotated along with the device.
+ followRotation: true/false,
+
+ // The callback when a barcode is scanned
+ onBarcodeScanned: (barcode) {
+
+ },
+ // Custom ui for building requesting permissions and loading screens.
+ // Build your own by extending the BarcodeKitUiBuilder
+ uiBuilder: BarcodeKitUiBuilder(),
+)
+```
+
+## Barcode Formats
+
+The `formats` parameter is a list of barcode formats to scan. The supported formats are:
+
+| Format | iOS | Android |
+| ----------- | --- | ------- |
+| AZTEC | ✅ | ✅ |
+| CODABAR | ❌ | ✅ |
+| CODE39 | ✅ | ✅ |
+| CODE93 | ✅ | ✅ |
+| CODE128 | ✅ | ✅ |
+| DATAM ATRIX | ✅ | ✅ |
+| EAN8 | ✅ | ✅ |
+| EAN13 | ✅ | ✅ |
+| ITF | ✅ | ✅ |
+| PDF417 | ✅ | ✅ |
+| QR-CODE | ✅ | ✅ |
+| UPCA | ❌ | ✅ |
+| UPCE | ✅ | ✅ |
+
+
+## Contributing
+We welcome contributions! Please fork the repository and submit a pull request with your changes. Ensure that your code adheres to our coding standards and includes appropriate tests.
+
+## License
+This project is licensed under the Apache 2.0 License. See the [LICENSE](./LICENSE) file for details.
\ No newline at end of file
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 0000000..636a781
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1,5 @@
+include: package:very_good_analysis/analysis_options.5.0.0.yaml
+
+analyzer:
+ exclude:
+ - lib/src/pigeon.dart
diff --git a/android/.gitignore b/android/.gitignore
new file mode 100644
index 0000000..161bdcd
--- /dev/null
+++ b/android/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.cxx
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 0000000..57293e6
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,46 @@
+group 'com.emddigital.barcode_kit'
+version '1.0-SNAPSHOT'
+
+
+ext.camerax_version = '1.3.0-alpha03'
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ compileSdkVersion 34
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ minSdkVersion 16
+ }
+}
+
+dependencies {
+ implementation "androidx.camera:camera-camera2:$camerax_version"
+ implementation "androidx.camera:camera-lifecycle:$camerax_version"
+ implementation "androidx.camera:camera-mlkit-vision:$camerax_version"
+ implementation "androidx.camera:camera-mlkit-vision:$camerax_version"
+ implementation 'com.google.mlkit:barcode-scanning:17.0.2'
+ implementation 'com.google.android.gms:play-services-mlkit-text-recognition-common:19.1.0'
+ implementation 'com.google.android.gms:play-services-mlkit-text-recognition:19.0.1'
+}
\ No newline at end of file
diff --git a/android/settings.gradle b/android/settings.gradle
new file mode 100644
index 0000000..05e070b
--- /dev/null
+++ b/android/settings.gradle
@@ -0,0 +1,25 @@
+pluginManagement {
+ def flutterSdkPath = {
+ def properties = new Properties()
+ file("local.properties").withInputStream { properties.load(it) }
+ def flutterSdkPath = properties.getProperty("flutter.sdk")
+ assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+ return flutterSdkPath
+ }()
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "7.3.0" apply false
+ id "org.jetbrains.kotlin.android" version "1.8.0" apply false
+}
+
+include ":app"
\ No newline at end of file
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4ce72ee
--- /dev/null
+++ b/android/src/main/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/android/src/main/kotlin/com/emddigital/barcode_kit/BarcodeKitPlugin.kt b/android/src/main/kotlin/com/emddigital/barcode_kit/BarcodeKitPlugin.kt
new file mode 100644
index 0000000..e2ad5ca
--- /dev/null
+++ b/android/src/main/kotlin/com/emddigital/barcode_kit/BarcodeKitPlugin.kt
@@ -0,0 +1,320 @@
+package com.emddigital.barcode_kit
+
+
+import BarcodeFormat
+import BarcodeKitFlutterApi
+import BarcodeKitHostApi
+
+import CameraLensDirection
+import CameraOpenResponse
+import CornerPoint
+import DetectedBarcode
+import android.annotation.SuppressLint
+import android.os.Build
+import android.util.Log
+import android.view.Surface
+import androidx.annotation.RequiresApi
+import androidx.camera.core.Camera
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.ImageAnalysis
+import androidx.camera.core.Preview
+import androidx.camera.core.TorchState
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.mlkit.vision.MlKitAnalyzer
+import androidx.core.content.ContextCompat
+import androidx.lifecycle.LifecycleOwner
+import com.google.mlkit.vision.barcode.BarcodeScanner
+import com.google.mlkit.vision.barcode.BarcodeScannerOptions
+import com.google.mlkit.vision.barcode.BarcodeScanning
+import com.google.mlkit.vision.barcode.common.Barcode
+import com.google.mlkit.vision.text.TextRecognition
+import com.google.mlkit.vision.text.TextRecognizer
+import com.google.mlkit.vision.text.latin.TextRecognizerOptions
+import io.flutter.embedding.engine.plugins.FlutterPlugin
+import io.flutter.embedding.engine.plugins.activity.ActivityAware
+import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
+import io.flutter.plugin.common.MethodChannel
+import io.flutter.view.TextureRegistry
+import java.util.concurrent.Executor
+
+
+val barcodeFormatMap = hashMapOf(
+ BarcodeFormat.AZTEC to Barcode.FORMAT_AZTEC,
+ BarcodeFormat.CODABAR to Barcode.FORMAT_CODABAR,
+ BarcodeFormat.CODE39 to Barcode.FORMAT_CODE_39,
+ BarcodeFormat.CODE93 to Barcode.FORMAT_CODE_93,
+ BarcodeFormat.CODE128 to Barcode.FORMAT_CODE_128,
+ BarcodeFormat.DATAMATRIX to Barcode.FORMAT_DATA_MATRIX,
+ BarcodeFormat.EAN8 to Barcode.FORMAT_EAN_8,
+ BarcodeFormat.EAN13 to Barcode.FORMAT_EAN_13,
+ BarcodeFormat.PDF417 to Barcode.FORMAT_PDF417,
+ BarcodeFormat.QRCODE to Barcode.FORMAT_QR_CODE,
+ BarcodeFormat.UPCA to Barcode.FORMAT_UPC_A,
+ BarcodeFormat.UPCE to Barcode.FORMAT_UPC_E,
+ BarcodeFormat.ITF to Barcode.FORMAT_ITF
+)
+
+val barcodeFormatsReversed = barcodeFormatMap.entries.associateBy({ it.value }, { it.key })
+
+/** BarcodeKitPlugin */
+class BarcodeKitPlugin : FlutterPlugin, BarcodeKitHostApi, ActivityAware {
+
+
+ // Flutter
+ private var activity: ActivityPluginBinding? = null
+ private var flutter: FlutterPlugin.FlutterPluginBinding? = null
+ private lateinit var channel: MethodChannel
+ private var flutterApi: BarcodeKitFlutterApi? = null
+
+ // Camera related
+ private var cameraProvider: ProcessCameraProvider? = null
+ private var camera: Camera? = null
+ private var preview: Preview? = null
+ private var analysis: ImageAnalysis? = null
+ private var cameraSelector: CameraSelector? = null
+
+ // Surface for preview
+ private var textureEntry: TextureRegistry.SurfaceTextureEntry? = null
+
+ private var ocrEnabled = false
+
+
+ override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
+ // Keep a reference to the binding
+ flutter = flutterPluginBinding
+ flutterApi = BarcodeKitFlutterApi(flutterPluginBinding.binaryMessenger)
+ BarcodeKitHostApi.setUp(flutterPluginBinding.binaryMessenger, this)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
+ // Close the pigeon api
+ BarcodeKitHostApi.setUp(binding.binaryMessenger, null)
+
+ flutterApi = null
+ // Remove reference
+ flutter = null
+
+ }
+
+ override fun onAttachedToActivity(binding: ActivityPluginBinding) {
+ // Keep binding to activity
+ activity = binding
+ }
+
+ override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
+ // Keep binding
+ onAttachedToActivity(binding)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ override fun onDetachedFromActivity() {
+ // Remove references
+ activity = null
+ closeCamera()
+ }
+
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ override fun onDetachedFromActivityForConfigChanges() {
+ onDetachedFromActivity()
+
+ }
+
+
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ fun makePreviewSurface(executor: Executor){
+
+
+ // Preview
+ val surfaceProvider = Preview.SurfaceProvider { request ->
+ val resolution = request.resolution
+ val texture = textureEntry!!.surfaceTexture()
+ texture.setDefaultBufferSize(resolution.width, resolution.height)
+ val surface = Surface(texture)
+ request.provideSurface(surface, executor) { }
+ }
+ preview = Preview.Builder().build().apply { setSurfaceProvider(surfaceProvider) }
+ textureEntry = flutter!!.textureRegistry.createSurfaceTexture()
+
+ }
+
+ fun buildDetectors(formats: List) : Pair{
+ val options = BarcodeScannerOptions.Builder()
+
+ if (formats.isNotEmpty()) {
+
+ val formattedFormats = formats.map { format ->
+ barcodeFormatMap[BarcodeFormat.values().first { format.toInt() == it.raw }]!!
+ }
+ if (formats.size > 1) {
+ options.setBarcodeFormats(
+ formattedFormats.first(),
+ *formattedFormats.subList(1, formattedFormats.lastIndex).toIntArray()
+ )
+ } else {
+ options.setBarcodeFormats(formattedFormats.first())
+ }
+ }
+
+ val barcodeScanner = BarcodeScanning.getClient(options.build())
+ val textRecognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
+ return Pair(barcodeScanner, textRecognizer)
+ }
+
+
+
+ // Open the camera and attach an MlKit analyzer
+ @SuppressLint("UnsafeOptInUsageError", "RestrictedApi")
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ override fun openCamera(
+ direction: CameraLensDirection,
+ formats: List,
+ callback: (Result) -> Unit
+ ) {
+ // Close potential existing camera instance
+ closeCamera()
+
+ val future = ProcessCameraProvider.getInstance(activity!!.activity)
+ val executor = ContextCompat.getMainExecutor(activity!!.activity)
+
+ future.addListener({
+ cameraProvider = future.get()
+ makePreviewSurface(executor)
+
+ val detectors = buildDetectors(formats)
+
+ val analyzer =
+ MlKitAnalyzer(
+ listOf(detectors.first,detectors.second),
+ ImageAnalysis.COORDINATE_SYSTEM_ORIGINAL,
+ executor
+ ) {
+ processMlResult(it, detectors.first, detectors.second)
+ }
+
+ analysis = ImageAnalysis.Builder()
+ .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
+ .build().apply { setAnalyzer(executor, analyzer) }
+ // Bind to lifecycle.
+
+
+ var cameraSelectorBuilder = CameraSelector.Builder()
+
+ cameraSelectorBuilder = when (direction) {
+ CameraLensDirection.BACK -> cameraSelectorBuilder.requireLensFacing(CameraSelector.LENS_FACING_BACK)
+ CameraLensDirection.EXT -> cameraSelectorBuilder.requireLensFacing(CameraSelector.LENS_FACING_EXTERNAL)
+ CameraLensDirection.FRONT -> cameraSelectorBuilder.requireLensFacing(CameraSelector.LENS_FACING_FRONT)
+ CameraLensDirection.UNKNOWN -> cameraSelectorBuilder
+
+ }
+
+ cameraSelector = cameraSelectorBuilder.build()
+ val camera = attachCamera()
+
+ @SuppressLint("RestrictedApi")
+ val resolution = preview!!.attachedSurfaceResolution!!
+ val portrait = camera.cameraInfo.sensorRotationDegrees % 180 == 0
+ val width = if (portrait) resolution.width.toLong() else resolution.height.toLong()
+ val height = if (portrait) resolution.height.toLong() else resolution.width.toLong()
+
+
+ callback(
+ Result.success(
+ CameraOpenResponse(
+ supportsFlash = camera.cameraInfo.hasFlashUnit(),
+ height = height,
+ width = width,
+ textureId = textureEntry!!.id().toString()
+ )
+ )
+ )
+ }, executor)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ fun attachCamera(): Camera{
+ val owner = activity!!.activity as LifecycleOwner
+ val camera = cameraProvider!!.bindToLifecycle(owner, cameraSelector!!, preview, analysis)
+ camera.cameraInfo.torchState.observe(owner) { state ->
+ flutterApi?.onTorchStateChanged(state == TorchState.ON) {}
+ }
+
+ this.camera = camera
+
+ return camera
+ }
+
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ fun processMlResult(result: MlKitAnalyzer.Result, barcodeScanner: BarcodeScanner, textRecognizer: TextRecognizer ) {
+ val barcodes = result.getValue(barcodeScanner)
+
+
+
+ if (!barcodes.isNullOrEmpty()) {
+ for (barcode: Barcode in barcodes) {
+ flutterApi?.onBarcodeScanned(
+ DetectedBarcode(
+ rawValue = barcode.rawValue,
+ format = barcodeFormatsReversed[barcode.format],
+ cornerPoints = barcode.cornerPoints!!.map { point ->
+ CornerPoint(point.x.toDouble(), point.y.toDouble())
+ }
+ )
+ ) {}
+ }
+ }
+ if (ocrEnabled) {
+ val texts = result.getValue(textRecognizer)
+ if (texts != null) {
+ for (block in texts.textBlocks) {
+ for (line in block.lines) {
+ // Handle recognized text
+ flutterApi?.onTextDetected(
+ line.text,
+ ) {}
+ }
+ }
+ }
+ }
+ }
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ override fun pauseCamera() {
+ cameraProvider?.unbindAll()
+ }
+
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ override fun resumeCamera() {
+ attachCamera()
+ }
+
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ override fun setTorch(enabled: Boolean) {
+ camera?.cameraControl?.enableTorch(enabled)
+ }
+
+ override fun setOCREnabled(enabled: Boolean) {
+ ocrEnabled = enabled
+ }
+
+
+ @SuppressLint("RestrictedApi")
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ override fun closeCamera() {
+
+
+ camera?.cameraInfo?.torchState?.removeObservers(activity!!.activity as LifecycleOwner)
+ cameraProvider?.unbindAll()
+
+
+ textureEntry?.release()
+ // Release references
+ camera = null
+ textureEntry = null
+ preview = null
+ cameraProvider = null
+
+
+ }
+
+
+}
diff --git a/android/src/main/kotlin/com/emddigital/barcode_kit/pigeon/Pigeon.kt b/android/src/main/kotlin/com/emddigital/barcode_kit/pigeon/Pigeon.kt
new file mode 100644
index 0000000..50338d0
--- /dev/null
+++ b/android/src/main/kotlin/com/emddigital/barcode_kit/pigeon/Pigeon.kt
@@ -0,0 +1,375 @@
+// Autogenerated from Pigeon (v10.1.6), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+
+
+import android.util.Log
+import io.flutter.plugin.common.BasicMessageChannel
+import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.common.MessageCodec
+import io.flutter.plugin.common.StandardMessageCodec
+import java.io.ByteArrayOutputStream
+import java.nio.ByteBuffer
+
+private fun wrapResult(result: Any?): List {
+ return listOf(result)
+}
+
+private fun wrapError(exception: Throwable): List {
+ if (exception is FlutterError) {
+ return listOf(
+ exception.code,
+ exception.message,
+ exception.details
+ )
+ } else {
+ return listOf(
+ exception.javaClass.simpleName,
+ exception.toString(),
+ "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
+ )
+ }
+}
+
+/**
+ * Error class for passing custom error details to Flutter via a thrown PlatformException.
+ * @property code The error code.
+ * @property message The error message.
+ * @property details The error details. Must be a datatype supported by the api codec.
+ */
+class FlutterError (
+ val code: String,
+ override val message: String? = null,
+ val details: Any? = null
+) : Throwable()
+
+enum class BarcodeFormat(val raw: Int) {
+ AZTEC(0),
+ CODABAR(1),
+ CODE39(2),
+ CODE93(3),
+ CODE128(4),
+ DATAMATRIX(5),
+ EAN8(6),
+ EAN13(7),
+ PDF417(8),
+ QRCODE(9),
+ UPCA(10),
+ UPCE(11),
+ ITF(12);
+
+ companion object {
+ fun ofRaw(raw: Int): BarcodeFormat? {
+ return values().firstOrNull { it.raw == raw }
+ }
+ }
+}
+
+enum class CameraLensDirection(val raw: Int) {
+ FRONT(0),
+ BACK(1),
+ EXT(2),
+ UNKNOWN(3);
+
+ companion object {
+ fun ofRaw(raw: Int): CameraLensDirection? {
+ return values().firstOrNull { it.raw == raw }
+ }
+ }
+}
+
+/** Generated class from Pigeon that represents data sent in messages. */
+data class CameraOpenResponse (
+ val supportsFlash: Boolean? = null,
+ val height: Long? = null,
+ val width: Long? = null,
+ val textureId: String? = null
+
+) {
+ companion object {
+ @Suppress("UNCHECKED_CAST")
+ fun fromList(list: List): CameraOpenResponse {
+ val supportsFlash = list[0] as Boolean?
+ val height = list[1].let { if (it is Int) it.toLong() else it as Long? }
+ val width = list[2].let { if (it is Int) it.toLong() else it as Long? }
+ val textureId = list[3] as String?
+ return CameraOpenResponse(supportsFlash, height, width, textureId)
+ }
+ }
+ fun toList(): List {
+ return listOf(
+ supportsFlash,
+ height,
+ width,
+ textureId,
+ )
+ }
+}
+
+/** Generated class from Pigeon that represents data sent in messages. */
+data class DetectedBarcode (
+ val rawValue: String? = null,
+ val cornerPoints: List? = null,
+ val format: BarcodeFormat? = null,
+ val textValue: String? = null
+
+) {
+ companion object {
+ @Suppress("UNCHECKED_CAST")
+ fun fromList(list: List): DetectedBarcode {
+ val rawValue = list[0] as String?
+ val cornerPoints = list[1] as List?
+ val format: BarcodeFormat? = (list[2] as Int?)?.let {
+ BarcodeFormat.ofRaw(it)
+ }
+ val textValue = list[3] as String?
+ return DetectedBarcode(rawValue, cornerPoints, format, textValue)
+ }
+ }
+ fun toList(): List {
+ return listOf(
+ rawValue,
+ cornerPoints,
+ format?.raw,
+ textValue,
+ )
+ }
+}
+
+/** Generated class from Pigeon that represents data sent in messages. */
+data class CornerPoint (
+ val x: Double,
+ val y: Double
+
+) {
+ companion object {
+ @Suppress("UNCHECKED_CAST")
+ fun fromList(list: List): CornerPoint {
+ val x = list[0] as Double
+ val y = list[1] as Double
+ return CornerPoint(x, y)
+ }
+ }
+ fun toList(): List {
+ return listOf(
+ x,
+ y,
+ )
+ }
+}
+
+@Suppress("UNCHECKED_CAST")
+private object BarcodeKitHostApiCodec : StandardMessageCodec() {
+ override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
+ return when (type) {
+ 128.toByte() -> {
+ return (readValue(buffer) as? List)?.let {
+ CameraOpenResponse.fromList(it)
+ }
+ }
+ else -> super.readValueOfType(type, buffer)
+ }
+ }
+ override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
+ when (value) {
+ is CameraOpenResponse -> {
+ stream.write(128)
+ writeValue(stream, value.toList())
+ }
+ else -> super.writeValue(stream, value)
+ }
+ }
+}
+
+/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
+interface BarcodeKitHostApi {
+ fun openCamera(direction: CameraLensDirection, formats: List, callback: (Result) -> Unit)
+ fun setOCREnabled(enabled: Boolean)
+ fun closeCamera()
+ fun pauseCamera()
+ fun resumeCamera()
+ fun setTorch(enabled: Boolean)
+
+ companion object {
+ /** The codec used by BarcodeKitHostApi. */
+ val codec: MessageCodec by lazy {
+ BarcodeKitHostApiCodec
+ }
+ /** Sets up an instance of `BarcodeKitHostApi` to handle messages through the `binaryMessenger`. */
+ @Suppress("UNCHECKED_CAST")
+ fun setUp(binaryMessenger: BinaryMessenger, api: BarcodeKitHostApi?) {
+ run {
+ val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.barcode_kit.BarcodeKitHostApi.openCamera", codec)
+ if (api != null) {
+ channel.setMessageHandler { message, reply ->
+ val args = message as List
+ val directionArg = CameraLensDirection.ofRaw(args[0] as Int)!!
+ val formatsArg = args[1] as List
+ api.openCamera(directionArg, formatsArg) { result: Result ->
+ val error = result.exceptionOrNull()
+ if (error != null) {
+ reply.reply(wrapError(error))
+ } else {
+ val data = result.getOrNull()
+ reply.reply(wrapResult(data))
+ }
+ }
+ }
+ } else {
+ channel.setMessageHandler(null)
+ }
+ }
+ run {
+ val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.barcode_kit.BarcodeKitHostApi.setOCREnabled", codec)
+ if (api != null) {
+ channel.setMessageHandler { message, reply ->
+ val args = message as List
+ val enabledArg = args[0] as Boolean
+ var wrapped: List
+ try {
+ api.setOCREnabled(enabledArg)
+ wrapped = listOf(null)
+ } catch (exception: Throwable) {
+ wrapped = wrapError(exception)
+ }
+ reply.reply(wrapped)
+ }
+ } else {
+ channel.setMessageHandler(null)
+ }
+ }
+ run {
+ val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.barcode_kit.BarcodeKitHostApi.closeCamera", codec)
+ if (api != null) {
+ channel.setMessageHandler { _, reply ->
+ var wrapped: List
+ try {
+ api.closeCamera()
+ wrapped = listOf(null)
+ } catch (exception: Throwable) {
+ wrapped = wrapError(exception)
+ }
+ reply.reply(wrapped)
+ }
+ } else {
+ channel.setMessageHandler(null)
+ }
+ }
+ run {
+ val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.barcode_kit.BarcodeKitHostApi.pauseCamera", codec)
+ if (api != null) {
+ channel.setMessageHandler { _, reply ->
+ var wrapped: List
+ try {
+ api.pauseCamera()
+ wrapped = listOf(null)
+ } catch (exception: Throwable) {
+ wrapped = wrapError(exception)
+ }
+ reply.reply(wrapped)
+ }
+ } else {
+ channel.setMessageHandler(null)
+ }
+ }
+ run {
+ val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.barcode_kit.BarcodeKitHostApi.resumeCamera", codec)
+ if (api != null) {
+ channel.setMessageHandler { _, reply ->
+ var wrapped: List
+ try {
+ api.resumeCamera()
+ wrapped = listOf(null)
+ } catch (exception: Throwable) {
+ wrapped = wrapError(exception)
+ }
+ reply.reply(wrapped)
+ }
+ } else {
+ channel.setMessageHandler(null)
+ }
+ }
+ run {
+ val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.barcode_kit.BarcodeKitHostApi.setTorch", codec)
+ if (api != null) {
+ channel.setMessageHandler { message, reply ->
+ val args = message as List
+ val enabledArg = args[0] as Boolean
+ var wrapped: List
+ try {
+ api.setTorch(enabledArg)
+ wrapped = listOf(null)
+ } catch (exception: Throwable) {
+ wrapped = wrapError(exception)
+ }
+ reply.reply(wrapped)
+ }
+ } else {
+ channel.setMessageHandler(null)
+ }
+ }
+ }
+ }
+}
+@Suppress("UNCHECKED_CAST")
+private object BarcodeKitFlutterApiCodec : StandardMessageCodec() {
+ override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
+ return when (type) {
+ 128.toByte() -> {
+ return (readValue(buffer) as? List)?.let {
+ CornerPoint.fromList(it)
+ }
+ }
+ 129.toByte() -> {
+ return (readValue(buffer) as? List)?.let {
+ DetectedBarcode.fromList(it)
+ }
+ }
+ else -> super.readValueOfType(type, buffer)
+ }
+ }
+ override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
+ when (value) {
+ is CornerPoint -> {
+ stream.write(128)
+ writeValue(stream, value.toList())
+ }
+ is DetectedBarcode -> {
+ stream.write(129)
+ writeValue(stream, value.toList())
+ }
+ else -> super.writeValue(stream, value)
+ }
+ }
+}
+
+/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */
+@Suppress("UNCHECKED_CAST")
+class BarcodeKitFlutterApi(private val binaryMessenger: BinaryMessenger) {
+ companion object {
+ /** The codec used by BarcodeKitFlutterApi. */
+ val codec: MessageCodec by lazy {
+ BarcodeKitFlutterApiCodec
+ }
+ }
+ /** Caleld from the host when a barcode is detected. */
+ fun onBarcodeScanned(barcodeArg: DetectedBarcode, callback: () -> Unit) {
+ val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.barcode_kit.BarcodeKitFlutterApi.onBarcodeScanned", codec)
+ channel.send(listOf(barcodeArg)) {
+ callback()
+ }
+ }
+ /** Called from the host when text is detected. */
+ fun onTextDetected(textArg: String, callback: () -> Unit) {
+ val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.barcode_kit.BarcodeKitFlutterApi.onTextDetected", codec)
+ channel.send(listOf(textArg)) {
+ callback()
+ }
+ }
+ /** Called from the host when the torch state changes. */
+ fun onTorchStateChanged(enabledArg: Boolean, callback: () -> Unit) {
+ val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.barcode_kit.BarcodeKitFlutterApi.onTorchStateChanged", codec)
+ channel.send(listOf(enabledArg)) {
+ callback()
+ }
+ }
+}
diff --git a/azure_pipeline.yml b/azure_pipeline.yml
new file mode 100644
index 0000000..ca1fa5c
--- /dev/null
+++ b/azure_pipeline.yml
@@ -0,0 +1,82 @@
+# Xcode
+# Build, test, and archive an Xcode workspace on macOS.
+# Add steps that install certificates, test, sign, and distribute an app, save build artifacts, and more:
+# https://docs.microsoft.com/azure/devops/pipelines/languages/xcode
+
+trigger:
+ - main
+
+pool:
+ vmImage: "ubuntu-latest"
+
+steps:
+ # Checkout with persist credentials
+ - checkout: self
+ persistCredentials: true
+ # Install cert for packages
+ - task: DownloadSecureFile@1
+ name: private
+ inputs:
+ secureFile: "pipeline_rsa"
+ - task: DownloadSecureFile@1
+ name: public
+ inputs:
+ secureFile: "pipeline_rsa.pub"
+
+ - script: |
+ mkdir ~/.ssh/
+
+ cp $(private.secureFilePath) ~/.ssh/id_rsa
+ chmod 400 ~/.ssh/id_rsa
+ cp $(public.secureFilePath) ~/.ssh/id_rsa.pub
+
+ - script: |
+ git config --global user.email "pipeline@mtrust.com"
+ git config --global user.name "Azure Pipeline M-Trust"
+
+ # Install Flutter
+ - task: FlutterInstall@0
+ inputs:
+ mode: "auto"
+ channel: "stable"
+ version: "latest"
+ # Get packages
+ - task: FlutterCommand@0
+ inputs:
+ projectDirectory: "."
+ arguments: "pub get"
+
+ # Build runner
+ - task: FlutterCommand@0
+ inputs:
+ projectDirectory: "."
+ arguments: |
+ pub run pigeon \
+ --input pigeons/barcode_kit_pigeon.dart \
+ --dart_out lib/src/pigeon.dart \
+ --experimental_swift_out ios/Classes/Pigeon.swift \
+ --experimental_kotlin_out android/src/main/kotlin/com/emddigital/barcode_kit/pigeon/Pigeon.kt \
+ --java_package "com.emddigital.barcode_kit.pigeon" "
+ # Install NPM
+ - task: Npm@1
+ inputs:
+ command: "install"
+ # Run release cmd
+ - task: Npm@1
+ inputs:
+ command: "custom"
+ customCommand: "run release"
+ # Get packages
+ - task: FlutterCommand@0
+ inputs:
+ projectDirectory: "example"
+ arguments: "pub get"
+
+ ## Android build and distribute
+ - task: FlutterBuild@0
+ inputs:
+ target: "apk"
+ projectDirectory: "example"
+ arguments: "--release"
+ # Push tags
+ - script: 'git -c http.extraheader="AUTHORIZATION: bearer $(System.AccessToken)" push --follow-tags origin HEAD:$(Build.SourceBranchName)'
diff --git a/banner.png b/banner.png
new file mode 100644
index 0000000..1374718
Binary files /dev/null and b/banner.png differ
diff --git a/demo.gif b/demo.gif
new file mode 100644
index 0000000..a11799f
Binary files /dev/null and b/demo.gif differ
diff --git a/demo.mov b/demo.mov
new file mode 100644
index 0000000..7d2d864
Binary files /dev/null and b/demo.mov differ
diff --git a/example/.gitignore b/example/.gitignore
new file mode 100644
index 0000000..24476c5
--- /dev/null
+++ b/example/.gitignore
@@ -0,0 +1,44 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/example/README.md b/example/README.md
new file mode 100644
index 0000000..e77a5ec
--- /dev/null
+++ b/example/README.md
@@ -0,0 +1,16 @@
+# barcode_kit_example
+
+Demonstrates how to use the barcode_kit plugin.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
+
+For help getting started with Flutter development, view the
+[online documentation](https://docs.flutter.dev/), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml
new file mode 100644
index 0000000..61b6c4d
--- /dev/null
+++ b/example/analysis_options.yaml
@@ -0,0 +1,29 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at
+ # https://dart-lang.github.io/linter/lints/index.html.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/example/android/.gitignore b/example/android/.gitignore
new file mode 100644
index 0000000..6f56801
--- /dev/null
+++ b/example/android/.gitignore
@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle
new file mode 100644
index 0000000..92e45da
--- /dev/null
+++ b/example/android/app/build.gradle
@@ -0,0 +1,65 @@
+plugins {
+ id "com.android.application"
+ id "kotlin-android"
+ id "dev.flutter.flutter-gradle-plugin"
+}
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+
+android {
+ compileSdkVersion flutter.compileSdkVersion
+ ndkVersion flutter.ndkVersion
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+
+ applicationId "com.emddigital.barcode_kit_example"
+ minSdkVersion flutter.minSdkVersion
+ targetSdkVersion flutter.targetSdkVersion
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+}
diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..6e1bcf6
--- /dev/null
+++ b/example/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..cc44ddd
--- /dev/null
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/android/app/src/main/ic_launcher-playstore.png b/example/android/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000..d3f4005
Binary files /dev/null and b/example/android/app/src/main/ic_launcher-playstore.png differ
diff --git a/example/android/app/src/main/kotlin/com/emddigital/barcode_kit_example/MainActivity.kt b/example/android/app/src/main/kotlin/com/emddigital/barcode_kit_example/MainActivity.kt
new file mode 100644
index 0000000..ae91da8
--- /dev/null
+++ b/example/android/app/src/main/kotlin/com/emddigital/barcode_kit_example/MainActivity.kt
@@ -0,0 +1,6 @@
+package com.emddigital.barcode_kit_example
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..f74085f
--- /dev/null
+++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/example/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..c52e972
Binary files /dev/null and b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..b0a443a
Binary files /dev/null and b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..4890608
Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4c7390a
Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4795323
Binary files /dev/null and b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..06952be
--- /dev/null
+++ b/example/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..cb1ef88
--- /dev/null
+++ b/example/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..6e1bcf6
--- /dev/null
+++ b/example/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/example/android/build.gradle b/example/android/build.gradle
new file mode 100644
index 0000000..8f31e8c
--- /dev/null
+++ b/example/android/build.gradle
@@ -0,0 +1,18 @@
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+tasks.register("clean", Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/example/android/gradle.properties b/example/android/gradle.properties
new file mode 100644
index 0000000..94adc3a
--- /dev/null
+++ b/example/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..aa49780
--- /dev/null
+++ b/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
diff --git a/example/android/settings.gradle b/example/android/settings.gradle
new file mode 100644
index 0000000..05e070b
--- /dev/null
+++ b/example/android/settings.gradle
@@ -0,0 +1,25 @@
+pluginManagement {
+ def flutterSdkPath = {
+ def properties = new Properties()
+ file("local.properties").withInputStream { properties.load(it) }
+ def flutterSdkPath = properties.getProperty("flutter.sdk")
+ assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+ return flutterSdkPath
+ }()
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "7.3.0" apply false
+ id "org.jetbrains.kotlin.android" version "1.8.0" apply false
+}
+
+include ":app"
\ No newline at end of file
diff --git a/example/assets/icon.png b/example/assets/icon.png
new file mode 100644
index 0000000..0573069
Binary files /dev/null and b/example/assets/icon.png differ
diff --git a/example/ios/.gitignore b/example/ios/.gitignore
new file mode 100644
index 0000000..7a7f987
--- /dev/null
+++ b/example/ios/.gitignore
@@ -0,0 +1,34 @@
+**/dgph
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..7c56964
--- /dev/null
+++ b/example/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 12.0
+
+
diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..ec97fc6
--- /dev/null
+++ b/example/ios/Flutter/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"
diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..c4855bf
--- /dev/null
+++ b/example/ios/Flutter/Release.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"
diff --git a/example/ios/Podfile b/example/ios/Podfile
new file mode 100644
index 0000000..d523f41
--- /dev/null
+++ b/example/ios/Podfile
@@ -0,0 +1,101 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '12.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+ use_modular_headers!
+
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+
+
+ # Start of the permission_handler configuration
+ target.build_configurations.each do |config|
+
+ # You can enable the permissions needed here. For example to enable camera
+ # permission, just remove the `#` character in front so it looks like this:
+ #
+ # ## dart: PermissionGroup.camera
+ # 'PERMISSION_CAMERA=1'
+ #
+ # Preprocessor definitions can be found in: https://github.com/Baseflow/flutter-permission-handler/blob/master/permission_handler_apple/ios/Classes/PermissionHandlerEnums.h
+ config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
+ '$(inherited)',
+
+ ## dart: PermissionGroup.calendar
+ # 'PERMISSION_EVENTS=1',
+
+ ## dart: PermissionGroup.reminders
+ # 'PERMISSION_REMINDERS=1',
+
+ ## dart: PermissionGroup.contacts
+ # 'PERMISSION_CONTACTS=1',
+
+ ## dart: PermissionGroup.camera
+ 'PERMISSION_CAMERA=1',
+
+ ## dart: PermissionGroup.microphone
+ 'PERMISSION_MICROPHONE=1',
+
+ ## dart: PermissionGroup.speech
+ # 'PERMISSION_SPEECH_RECOGNIZER=1',
+
+ ## dart: PermissionGroup.photos
+ # 'PERMISSION_PHOTOS=1',
+
+ ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
+ # 'PERMISSION_LOCATION=1',
+
+ ## dart: PermissionGroup.notification
+ # 'PERMISSION_NOTIFICATIONS=1',
+
+ ## dart: PermissionGroup.mediaLibrary
+ # 'PERMISSION_MEDIA_LIBRARY=1',
+
+ ## dart: PermissionGroup.sensors
+ # 'PERMISSION_SENSORS=1',
+
+ ## dart: PermissionGroup.bluetooth
+ # 'PERMISSION_BLUETOOTH=1',
+
+ ## dart: PermissionGroup.appTrackingTransparency
+ # 'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
+
+ ## dart: PermissionGroup.criticalAlerts
+ # 'PERMISSION_CRITICAL_ALERTS=1'
+ ]
+
+ end
+ # End of the permission_handler configuration
+ end
+end
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
new file mode 100644
index 0000000..65fec55
--- /dev/null
+++ b/example/ios/Podfile.lock
@@ -0,0 +1,34 @@
+PODS:
+ - barcode_kit (0.0.1):
+ - Flutter
+ - Flutter (1.0.0)
+ - native_device_orientation (0.0.1):
+ - Flutter
+ - permission_handler_apple (9.3.0):
+ - Flutter
+
+DEPENDENCIES:
+ - barcode_kit (from `.symlinks/plugins/barcode_kit/ios`)
+ - Flutter (from `Flutter`)
+ - native_device_orientation (from `.symlinks/plugins/native_device_orientation/ios`)
+ - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
+
+EXTERNAL SOURCES:
+ barcode_kit:
+ :path: ".symlinks/plugins/barcode_kit/ios"
+ Flutter:
+ :path: Flutter
+ native_device_orientation:
+ :path: ".symlinks/plugins/native_device_orientation/ios"
+ permission_handler_apple:
+ :path: ".symlinks/plugins/permission_handler_apple/ios"
+
+SPEC CHECKSUMS:
+ barcode_kit: fa9db39e48737deac1e76dbca206f41e60656d4a
+ Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
+ native_device_orientation: 348b10c346a60ebbc62fb235a4fdb5d1b61a8f55
+ permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
+
+PODFILE CHECKSUM: af58d951dbef8da019280072eabcbd9dcc50cf6f
+
+COCOAPODS: 1.15.2
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..2adc595
--- /dev/null
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,572 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+ E87288FC5943E3C48E6481E9 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E62CA51F876661F3917C3EFF /* Pods_Runner.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 18C0A378C5E28B553D3B81FE /* 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 = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 6FF6BC1CFD3063BC3E87D998 /* 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 = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ AB952FDCDF3C26FA9834E8A7 /* 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 = ""; };
+ E62CA51F876661F3917C3EFF /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ E87288FC5943E3C48E6481E9 /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 0CA954CD32B5BB53D663B2A9 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 6FF6BC1CFD3063BC3E87D998 /* Pods-Runner.debug.xcconfig */,
+ 18C0A378C5E28B553D3B81FE /* Pods-Runner.release.xcconfig */,
+ AB952FDCDF3C26FA9834E8A7 /* Pods-Runner.profile.xcconfig */,
+ );
+ path = Pods;
+ sourceTree = "";
+ };
+ 0D9BB62104A2B4D9C00AD40E /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ E62CA51F876661F3917C3EFF /* Pods_Runner.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ 0CA954CD32B5BB53D663B2A9 /* Pods */,
+ 0D9BB62104A2B4D9C00AD40E /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 3448611280957658ABD37AF1 /* [CP] Check Pods Manifest.lock */,
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ 0422542D11D4C950636A01A0 /* [CP] Embed Pods Frameworks */,
+ 1064C05A1619B506B1125AB5 /* [CP] Copy Pods Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1510;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 0422542D11D4C950636A01A0 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 1064C05A1619B506B1125AB5 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 3448611280957658ABD37AF1 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ 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";
+ showEnvVarsInLog = 0;
+ };
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 2KG63XA6XL;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.emddigital.barcodeKitExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 2KG63XA6XL;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.emddigital.barcodeKitExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 2KG63XA6XL;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.emddigital.barcodeKitExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 249021D3217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 249021D4217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..5e31d3d
--- /dev/null
+++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..21a3cc1
--- /dev/null
+++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift
new file mode 100644
index 0000000..b636303
--- /dev/null
+++ b/example/ios/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+import UIKit
+import Flutter
+
+@main
+@objc class AppDelegate: FlutterAppDelegate {
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+ GeneratedPluginRegistrant.register(with: self)
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
+}
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..eabd851
--- /dev/null
+++ b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images": [
+ {
+ "filename": "Icon-App-20x20@2x.png",
+ "idiom": "iphone",
+ "scale": "2x",
+ "size": "20x20"
+ },
+ {
+ "filename": "Icon-App-20x20@3x.png",
+ "idiom": "iphone",
+ "scale": "3x",
+ "size": "20x20"
+ },
+ {
+ "filename": "Icon-App-29x29@1x.png",
+ "idiom": "iphone",
+ "scale": "1x",
+ "size": "29x29"
+ },
+ {
+ "filename": "Icon-App-29x29@2x.png",
+ "idiom": "iphone",
+ "scale": "2x",
+ "size": "29x29"
+ },
+ {
+ "filename": "Icon-App-29x29@3x.png",
+ "idiom": "iphone",
+ "scale": "3x",
+ "size": "29x29"
+ },
+ {
+ "filename": "Icon-App-40x40@2x.png",
+ "idiom": "iphone",
+ "scale": "2x",
+ "size": "40x40"
+ },
+ {
+ "filename": "Icon-App-40x40@3x.png",
+ "idiom": "iphone",
+ "scale": "3x",
+ "size": "40x40"
+ },
+ {
+ "filename": "Icon-App-60x60@2x.png",
+ "idiom": "iphone",
+ "scale": "2x",
+ "size": "60x60"
+ },
+ {
+ "filename": "Icon-App-60x60@3x.png",
+ "idiom": "iphone",
+ "scale": "3x",
+ "size": "60x60"
+ },
+ {
+ "filename": "Icon-App-20x20@1x.png",
+ "idiom": "ipad",
+ "scale": "1x",
+ "size": "20x20"
+ },
+ {
+ "filename": "Icon-App-20x20@2x.png",
+ "idiom": "ipad",
+ "scale": "2x",
+ "size": "20x20"
+ },
+ {
+ "filename": "Icon-App-29x29@1x.png",
+ "idiom": "ipad",
+ "scale": "1x",
+ "size": "29x29"
+ },
+ {
+ "filename": "Icon-App-29x29@2x.png",
+ "idiom": "ipad",
+ "scale": "2x",
+ "size": "29x29"
+ },
+ {
+ "filename": "Icon-App-40x40@1x.png",
+ "idiom": "ipad",
+ "scale": "1x",
+ "size": "40x40"
+ },
+ {
+ "filename": "Icon-App-40x40@2x.png",
+ "idiom": "ipad",
+ "scale": "2x",
+ "size": "40x40"
+ },
+ {
+ "filename": "Icon-App-76x76@1x.png",
+ "idiom": "ipad",
+ "scale": "1x",
+ "size": "76x76"
+ },
+ {
+ "filename": "Icon-App-76x76@2x.png",
+ "idiom": "ipad",
+ "scale": "2x",
+ "size": "76x76"
+ },
+ {
+ "filename": "Icon-App-83.5x83.5@2x.png",
+ "idiom": "ipad",
+ "scale": "2x",
+ "size": "83.5x83.5"
+ },
+ {
+ "filename": "Icon-App-1024x1024@1x.png",
+ "idiom": "ios-marketing",
+ "scale": "1x",
+ "size": "1024x1024"
+ }
+ ],
+ "info": {
+ "author": "icons_launcher",
+ "version": 1
+ }
+}
\ No newline at end of file
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 0000000..a716d92
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000..c754722
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000..0685377
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000..d9e50e1
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000..75d1bca
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000..3e679c9
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000..27df148
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000..0685377
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000..fdf2ccb
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000..f901e5e
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000..f901e5e
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000..b527ca9
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000..a92d0df
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000..c681b21
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000..faaec31
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000..9da19ea
Binary files /dev/null and b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/example/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist
new file mode 100644
index 0000000..d859b61
--- /dev/null
+++ b/example/ios/Runner/Info.plist
@@ -0,0 +1,53 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Barcode Kit
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ barcode_kit_example
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+ CADisableMinimumFrameDurationOnPhone
+
+ NSCameraUsageDescription
+ Used for reading barcodes
+ UIApplicationSupportsIndirectInputEvents
+
+
+
diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 0000000..308a2a5
--- /dev/null
+++ b/example/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/example/lib/main.dart b/example/lib/main.dart
new file mode 100644
index 0000000..ac68d7c
--- /dev/null
+++ b/example/lib/main.dart
@@ -0,0 +1,286 @@
+import 'package:mtrust_barcode_kit/mtrust_barcode_kit.dart';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+
+class _Ui extends BarcodeKitUiBuilder {
+ @override
+ Widget buildCameraNotAvailable(BuildContext context) {
+ return const Stack(
+ children: [
+ Center(
+ child: Text("Camera not available"),
+ )
+ ],
+ );
+ }
+
+ @override
+ Widget buildCameraNotOpened(BuildContext context) {
+ return const Stack(
+ children: [
+ Center(
+ child: CircularProgressIndicator(),
+ )
+ ],
+ );
+ }
+
+ @override
+ Widget buildNoPermission(
+ BuildContext context, Function() onSettingsRequested) {
+ return Stack(
+ children: [
+ Center(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Text("No permissions granted"),
+ ElevatedButton(
+ onPressed: () async {
+ onSettingsRequested();
+ },
+ child: const Text("Open settings"),
+ )
+ ],
+ ),
+ )
+ ],
+ );
+ }
+
+ @override
+ Widget buildRequestPermission(
+ BuildContext context, Function() onPermissionRequested) {
+ return Stack(
+ children: [
+ Center(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Text("We need permissions to use the camera"),
+ ElevatedButton(
+ onPressed: () async {
+ onPermissionRequested();
+ },
+ child: const Text("Request permissions"),
+ )
+ ],
+ ),
+ )
+ ],
+ );
+ }
+}
+
+void main() {
+ runApp(const MaterialApp(
+ home: BarcodeKitDemo(),
+ ));
+}
+
+class BarcodeKitDemo extends StatefulWidget {
+ const BarcodeKitDemo({
+ super.key,
+ });
+
+ @override
+ State createState() => _BarcodeKitDemoState();
+}
+
+class _BarcodeKitDemoState extends State {
+ bool _paused = false;
+ bool _rotate = false;
+ bool _ocr = false;
+ final Set _formats = {BarcodeFormat.dataMatrix};
+
+ final List _barcodes = [];
+
+ final List _texts = [];
+
+ @override
+ void initState() {
+ super.initState();
+ }
+
+ void _onTextDetected(String text) {
+ setState(() {
+ _texts.add(text);
+ });
+ }
+
+ void _onBarcodeScanned(DetectedBarcode barcode) {
+ setState(() {
+ _paused = true;
+ });
+
+ setState(() {
+ _barcodes.add(barcode);
+ });
+
+ HapticFeedback.mediumImpact();
+
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text(
+ "Scanned barcode ${barcode.rawValue?.substring(0, 10)} ${barcode.format}",
+ ),
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ extendBodyBehindAppBar: true,
+ body: BarcodeKitView(
+ formats: _formats,
+ cameraFit: BoxFit.cover,
+ maskHeight: 200,
+ maskWidth: 200,
+ onTextDetected: _onTextDetected,
+ widgetAboveMask: Center(
+ child: Text(
+ "Look for the barcode",
+ style: Theme.of(context)
+ .textTheme
+ .headlineMedium!
+ .copyWith(color: Colors.white),
+ ),
+ ),
+ widgetBelowMask: Center(
+ child: Text(
+ "Approach barcode with camera 📸",
+ style: Theme.of(context)
+ .textTheme
+ .bodyLarge!
+ .copyWith(color: Colors.white),
+ ),
+ ),
+ paused: _paused,
+ maskAdditionpauseOpacity: 0.2,
+ enableOCR: _ocr,
+ followRotation: _rotate,
+ onBarcodeScanned: (barcode) {
+ _onBarcodeScanned(barcode);
+ },
+ uiBuilder: _Ui(),
+ ),
+ bottomNavigationBar: Container(
+ decoration: const BoxDecoration(color: Colors.white),
+ padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 8.0),
+ child: SafeArea(
+ child: Row(
+ children: [
+ ElevatedButton(
+ child:
+ _paused ? const Text("Start Scan") : const Text("Pause"),
+ onPressed: () {
+ setState(() {
+ _paused = !_paused;
+ });
+ },
+ ),
+ const Spacer(),
+ Text(_texts.lastOrNull ?? ""),
+ IconButton(
+ icon: Icon(_rotate
+ ? Icons.screen_rotation
+ : Icons.screen_lock_rotation),
+ onPressed: () {
+ setState(() {
+ _rotate = !_rotate;
+ });
+ }),
+ IconButton(
+ icon: Icon(_ocr ? Icons.text_format : Icons.text_decrease),
+ onPressed: () {
+ setState(() {
+ _ocr = !_ocr;
+ });
+
+ if (_ocr) {
+ ScaffoldMessenger.of(context).clearSnackBars();
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(
+ content: Text(
+ "OCR enabled",
+ ),
+ ),
+ );
+ } else {
+ ScaffoldMessenger.of(context).clearSnackBars();
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(
+ content: Text(
+ "OCR disabled",
+ ),
+ ),
+ );
+ }
+ }),
+ IconButton(
+ onPressed: () {
+ Navigator.of(context).push(MaterialPageRoute(
+ builder: (context) => _Settings(
+ formats: _formats,
+ onFormatsChanged: (formats) {
+ setState(() {});
+ })));
+ },
+ icon: const Icon(Icons.settings))
+ ],
+ ),
+ ),
+ ));
+ }
+}
+
+class _Settings extends StatefulWidget {
+ final Set formats;
+ final Function(Set) onFormatsChanged;
+
+ const _Settings({
+ required this.formats,
+ required this.onFormatsChanged,
+ });
+
+ @override
+ State<_Settings> createState() => _SettingsState();
+}
+
+class _SettingsState extends State<_Settings> {
+ Set _formats = {};
+
+ @override
+ void initState() {
+ _formats = widget.formats;
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text("Settings"),
+ ),
+ body: ListView(
+ children: BarcodeFormat.values
+ .map(
+ (e) => CheckboxListTile(
+ title: Text(e.toString()),
+ value: _formats.contains(e),
+ onChanged: (value) {
+ setState(() {
+ if (value == false) {
+ _formats.remove(e);
+ } else {
+ _formats.add(e);
+ }
+ });
+ widget.onFormatsChanged(_formats);
+ }),
+ )
+ .toList()));
+ }
+}
diff --git a/example/pubspec.lock b/example/pubspec.lock
new file mode 100644
index 0000000..6179681
--- /dev/null
+++ b/example/pubspec.lock
@@ -0,0 +1,401 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ archive:
+ dependency: transitive
+ description:
+ name: archive
+ sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.6.1"
+ args:
+ dependency: transitive
+ description:
+ name: args
+ sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.0"
+ async:
+ dependency: transitive
+ description:
+ name: async
+ sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.11.0"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
+ characters:
+ dependency: transitive
+ description:
+ name: characters
+ sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ collection:
+ dependency: transitive
+ description:
+ name: collection
+ sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.18.0"
+ crypto:
+ dependency: transitive
+ description:
+ name: crypto
+ sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.3"
+ cupertino_icons:
+ dependency: "direct main"
+ description:
+ name: cupertino_icons
+ sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.8"
+ equatable:
+ dependency: "direct main"
+ description:
+ name: equatable
+ sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.5"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.1"
+ flutter:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_animate:
+ dependency: "direct main"
+ description:
+ name: flutter_animate
+ sha256: "7c8a6594a9252dad30cc2ef16e33270b6248c4dedc3b3d06c86c4f3f4dc05ae5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.5.0"
+ flutter_lints:
+ dependency: "direct dev"
+ description:
+ name: flutter_lints
+ sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.3"
+ flutter_portal:
+ dependency: "direct main"
+ description:
+ name: flutter_portal
+ sha256: "4601b3dc24f385b3761721bd852a3f6c09cddd4e943dd184ed58ee1f43006257"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.4"
+ flutter_shaders:
+ dependency: transitive
+ description:
+ name: flutter_shaders
+ sha256: "02750b545c01ff4d8e9bbe8f27a7731aa3778402506c67daa1de7f5fc3f4befe"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.1.2"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_web_plugins:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ icons_launcher:
+ dependency: "direct dev"
+ description:
+ name: icons_launcher
+ sha256: "9b514ffed6ed69b232fd2bf34c44878c8526be71fc74129a658f35c04c9d4a9d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.7"
+ image:
+ dependency: transitive
+ description:
+ name: image
+ sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.2.0"
+ leak_tracker:
+ dependency: transitive
+ description:
+ name: leak_tracker
+ sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
+ url: "https://pub.dev"
+ source: hosted
+ version: "10.0.5"
+ leak_tracker_flutter_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_flutter_testing
+ sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.5"
+ leak_tracker_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_testing
+ sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.1"
+ lints:
+ dependency: transitive
+ description:
+ name: lints
+ sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.12.16+1"
+ material_color_utilities:
+ dependency: transitive
+ description:
+ name: material_color_utilities
+ sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.11.1"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.15.0"
+ mtrust_barcode_kit:
+ dependency: "direct main"
+ description:
+ path: ".."
+ relative: true
+ source: path
+ version: "2.0.1"
+ native_device_orientation:
+ dependency: transitive
+ description:
+ name: native_device_orientation
+ sha256: "0c330c068575e4be72cce5968ca479a3f8d5d1e5dfce7d89d5c13a1e943b338c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.3"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.9.0"
+ permission_handler:
+ dependency: transitive
+ description:
+ name: permission_handler
+ sha256: "18bf33f7fefbd812f37e72091a15575e72d5318854877e0e4035a24ac1113ecb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "11.3.1"
+ permission_handler_android:
+ dependency: transitive
+ description:
+ name: permission_handler_android
+ sha256: "8bb852cd759488893805c3161d0b2b5db55db52f773dbb014420b304055ba2c5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "12.0.6"
+ permission_handler_apple:
+ dependency: transitive
+ description:
+ name: permission_handler_apple
+ sha256: e6f6d73b12438ef13e648c4ae56bd106ec60d17e90a59c4545db6781229082a0
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.4.5"
+ permission_handler_html:
+ dependency: transitive
+ description:
+ name: permission_handler_html
+ sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.1.1"
+ permission_handler_platform_interface:
+ dependency: transitive
+ description:
+ name: permission_handler_platform_interface
+ sha256: "48d4fcf201a1dad93ee869ab0d4101d084f49136ec82a8a06ed9cfeacab9fd20"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.2.1"
+ permission_handler_windows:
+ dependency: transitive
+ description:
+ name: permission_handler_windows
+ sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.1"
+ petitparser:
+ dependency: transitive
+ description:
+ name: petitparser
+ sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.2"
+ plugin_platform_interface:
+ dependency: transitive
+ description:
+ name: plugin_platform_interface
+ sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.8"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.99"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.10.0"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.11.1"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.0"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.1"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.7.2"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.2"
+ universal_io:
+ dependency: transitive
+ description:
+ name: universal_io
+ sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.2"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.4"
+ vm_service:
+ dependency: transitive
+ description:
+ name: vm_service
+ sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "14.2.5"
+ xml:
+ dependency: transitive
+ description:
+ name: xml
+ sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.5.0"
+ yaml:
+ dependency: transitive
+ description:
+ name: yaml
+ sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.2"
+sdks:
+ dart: ">=3.3.1 <4.0.0"
+ flutter: ">=3.18.0-18.0.pre.54"
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
new file mode 100644
index 0000000..f2f9a44
--- /dev/null
+++ b/example/pubspec.yaml
@@ -0,0 +1,90 @@
+name: barcode_kit_example
+description: Demonstrates how to use the barcode_kit plugin.
+
+# The following line prevents the package from being accidentally published to
+# pub.dev using `flutter pub publish`. This is preferred for private packages.
+publish_to: "none" # Remove this line if you wish to publish to pub.dev
+
+environment:
+ sdk: ">=3.3.1 <4.0.0"
+ flutter: ">=3.3.0"
+
+# Dependencies specify other packages that your package needs in order to work.
+# To automatically upgrade your package dependencies to the latest versions
+# consider running `flutter pub upgrade --major-versions`. Alternatively,
+# dependencies can be manually updated by changing the version numbers below to
+# the latest version available on pub.dev. To see which dependencies have newer
+# versions available, run `flutter pub outdated`.
+dependencies:
+ flutter:
+ sdk: flutter
+
+ mtrust_barcode_kit:
+ path: ../
+
+ # The following adds the Cupertino Icons font to your application.
+ # Use with the CupertinoIcons class for iOS style icons.
+ cupertino_icons: ^1.0.2
+ equatable:
+ flutter_animate: ^4.1.0
+ flutter_portal: ^1.1.4
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+ # The "flutter_lints" package below contains a set of recommended lints to
+ # encourage good coding practices. The lint set provided by the package is
+ # activated in the `analysis_options.yaml` file located at the root of your
+ # package. See that file for information about deactivating specific lint
+ # rules and activating additional ones.
+ flutter_lints: ^2.0.0
+ icons_launcher: ^2.1.7
+
+icons_launcher:
+ image_path: "assets/icon.png"
+ platforms:
+ android:
+ enable: true
+ ios:
+ enable: true
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the material Icons class.
+ uses-material-design: true
+
+ # To add assets to your application, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/assets-and-images/#resolution-aware
+
+ # For details regarding adding assets from package dependencies, see
+ # https://flutter.dev/assets-and-images/#from-packages
+
+ # To add custom fonts to your application, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts from package dependencies,
+ # see https://flutter.dev/custom-fonts/#from-packages
diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart
new file mode 100644
index 0000000..0cc1320
--- /dev/null
+++ b/example/test/widget_test.dart
@@ -0,0 +1,24 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility in the flutter_test package. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ testWidgets('Verify Platform version', (WidgetTester tester) async {
+ // Build our app and trigger a frame.
+
+ // Verify that platform version is retrieved.
+ expect(
+ find.byWidgetPredicate(
+ (Widget widget) =>
+ widget is Text && widget.data!.startsWith('Running on:'),
+ ),
+ findsOneWidget,
+ );
+ });
+}
diff --git a/generate.sh b/generate.sh
new file mode 100755
index 0000000..9a019e5
--- /dev/null
+++ b/generate.sh
@@ -0,0 +1,6 @@
+flutter pub run pigeon \
+ --input pigeons/barcode_kit_pigeon.dart \
+ --dart_out lib/src/pigeon.dart \
+ --experimental_swift_out ios/Classes/Pigeon.swift \
+ --experimental_kotlin_out android/src/main/kotlin/com/emddigital/barcode_kit/pigeon/Pigeon.kt \
+ --java_package "com.emddigital.barcode_kit.pigeon" \
\ No newline at end of file
diff --git a/ios/.gitignore b/ios/.gitignore
new file mode 100644
index 0000000..0c88507
--- /dev/null
+++ b/ios/.gitignore
@@ -0,0 +1,38 @@
+.idea/
+.vagrant/
+.sconsign.dblite
+.svn/
+
+.DS_Store
+*.swp
+profile
+
+DerivedData/
+build/
+GeneratedPluginRegistrant.h
+GeneratedPluginRegistrant.m
+
+.generated/
+
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+
+!default.pbxuser
+!default.mode1v3
+!default.mode2v3
+!default.perspectivev3
+
+xcuserdata
+
+*.moved-aside
+
+*.pyc
+*sync/
+Icon?
+.tags*
+
+/Flutter/Generated.xcconfig
+/Flutter/ephemeral/
+/Flutter/flutter_export_environment.sh
\ No newline at end of file
diff --git a/ios/Assets/.gitkeep b/ios/Assets/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/ios/Classes/AVMetaDataExtension.swift b/ios/Classes/AVMetaDataExtension.swift
new file mode 100644
index 0000000..c2d959f
--- /dev/null
+++ b/ios/Classes/AVMetaDataExtension.swift
@@ -0,0 +1,38 @@
+//
+// AVMetaDataExtension.swift
+// barcode_kit
+//
+// Created by Yannick Stolle on 27.07.23.
+//
+
+import Foundation
+import AVKit
+
+// This extension is based upon https://www.thonky.com/qr-code-tutorial/data-encoding
+extension AVMetadataMachineReadableCodeObject
+{
+
+
+ var rawValue: Data?
+ {
+ guard let descriptor = descriptor else
+ {
+ return nil
+ }
+ switch type
+ {
+ case .qr:
+ return (descriptor as! CIQRCodeDescriptor).errorCorrectedPayload
+ case .aztec:
+ return (descriptor as! CIAztecCodeDescriptor).errorCorrectedPayload
+ case .pdf417:
+ return (descriptor as! CIPDF417CodeDescriptor).errorCorrectedPayload
+ case .dataMatrix:
+ return (descriptor as! CIDataMatrixCodeDescriptor).errorCorrectedPayload
+ default:
+ return stringValue?.data(using: String.Encoding.utf8)
+ }
+ }
+
+
+}
diff --git a/ios/Classes/BarcodeKitPlugin.h b/ios/Classes/BarcodeKitPlugin.h
new file mode 100644
index 0000000..163f6a7
--- /dev/null
+++ b/ios/Classes/BarcodeKitPlugin.h
@@ -0,0 +1,4 @@
+#import
+
+@interface BarcodeKitPlugin : NSObject
+@end
diff --git a/ios/Classes/BarcodeKitPlugin.m b/ios/Classes/BarcodeKitPlugin.m
new file mode 100644
index 0000000..b21b967
--- /dev/null
+++ b/ios/Classes/BarcodeKitPlugin.m
@@ -0,0 +1,15 @@
+#import "BarcodeKitPlugin.h"
+#if __has_include()
+#import
+#else
+// Support project import fallback if the generated compatibility header
+// is not copied when this plugin is created as a library.
+// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
+#import "barcode_kit-Swift.h"
+#endif
+
+@implementation BarcodeKitPlugin
++ (void)registerWithRegistrar:(NSObject*)registrar {
+ [SwiftBarcodeKitPlugin registerWithRegistrar:registrar];
+}
+@end
diff --git a/ios/Classes/Pigeon.swift b/ios/Classes/Pigeon.swift
new file mode 100644
index 0000000..e772ee9
--- /dev/null
+++ b/ios/Classes/Pigeon.swift
@@ -0,0 +1,358 @@
+// Autogenerated from Pigeon (v10.1.6), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+
+import Foundation
+#if os(iOS)
+import Flutter
+#elseif os(macOS)
+import FlutterMacOS
+#else
+#error("Unsupported platform.")
+#endif
+
+private func wrapResult(_ result: Any?) -> [Any?] {
+ return [result]
+}
+
+private func wrapError(_ error: Any) -> [Any?] {
+ if let flutterError = error as? FlutterError {
+ return [
+ flutterError.code,
+ flutterError.message,
+ flutterError.details
+ ]
+ }
+ return [
+ "\(error)",
+ "\(type(of: error))",
+ "Stacktrace: \(Thread.callStackSymbols)"
+ ]
+}
+
+private func nilOrValue(_ value: Any?) -> T? {
+ if value is NSNull { return nil }
+ return value as! T?
+}
+
+enum BarcodeFormat: Int {
+ case aztec = 0
+ case codabar = 1
+ case code39 = 2
+ case code93 = 3
+ case code128 = 4
+ case dataMatrix = 5
+ case ean8 = 6
+ case ean13 = 7
+ case pdf417 = 8
+ case qrCode = 9
+ case upcA = 10
+ case upcE = 11
+ case itf = 12
+}
+
+enum CameraLensDirection: Int {
+ case front = 0
+ case back = 1
+ case ext = 2
+ case unknown = 3
+}
+
+/// Generated class from Pigeon that represents data sent in messages.
+struct CameraOpenResponse {
+ var supportsFlash: Bool? = nil
+ var height: Int64? = nil
+ var width: Int64? = nil
+ var textureId: String? = nil
+
+ static func fromList(_ list: [Any?]) -> CameraOpenResponse? {
+ let supportsFlash: Bool? = nilOrValue(list[0])
+ let height: Int64? = list[1] is NSNull ? nil : (list[1] is Int64? ? list[1] as! Int64? : Int64(list[1] as! Int32))
+ let width: Int64? = list[2] is NSNull ? nil : (list[2] is Int64? ? list[2] as! Int64? : Int64(list[2] as! Int32))
+ let textureId: String? = nilOrValue(list[3])
+
+ return CameraOpenResponse(
+ supportsFlash: supportsFlash,
+ height: height,
+ width: width,
+ textureId: textureId
+ )
+ }
+ func toList() -> [Any?] {
+ return [
+ supportsFlash,
+ height,
+ width,
+ textureId,
+ ]
+ }
+}
+
+/// Generated class from Pigeon that represents data sent in messages.
+struct DetectedBarcode {
+ var rawValue: String? = nil
+ var cornerPoints: [CornerPoint?]? = nil
+ var format: BarcodeFormat? = nil
+ var textValue: String? = nil
+
+ static func fromList(_ list: [Any?]) -> DetectedBarcode? {
+ let rawValue: String? = nilOrValue(list[0])
+ let cornerPoints: [CornerPoint?]? = nilOrValue(list[1])
+ var format: BarcodeFormat? = nil
+ let formatEnumVal: Int? = nilOrValue(list[2])
+ if let formatRawValue = formatEnumVal {
+ format = BarcodeFormat(rawValue: formatRawValue)!
+ }
+ let textValue: String? = nilOrValue(list[3])
+
+ return DetectedBarcode(
+ rawValue: rawValue,
+ cornerPoints: cornerPoints,
+ format: format,
+ textValue: textValue
+ )
+ }
+ func toList() -> [Any?] {
+ return [
+ rawValue,
+ cornerPoints,
+ format?.rawValue,
+ textValue,
+ ]
+ }
+}
+
+/// Generated class from Pigeon that represents data sent in messages.
+struct CornerPoint {
+ var x: Double
+ var y: Double
+
+ static func fromList(_ list: [Any?]) -> CornerPoint? {
+ let x = list[0] as! Double
+ let y = list[1] as! Double
+
+ return CornerPoint(
+ x: x,
+ y: y
+ )
+ }
+ func toList() -> [Any?] {
+ return [
+ x,
+ y,
+ ]
+ }
+}
+
+private class BarcodeKitHostApiCodecReader: FlutterStandardReader {
+ override func readValue(ofType type: UInt8) -> Any? {
+ switch type {
+ case 128:
+ return CameraOpenResponse.fromList(self.readValue() as! [Any?])
+ default:
+ return super.readValue(ofType: type)
+ }
+ }
+}
+
+private class BarcodeKitHostApiCodecWriter: FlutterStandardWriter {
+ override func writeValue(_ value: Any) {
+ if let value = value as? CameraOpenResponse {
+ super.writeByte(128)
+ super.writeValue(value.toList())
+ } else {
+ super.writeValue(value)
+ }
+ }
+}
+
+private class BarcodeKitHostApiCodecReaderWriter: FlutterStandardReaderWriter {
+ override func reader(with data: Data) -> FlutterStandardReader {
+ return BarcodeKitHostApiCodecReader(data: data)
+ }
+
+ override func writer(with data: NSMutableData) -> FlutterStandardWriter {
+ return BarcodeKitHostApiCodecWriter(data: data)
+ }
+}
+
+class BarcodeKitHostApiCodec: FlutterStandardMessageCodec {
+ static let shared = BarcodeKitHostApiCodec(readerWriter: BarcodeKitHostApiCodecReaderWriter())
+}
+
+/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
+protocol BarcodeKitHostApi {
+ func openCamera(direction: CameraLensDirection, formats: [Int64], completion: @escaping (Result) -> Void)
+ func setOCREnabled(enabled: Bool) throws
+ func closeCamera() throws
+ func pauseCamera() throws
+ func resumeCamera() throws
+ func setTorch(enabled: Bool) throws
+}
+
+/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
+class BarcodeKitHostApiSetup {
+ /// The codec used by BarcodeKitHostApi.
+ static var codec: FlutterStandardMessageCodec { BarcodeKitHostApiCodec.shared }
+ /// Sets up an instance of `BarcodeKitHostApi` to handle messages through the `binaryMessenger`.
+ static func setUp(binaryMessenger: FlutterBinaryMessenger, api: BarcodeKitHostApi?) {
+ let openCameraChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.barcode_kit.BarcodeKitHostApi.openCamera", binaryMessenger: binaryMessenger, codec: codec)
+ if let api = api {
+ openCameraChannel.setMessageHandler { message, reply in
+ let args = message as! [Any?]
+ let directionArg = CameraLensDirection(rawValue: args[0] as! Int)!
+ let formatsArg = args[1] as! [Int64]
+ api.openCamera(direction: directionArg, formats: formatsArg) { result in
+ switch result {
+ case .success(let res):
+ reply(wrapResult(res))
+ case .failure(let error):
+ reply(wrapError(error))
+ }
+ }
+ }
+ } else {
+ openCameraChannel.setMessageHandler(nil)
+ }
+ let setOCREnabledChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.barcode_kit.BarcodeKitHostApi.setOCREnabled", binaryMessenger: binaryMessenger, codec: codec)
+ if let api = api {
+ setOCREnabledChannel.setMessageHandler { message, reply in
+ let args = message as! [Any?]
+ let enabledArg = args[0] as! Bool
+ do {
+ try api.setOCREnabled(enabled: enabledArg)
+ reply(wrapResult(nil))
+ } catch {
+ reply(wrapError(error))
+ }
+ }
+ } else {
+ setOCREnabledChannel.setMessageHandler(nil)
+ }
+ let closeCameraChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.barcode_kit.BarcodeKitHostApi.closeCamera", binaryMessenger: binaryMessenger, codec: codec)
+ if let api = api {
+ closeCameraChannel.setMessageHandler { _, reply in
+ do {
+ try api.closeCamera()
+ reply(wrapResult(nil))
+ } catch {
+ reply(wrapError(error))
+ }
+ }
+ } else {
+ closeCameraChannel.setMessageHandler(nil)
+ }
+ let pauseCameraChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.barcode_kit.BarcodeKitHostApi.pauseCamera", binaryMessenger: binaryMessenger, codec: codec)
+ if let api = api {
+ pauseCameraChannel.setMessageHandler { _, reply in
+ do {
+ try api.pauseCamera()
+ reply(wrapResult(nil))
+ } catch {
+ reply(wrapError(error))
+ }
+ }
+ } else {
+ pauseCameraChannel.setMessageHandler(nil)
+ }
+ let resumeCameraChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.barcode_kit.BarcodeKitHostApi.resumeCamera", binaryMessenger: binaryMessenger, codec: codec)
+ if let api = api {
+ resumeCameraChannel.setMessageHandler { _, reply in
+ do {
+ try api.resumeCamera()
+ reply(wrapResult(nil))
+ } catch {
+ reply(wrapError(error))
+ }
+ }
+ } else {
+ resumeCameraChannel.setMessageHandler(nil)
+ }
+ let setTorchChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.barcode_kit.BarcodeKitHostApi.setTorch", binaryMessenger: binaryMessenger, codec: codec)
+ if let api = api {
+ setTorchChannel.setMessageHandler { message, reply in
+ let args = message as! [Any?]
+ let enabledArg = args[0] as! Bool
+ do {
+ try api.setTorch(enabled: enabledArg)
+ reply(wrapResult(nil))
+ } catch {
+ reply(wrapError(error))
+ }
+ }
+ } else {
+ setTorchChannel.setMessageHandler(nil)
+ }
+ }
+}
+private class BarcodeKitFlutterApiCodecReader: FlutterStandardReader {
+ override func readValue(ofType type: UInt8) -> Any? {
+ switch type {
+ case 128:
+ return CornerPoint.fromList(self.readValue() as! [Any?])
+ case 129:
+ return DetectedBarcode.fromList(self.readValue() as! [Any?])
+ default:
+ return super.readValue(ofType: type)
+ }
+ }
+}
+
+private class BarcodeKitFlutterApiCodecWriter: FlutterStandardWriter {
+ override func writeValue(_ value: Any) {
+ if let value = value as? CornerPoint {
+ super.writeByte(128)
+ super.writeValue(value.toList())
+ } else if let value = value as? DetectedBarcode {
+ super.writeByte(129)
+ super.writeValue(value.toList())
+ } else {
+ super.writeValue(value)
+ }
+ }
+}
+
+private class BarcodeKitFlutterApiCodecReaderWriter: FlutterStandardReaderWriter {
+ override func reader(with data: Data) -> FlutterStandardReader {
+ return BarcodeKitFlutterApiCodecReader(data: data)
+ }
+
+ override func writer(with data: NSMutableData) -> FlutterStandardWriter {
+ return BarcodeKitFlutterApiCodecWriter(data: data)
+ }
+}
+
+class BarcodeKitFlutterApiCodec: FlutterStandardMessageCodec {
+ static let shared = BarcodeKitFlutterApiCodec(readerWriter: BarcodeKitFlutterApiCodecReaderWriter())
+}
+
+/// Generated class from Pigeon that represents Flutter messages that can be called from Swift.
+class BarcodeKitFlutterApi {
+ private let binaryMessenger: FlutterBinaryMessenger
+ init(binaryMessenger: FlutterBinaryMessenger){
+ self.binaryMessenger = binaryMessenger
+ }
+ var codec: FlutterStandardMessageCodec {
+ return BarcodeKitFlutterApiCodec.shared
+ }
+ /// Caleld from the host when a barcode is detected.
+ func onBarcodeScanned(barcode barcodeArg: DetectedBarcode, completion: @escaping () -> Void) {
+ let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.barcode_kit.BarcodeKitFlutterApi.onBarcodeScanned", binaryMessenger: binaryMessenger, codec: codec)
+ channel.sendMessage([barcodeArg] as [Any?]) { _ in
+ completion()
+ }
+ }
+ /// Called from the host when text is detected.
+ func onTextDetected(text textArg: String, completion: @escaping () -> Void) {
+ let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.barcode_kit.BarcodeKitFlutterApi.onTextDetected", binaryMessenger: binaryMessenger, codec: codec)
+ channel.sendMessage([textArg] as [Any?]) { _ in
+ completion()
+ }
+ }
+ /// Called from the host when the torch state changes.
+ func onTorchStateChanged(enabled enabledArg: Bool, completion: @escaping () -> Void) {
+ let channel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.barcode_kit.BarcodeKitFlutterApi.onTorchStateChanged", binaryMessenger: binaryMessenger, codec: codec)
+ channel.sendMessage([enabledArg] as [Any?]) { _ in
+ completion()
+ }
+ }
+}
diff --git a/ios/Classes/SwiftBarcodeKitPlugin.swift b/ios/Classes/SwiftBarcodeKitPlugin.swift
new file mode 100644
index 0000000..d6718c0
--- /dev/null
+++ b/ios/Classes/SwiftBarcodeKitPlugin.swift
@@ -0,0 +1,324 @@
+import Flutter
+import AVFoundation
+import UIKit
+import Vision
+
+public class SwiftBarcodeKitPlugin: NSObject, FlutterPlugin , BarcodeKitHostApi, FlutterTexture, AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureMetadataOutputObjectsDelegate{
+
+
+ public func copyPixelBuffer() -> Unmanaged? {
+ if latestBuffer == nil {
+ return nil
+ }
+ return Unmanaged.passRetained(latestBuffer!)
+ }
+
+
+ let registry: FlutterTextureRegistry?
+ var sink: FlutterEventSink? = nil
+ var textureId: Int64? = nil
+ var captureSession: AVCaptureSession? = nil
+ var device: AVCaptureDevice? = nil
+ var latestBuffer: CVImageBuffer? = nil
+ var analyzeMode: Int = 0
+ var analyzing: Bool = false
+
+ var height: Int32 = 0
+ var width: Int32 = 0
+
+ private let metadataObjectsQueue = DispatchQueue(label: "metadata objects queue", attributes: [], target: nil)
+
+ private let visionQueue = DispatchQueue(label: "com.barcodekit.VisionQueue")
+ private let semaphore = DispatchSemaphore(value: 1)
+ private let metadataOutput = AVCaptureMetadataOutput()
+
+ var ocrEnabled = false
+
+
+ private let flutterApi: BarcodeKitFlutterApi
+
+ // Setup pigeon
+ public static func register(with registrar: FlutterPluginRegistrar) {
+ let messenger : FlutterBinaryMessenger = registrar.messenger()
+ BarcodeKitHostApiSetup.setUp(binaryMessenger: messenger, api: SwiftBarcodeKitPlugin(registrar.textures(), registrar: registrar));
+
+ }
+
+ init(_ registry: FlutterTextureRegistry, registrar: FlutterPluginRegistrar) {
+ self.registry = registry
+ analyzeMode = 0
+ flutterApi = BarcodeKitFlutterApi(binaryMessenger: registrar.messenger())
+ analyzing = false
+ super.init()
+ }
+
+
+
+ func setOCREnabled(enabled: Bool) throws {
+ ocrEnabled = enabled
+ }
+
+ // No op on iOS
+ func pauseCamera() {
+
+ }
+
+ // No op on iOS
+ func resumeCamera() {
+
+ }
+
+
+
+ func openCamera(direction: CameraLensDirection,formats: [Int64] , completion: @escaping (Result) -> Void) {
+ textureId = registry!.register(self)
+ captureSession = AVCaptureSession()
+
+ let position = direction == .front ? AVCaptureDevice.Position.front : .back
+
+
+ var devices: [AVCaptureDevice] = []
+
+ if #available(iOS 10.0, *) {
+ devices = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: .video, position: position).devices
+ } else {
+ devices = AVCaptureDevice.devices(for: .video).filter({$0.position == position})
+ }
+
+ if(devices.isEmpty){
+ completion(.failure(NSError(domain: "BarcodeKit", code: 1, userInfo: ["message": "No camera found"])))
+ return
+ }else{
+ device = devices.first
+ }
+
+ device!.addObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode), options: .new, context: nil)
+ captureSession!.beginConfiguration()
+ // Add device input.
+ do {
+ let input = try AVCaptureDeviceInput(device: device!)
+ captureSession!.addInput(input)
+ } catch {
+ completion(.failure(error))
+ }
+ // Add video output.
+ let videoOutput = AVCaptureVideoDataOutput()
+ videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
+ videoOutput.alwaysDiscardsLateVideoFrames = true
+ videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue.main)
+ captureSession!.addOutput(videoOutput)
+ for connection in videoOutput.connections {
+ connection.videoOrientation = .portrait
+ if position == .front && connection.isVideoMirroringSupported {
+ connection.isVideoMirrored = true
+ }
+ }
+
+
+ if(captureSession!.canAddOutput(metadataOutput)){
+ captureSession!.addOutput(metadataOutput)
+
+
+ if(formats.isEmpty){
+ metadataOutput.metadataObjectTypes = metadataOutput.availableMetadataObjectTypes
+ }else{
+ var objectTypes: [AVMetadataObject.ObjectType] = []
+
+
+ for formatItem in formats {
+ let objectType = barcodeMap[BarcodeFormat(rawValue: Int(formatItem))!]
+ if(objectType != nil){
+ objectTypes.append(objectType!)
+ }
+ }
+ metadataOutput.metadataObjectTypes = objectTypes
+ }
+
+
+
+ metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main )
+ print("Added capture output for metadata")
+ }
+
+
+ DispatchQueue.global(qos: .background).async {
+ self.captureSession!.commitConfiguration()
+ self.captureSession!.startRunning()
+
+ let dimensions = CMVideoFormatDescriptionGetDimensions(self.device!.activeFormat.formatDescription)
+ self.width = dimensions.height
+ self.height = dimensions.width
+
+ completion(.success(CameraOpenResponse(
+ supportsFlash: self.device!.hasTorch,
+ height: Int64(self.height),
+ width: Int64(self.width),
+ textureId: String(self.textureId!)
+
+
+ )))
+ }
+
+
+ }
+
+ var lastFrameTime: Double = 0
+
+ public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
+
+ guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
+ print("Failed to get pixel buffer from sample buffer")
+ return
+ }
+
+ latestBuffer = pixelBuffer
+
+ registry!.textureFrameAvailable(textureId!)
+
+ // Throttle frame processing to improve performance
+ let currentTime = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer))
+
+ let frameRateThrottle = 0.5 // Process 10 frames per second
+ if currentTime - lastFrameTime < frameRateThrottle {
+ return
+ }
+ lastFrameTime = currentTime
+
+ if #available(iOS 13.0, *) {
+
+ if ocrEnabled{
+
+ visionQueue.async {
+
+ self.semaphore.wait()
+ defer { self.semaphore.signal() }
+
+ // Create a request handler using the sample buffer's pixel buffer
+ let requestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
+
+ // Create a text detection request
+ let request = VNRecognizeTextRequest { (request, error) in
+ guard let observations = request.results as? [VNRecognizedTextObservation] else { return }
+
+ for observation in observations {
+ // Get the top recognized text
+ guard let topCandidate = observation.topCandidates(1).first else { continue }
+
+
+ DispatchQueue.main.async {
+ self.flutterApi.onTextDetected(text: topCandidate.string,completion: {
+
+ })
+ }
+
+ }
+ }
+
+ do {
+ // Perform the text recognition request
+ try requestHandler.perform([request])
+ } catch {
+ print("Failed to perform text recognition: \(error.localizedDescription)")
+ }
+
+ }
+ }
+ }
+ }
+
+ func closeCamera() throws {
+ if(captureSession != nil){
+ captureSession!.stopRunning()
+ for input in captureSession!.inputs {
+ captureSession!.removeInput(input)
+ }
+ for output in captureSession!.outputs {
+ captureSession!.removeOutput(output)
+ }
+ }
+ device?.removeObserver(self, forKeyPath: #keyPath(AVCaptureDevice.torchMode))
+ if(textureId != nil){
+ registry?.unregisterTexture(textureId!)
+ }
+
+
+ analyzeMode = 0
+ latestBuffer = nil
+ captureSession = nil
+ device = nil
+ textureId = nil
+
+
+ }
+
+
+
+ public func metadataOutput(_: AVCaptureMetadataOutput, didOutput: [AVMetadataObject], from: AVCaptureConnection){
+
+
+ for metadataObject in didOutput {
+ if let barcodeMetadataObject = metadataObject as? AVMetadataMachineReadableCodeObject {
+
+ let barcode = DetectedBarcode(
+ rawValue: barcodeMetadataObject.rawValue?.base64EncodedString(),
+ cornerPoints: barcodeMetadataObject.corners.map({cornerpoint in
+ return CornerPoint( x: CGFloat(width)-cornerpoint.y*CGFloat(width),y: cornerpoint.x*CGFloat(height))
+ }),
+ format: barcodeMap.first(where: { (key: BarcodeFormat, value: AVMetadataObject.ObjectType) in
+ value == barcodeMetadataObject.type
+ })?.key,
+ textValue: barcodeMetadataObject.stringValue
+ )
+ flutterApi.onBarcodeScanned(barcode: barcode,completion: {
+
+ })
+ }
+
+ }
+
+ }
+
+
+
+
+ func setTorch(enabled: Bool) throws {
+
+ try device?.lockForConfiguration()
+ device?.torchMode = enabled ? .on : .off
+ device?.unlockForConfiguration()
+
+ }
+
+ public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
+ switch keyPath {
+ case "torchMode":
+ // off = 0; on = 1; auto = 2;
+ let state = change?[.newKey] as? Int
+ flutterApi.onTorchStateChanged(enabled: state == 1, completion: {
+
+ })
+
+ default:
+ break
+ }
+ }
+
+
+
+
+ var barcodeMap: [BarcodeFormat: AVMetadataObject.ObjectType] = [
+ .aztec: .aztec,
+ .code93: .code93,
+ .code39: .code39,
+ .code128: .code128,
+ .dataMatrix: .dataMatrix,
+ .ean8: .ean8,
+ .ean13: .ean13,
+ .pdf417: .pdf417,
+ .qrCode: .qr,
+ .upcE: .upce,
+ .itf: .interleaved2of5
+ ]
+
+
+}
diff --git a/ios/barcode_kit.podspec b/ios/barcode_kit.podspec
new file mode 100644
index 0000000..15d3b2e
--- /dev/null
+++ b/ios/barcode_kit.podspec
@@ -0,0 +1,23 @@
+#
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
+# Run `pod lib lint barcode_kit.podspec` to validate before publishing.
+#
+Pod::Spec.new do |s|
+ s.name = 'barcode_kit'
+ s.version = '0.0.1'
+ s.summary = 'A new Flutter plugin project.'
+ s.description = <<-DESC
+A new Flutter plugin project.
+ DESC
+ s.homepage = 'http://example.com'
+ s.license = { :file => '../LICENSE' }
+ s.author = { 'Your Company' => 'email@example.com' }
+ s.source = { :path => '.' }
+ s.source_files = 'Classes/**/*'
+ s.dependency 'Flutter'
+ s.platform = :ios, '9.0'
+
+ # Flutter.framework does not contain a i386 slice.
+ s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
+ s.swift_version = '5.0'
+end
diff --git a/lib/mtrust_barcode_kit.dart b/lib/mtrust_barcode_kit.dart
new file mode 100644
index 0000000..d6b8b9b
--- /dev/null
+++ b/lib/mtrust_barcode_kit.dart
@@ -0,0 +1,4 @@
+export 'src/barcode_kit.dart';
+export 'src/barcode_kit_view.dart';
+export 'src/perspective.dart';
+export 'src/pigeon.dart';
diff --git a/lib/src/barcode_kit.dart b/lib/src/barcode_kit.dart
new file mode 100644
index 0000000..fc86f0a
--- /dev/null
+++ b/lib/src/barcode_kit.dart
@@ -0,0 +1,149 @@
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+import 'package:mtrust_barcode_kit/src/pigeon.dart';
+import 'package:permission_handler/permission_handler.dart';
+
+/// Callback for when a barcode is scanned.
+typedef OnBarcodeScannedCallback = void Function(DetectedBarcode barcode);
+
+/// Callback for when a text is detected.
+typedef OnTextDetectedCallback = void Function(String text);
+
+/// The main class of the plugin.
+class BarcodeKit extends BarcodeKitFlutterApi {
+ /// Create a new instance of the plugin.
+ BarcodeKit() {
+ BarcodeKitFlutterApi.setup(this);
+ }
+ final BarcodeKitHostApi _host = BarcodeKitHostApi();
+
+ bool _torchEnabled = false;
+
+ /// Returns whether the torch is enabled.
+ bool get torchEnabled => _torchEnabled;
+
+ /// Callback for when a barcode is scanned.
+ OnBarcodeScannedCallback? onBarcodeScannedCallback;
+
+ /// Callback for when a text is scanned.
+ OnTextDetectedCallback? onTextDetectedCallback;
+
+ /// Returns the current permission status.
+ Future getPermissionStatus() async {
+ return Permission.camera.status;
+ }
+
+ /// Requests the camera permission.
+ Future requestPermissions() async {
+ final status = await Permission.camera.status;
+
+ if (status.isRestricted || status.isDenied || status.isLimited) {
+ await Permission.camera.request();
+ }
+
+ return Permission.camera.status.isGranted;
+ }
+
+ /// Opens the camera in [direction] and scans for [formats].
+ Future openCamera(
+ CameraLensDirection direction,
+ List formats,
+ ) async {
+ return _host.openCamera(
+ direction,
+ formats.map((e) => e.index).toList(),
+ );
+ }
+
+ /// Opens the app settings to fix the permissions.
+ void openSettings() {
+ openAppSettings();
+ }
+
+ /// Called from the host when the torch state changes.
+ @override
+ void onTorchStateChanged(bool enabled) {
+ _torchEnabled = enabled;
+ }
+
+ /// Sets the torch state.
+ void setTorch({required bool enabled}) {
+ _host.setTorch(enabled);
+ }
+
+ /// Pauses the camera.
+ void pauseCamera() {
+ _host.pauseCamera();
+ }
+
+ /// Resumes the camera.
+ void resumeCamera() {
+ _host.resumeCamera();
+ }
+
+ String _convertDataMatrix(String base64Input) {
+ final input = base64.decode(base64Input);
+
+ var string = '';
+
+ for (final byte in input) {
+ // 232 is the GS1 FNC1 character
+ if (byte == 232) {
+ string += String.fromCharCode(29);
+ }
+ // This byte represents two digits. We pad left if the value < 10
+ if (byte >= 130 && byte <= 229) {
+ // numeric value
+ final value = byte - 130;
+ string += value.toString().padLeft(2, '0');
+ }
+ // This byte represents a single character
+ if (byte < 128) {
+ string += String.fromCharCode(byte - 1);
+ }
+ // Padding byte. Signals the end of the actual content
+ if (byte == 129) {
+ break;
+ }
+ }
+
+ return string;
+ }
+
+ /// Called from the host when a barcode is detected.
+ @override
+ void onBarcodeScanned(DetectedBarcode barcode) {
+ var finalBarcode = barcode;
+ if (!kIsWeb &&
+ Platform.isIOS &&
+ barcode.rawValue != null &&
+ barcode.format == BarcodeFormat.dataMatrix) {
+ // Decode base 64 value
+
+ finalBarcode = DetectedBarcode(
+ rawValue: _convertDataMatrix(barcode.rawValue!),
+ cornerPoints: barcode.cornerPoints,
+ );
+ }
+
+ onBarcodeScannedCallback?.call(finalBarcode);
+ }
+
+ /// Closes the camera.
+ void closeCamera() {
+ BarcodeKitHostApi().closeCamera();
+ }
+
+ /// Sets the OCR state.
+ // ignore: avoid_positional_boolean_parameters
+ void setOCREnabled(bool enabled) {
+ _host.setOCREnabled(enabled);
+ }
+
+ @override
+ void onTextDetected(String text) {
+ onTextDetectedCallback?.call(text);
+ }
+}
diff --git a/lib/src/barcode_kit_method_channel.dart b/lib/src/barcode_kit_method_channel.dart
new file mode 100644
index 0000000..166f9d4
--- /dev/null
+++ b/lib/src/barcode_kit_method_channel.dart
@@ -0,0 +1,17 @@
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+import 'package:mtrust_barcode_kit/src/barcode_kit_platform_interface.dart';
+
+/// An implementation of [BarcodeKitPlatform] that uses method channels.
+class MethodChannelBarcodeKit extends BarcodeKitPlatform {
+ /// The method channel used to interact with the native platform.
+ @visibleForTesting
+ final methodChannel = const MethodChannel('barcode_kit');
+
+ @override
+ Future getPlatformVersion() async {
+ final version =
+ await methodChannel.invokeMethod('getPlatformVersion');
+ return version;
+ }
+}
diff --git a/lib/src/barcode_kit_platform_interface.dart b/lib/src/barcode_kit_platform_interface.dart
new file mode 100644
index 0000000..f45aadc
--- /dev/null
+++ b/lib/src/barcode_kit_platform_interface.dart
@@ -0,0 +1,30 @@
+import 'package:mtrust_barcode_kit/src/barcode_kit_method_channel.dart';
+import 'package:plugin_platform_interface/plugin_platform_interface.dart';
+
+/// The interface that implementations of barcode_kit must implement.
+abstract class BarcodeKitPlatform extends PlatformInterface {
+ /// Constructs a BarcodeKitPlatform.
+ BarcodeKitPlatform() : super(token: _token);
+
+ static final Object _token = Object();
+
+ static BarcodeKitPlatform _instance = MethodChannelBarcodeKit();
+
+ /// The default instance of [BarcodeKitPlatform] to use.
+ ///
+ /// Defaults to [MethodChannelBarcodeKit].
+ static BarcodeKitPlatform get instance => _instance;
+
+ /// Platform-specific implementations should set this with their own
+ /// platform-specific class that extends [BarcodeKitPlatform] when
+ /// they register themselves.
+ static set instance(BarcodeKitPlatform instance) {
+ PlatformInterface.verifyToken(instance, _token);
+ _instance = instance;
+ }
+
+ /// Requests the platform version.
+ Future getPlatformVersion() {
+ throw UnimplementedError('platformVersion() has not been implemented.');
+ }
+}
diff --git a/lib/src/barcode_kit_view.dart b/lib/src/barcode_kit_view.dart
new file mode 100644
index 0000000..07c57b7
--- /dev/null
+++ b/lib/src/barcode_kit_view.dart
@@ -0,0 +1,564 @@
+import 'dart:io';
+import 'dart:ui';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:mtrust_barcode_kit/mtrust_barcode_kit.dart';
+import 'package:native_device_orientation/native_device_orientation.dart';
+import 'package:permission_handler/permission_handler.dart';
+
+/// A builder that is used to build the UI for the barcode kit
+abstract class BarcodeKitUiBuilder {
+ /// Build the widget that is shown when the user has not granted permission
+ Widget buildRequestPermission(
+ BuildContext context,
+ void Function() onPermissionRequested,
+ );
+
+ /// Build the widget that is shown when the user has not granted permission
+ Widget buildNoPermission(
+ BuildContext context,
+ void Function() onSettingsRequested,
+ );
+
+ /// Build the widget that is shown when the camera is not available
+ Widget buildCameraNotAvailable(BuildContext context);
+
+ /// Build the widget that is shown when the camera is not opened yet
+ Widget buildCameraNotOpened(BuildContext context);
+}
+
+/// Overlay painter is used to draw a rectangle on the camera view and
+/// shade the rest of the view
+class MaskPainter extends CustomPainter {
+ /// Create a new [MaskPainter]
+ MaskPainter({
+ required this.animationValue,
+ this.height = 0.4,
+ this.width = 0.5,
+ });
+
+ /// The animation value that is used to animate the mask
+ double animationValue = 0;
+
+ /// The height of the mask
+ double height = 0;
+
+ /// The width of the mask
+ double width = 0;
+
+ @override
+ void paint(Canvas canvas, Size size) {
+ canvas.saveLayer(Rect.largest, Paint());
+ final paint = Paint()
+ ..shader = const LinearGradient(
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ colors: [
+ Color.fromARGB(150, 15, 26, 46),
+ Colors.black,
+ ],
+ ).createShader(Rect.fromLTWH(0, 0, size.width, size.height));
+
+ canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint);
+
+ final cutoutRect = RRect.fromRectAndRadius(
+ Rect.fromCenter(
+ center: Offset(size.width / 2, size.height / 2),
+ width: width,
+ height: height * animationValue,
+ ),
+ const Radius.circular(16),
+ );
+
+ canvas
+ ..drawRRect(cutoutRect, Paint()..blendMode = BlendMode.clear)
+ ..restore()
+ ..drawRRect(
+ cutoutRect,
+ Paint()
+ ..color = Colors.white.withAlpha(animationValue.toInt() * 255)
+ ..style = PaintingStyle.stroke
+ ..strokeWidth = 2,
+ );
+ }
+
+ @override
+ bool shouldRepaint(CustomPainter oldDelegate) {
+ return true;
+ }
+}
+
+/// The default overlay builder that is used to draw a rectangle on the barcode
+Widget defaultBarcodeOverlayBuilder(
+ BuildContext context,
+ DetectedBarcode barcode,
+) {
+ return Container(
+ height: 1,
+ width: 1,
+ decoration: BoxDecoration(
+ color: Colors.yellowAccent.withAlpha(100),
+ borderRadius: BorderRadius.circular(0.05),
+ ),
+ );
+}
+
+/// A utility widget that handles most functionality needed to scan barcodes
+class BarcodeKitView extends StatefulWidget {
+ /// Create a new [BarcodeKitView]
+ const BarcodeKitView({
+ required this.onBarcodeScanned,
+ required this.uiBuilder,
+ required this.formats,
+ this.onTextDetected,
+ this.backdropColor = Colors.black,
+ this.barcodeOverlayBuilder = defaultBarcodeOverlayBuilder,
+ this.paused = false,
+ this.mask = true,
+ this.maskHeight = 200,
+ this.maskWidth = 200,
+ this.pauseBlurAmount = 10,
+ this.pauseZoomAmount = 0.2,
+ this.followRotation = true,
+ this.children,
+ this.cameraFit = BoxFit.cover,
+ this.enableOCR = false,
+ this.direction = CameraLensDirection.back,
+ this.widgetAboveMask,
+ this.widgetBelowMask,
+ this.maskAdditionpauseOpacity = 0.1,
+ super.key,
+ });
+
+ /// Direction of the camera
+ final CameraLensDirection direction;
+
+ /// The formats that should be scanned
+ final Set formats;
+
+ /// Whether to run OCR
+ final bool enableOCR;
+
+ /// Callback for when text is detected
+ final void Function(String detectedText)? onTextDetected;
+
+ /// Widget that gets transformed to be placed on top of the barcode.
+ /// Needs to be 1x1 in size to work properly
+ final Widget Function(BuildContext context, DetectedBarcode barcode)?
+ barcodeOverlayBuilder;
+
+ /// The color that is used to fill around the mask
+ final Color backdropColor;
+
+ /// The ui builder that is used to build the UI for requesting permissions
+ /// etc.
+ final BarcodeKitUiBuilder uiBuilder;
+
+ /// The children that are placed on top of the camera view
+ final List? children;
+
+ /// Opacity of [widgetAboveMask] and [widgetBelowMask] when the camera is
+ /// paused
+ final double maskAdditionpauseOpacity;
+
+ /// Widget to be placed above the mask
+ final Widget? widgetAboveMask;
+
+ /// Widget to be placed below the mask
+ final Widget? widgetBelowMask;
+
+ /// The amount of blur that is applied to the camera view when the
+ /// camera is paused
+ final double pauseBlurAmount;
+
+ /// The amount of zoom that is applied to the camera view when the
+ /// camera is paused
+ final double pauseZoomAmount;
+
+ /// Whether the camera should be paused, will prevent calls
+ /// to [onBarcodeScanned]
+ final bool paused;
+
+ /// Whether the mask should be shown
+ final bool mask;
+
+ /// The height of the mask
+ final double maskHeight;
+
+ /// The width of the mask
+ final double maskWidth;
+
+ /// The fit of the camera view
+ final BoxFit cameraFit;
+
+ /// Whether the camera should follow the rotation of the device
+ final bool followRotation;
+
+ /// The callback that is called when a barcode is scanned
+ final void Function(DetectedBarcode barcode) onBarcodeScanned;
+
+ @override
+ State createState() => _BarcodeKitViewState();
+}
+
+class _BarcodeKitViewState extends State
+ with TickerProviderStateMixin {
+ final BarcodeKit _barcodeKitPlugin = BarcodeKit();
+
+ int? textureId;
+ double width = 0;
+ double height = 0;
+
+ Set _lastFormats = {};
+
+ bool _unableToOpenCamera = false;
+
+ DetectedBarcode? lastBarcode;
+
+ PermissionStatus _permissionStatus = PermissionStatus.denied;
+
+ late AnimationController _animationController;
+
+ @override
+ void initState() {
+ assert(widget.formats.isNotEmpty, 'Needs at least one format to scan');
+ _animationController = AnimationController(
+ vsync: this,
+ duration: const Duration(milliseconds: 400),
+ );
+ super.initState();
+ _start();
+ }
+
+ Future _start() async {
+ _lastFormats = widget.formats.toList().toSet();
+ _permissionStatus = await _barcodeKitPlugin.getPermissionStatus();
+ if (_permissionStatus != PermissionStatus.granted) {
+ setState(() {});
+ return;
+ }
+
+ _unableToOpenCamera = false;
+
+ if (widget.enableOCR) {
+ _barcodeKitPlugin.setOCREnabled(true);
+ } else {
+ _barcodeKitPlugin.setOCREnabled(false);
+ }
+
+ try {
+ final value = await _barcodeKitPlugin.openCamera(
+ CameraLensDirection.back,
+ widget.formats.toList(),
+ );
+ setState(() {
+ textureId = int.parse(value.textureId!);
+
+ height = value.height!.toDouble();
+ width = value.width!.toDouble();
+ });
+ } catch (e) {
+ setState(() {
+ _unableToOpenCamera = true;
+ });
+ }
+
+ _barcodeKitPlugin
+ ..onBarcodeScannedCallback = (barcode) {
+ if (!widget.paused) {
+ widget.onBarcodeScanned(barcode);
+ setState(() {
+ lastBarcode = barcode;
+ });
+ }
+ }
+ ..onTextDetectedCallback = (text) {
+ if (!widget.paused) {
+ widget.onTextDetected?.call(text);
+ }
+ };
+ }
+
+ @override
+ void didUpdateWidget(covariant BarcodeKitView oldWidget) {
+ if (widget.paused != oldWidget.paused) {
+ if (widget.paused) {
+ if (Platform.isAndroid) {
+ _barcodeKitPlugin.pauseCamera();
+ }
+ _animationController.forward();
+ } else {
+ if (Platform.isAndroid) {
+ _barcodeKitPlugin.resumeCamera();
+ }
+ _animationController.reverse();
+ setState(() {
+ lastBarcode = null;
+ });
+ }
+ }
+
+ if (widget.enableOCR != oldWidget.enableOCR) {
+ _barcodeKitPlugin.setOCREnabled(widget.enableOCR);
+ }
+
+ if (!setEquals(widget.formats, _lastFormats)) {
+ _barcodeKitPlugin.closeCamera();
+ _start();
+ }
+
+ super.didUpdateWidget(oldWidget);
+ }
+
+ Widget _wrapInRotatedBox({
+ required Widget child,
+ required NativeDeviceOrientation orientation,
+ }) {
+ if (kIsWeb) {
+ return child;
+ }
+
+ return RotatedBox(
+ quarterTurns: _getQuarterTurns(orientation),
+ child: child,
+ );
+ }
+
+ Widget _buildLastBarcode() {
+ return PerspectiveBarcode(
+ barcode: lastBarcode!,
+ child: widget.barcodeOverlayBuilder!(context, lastBarcode!),
+ );
+ }
+
+ int _getQuarterTurns(NativeDeviceOrientation orientation) {
+ if (Platform.isIOS) {
+ return turnsIoS[orientation] ?? 0;
+ }
+ return turns[orientation]!;
+ }
+
+ Map turnsIoS = {
+ NativeDeviceOrientation.portraitUp: 0,
+ NativeDeviceOrientation.landscapeRight: 1,
+ NativeDeviceOrientation.portraitDown: 2,
+ NativeDeviceOrientation.landscapeLeft: 3,
+ };
+
+ Map turns = {
+ NativeDeviceOrientation.portraitUp: 0,
+ NativeDeviceOrientation.landscapeRight: 2,
+ NativeDeviceOrientation.portraitDown: 0,
+ NativeDeviceOrientation.landscapeLeft: 0,
+ };
+
+ @override
+ void dispose() {
+ _barcodeKitPlugin
+ ..closeCamera()
+ ..onBarcodeScannedCallback = null;
+ super.dispose();
+ }
+
+ void _openSettings() {
+ _barcodeKitPlugin.openSettings();
+ }
+
+ Future _requestPermission() async {
+ if (await _barcodeKitPlugin.requestPermissions()) {
+ await _start();
+ } else {
+ setState(() {
+ _permissionStatus = PermissionStatus.permanentlyDenied;
+ });
+ }
+ }
+
+ Widget _buildAnimationWrapper(BuildContext context, Widget child) {
+ return AnimatedBuilder(
+ animation: _animationController,
+ builder: (context, child2) {
+ final value = CurvedAnimation(
+ parent: _animationController,
+ curve: Curves.elasticOut,
+ ).value;
+ return ColorFiltered(
+ colorFilter: ColorFilter.mode(
+ widget.backdropColor.withOpacity(value * 0.2),
+ BlendMode.srcATop,
+ ),
+ child: ImageFiltered(
+ imageFilter: ImageFilter.blur(
+ sigmaX: value * widget.pauseBlurAmount,
+ sigmaY: value * widget.pauseBlurAmount,
+ ),
+ child: Transform.scale(
+ scale: 1 + widget.pauseZoomAmount * value,
+ child: child,
+ ),
+ ),
+ );
+ },
+ );
+ }
+
+ Widget _buildTextureWrapper() {
+ return SizedBox(
+ width: width,
+ height: height,
+ child: Stack(
+ children: [
+ Texture(
+ key: ValueKey(textureId),
+ textureId: textureId!,
+ freeze: widget.paused,
+ ),
+ if (lastBarcode != null && widget.barcodeOverlayBuilder != null)
+ _buildLastBarcode(),
+ ],
+ ),
+ );
+ }
+
+ Widget _buildCamera() {
+ return NativeDeviceOrientationReader(
+ builder: (context) {
+ return Stack(
+ fit: StackFit.expand,
+ children: [
+ _buildAnimationWrapper(
+ context,
+ _wrapInRotatedBox(
+ orientation: NativeDeviceOrientationReader.orientation(
+ context,
+ ),
+ child: FittedBox(
+ fit: widget.cameraFit,
+ clipBehavior: Clip.hardEdge,
+ child: _buildTextureWrapper(),
+ ),
+ ),
+ ),
+ if (widget.mask) _buildMask(),
+ _buildMaskAdditions(),
+ ...?widget.children,
+ ],
+ );
+ },
+ );
+ }
+
+ Widget _buildMask() {
+ return AnimatedBuilder(
+ animation: _animationController,
+ builder: (context, child) => SizedBox.expand(
+ child: Opacity(
+ opacity: 1 - _animationController.value,
+ child: CustomPaint(
+ painter: MaskPainter(
+ height: widget.maskHeight,
+ width: widget.maskWidth,
+ animationValue: 1 -
+ CurvedAnimation(
+ curve: const Interval(
+ 0,
+ 0.3,
+ curve: Curves.easeOutCubic,
+ ),
+ parent: _animationController,
+ ).value,
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ Widget _buildStaticCamera() {
+ return Stack(
+ fit: StackFit.expand,
+ children: [
+ _buildAnimationWrapper(
+ context,
+ SizedBox.expand(
+ child: FittedBox(
+ fit: widget.cameraFit,
+ child: _buildTextureWrapper(),
+ ),
+ ),
+ ),
+ if (widget.mask) _buildMask(),
+ _buildMaskAdditions(),
+ ...?widget.children,
+ ],
+ );
+ }
+
+ Widget _buildMaskAdditions() {
+ return AnimatedBuilder(
+ animation: _animationController,
+ builder: (context, child) => SizedBox.expand(
+ child: Opacity(
+ opacity: 1 -
+ (1 - widget.maskAdditionpauseOpacity) *
+ _animationController.value,
+ child: SizedBox.expand(
+ child: Builder(
+ builder: (context) {
+ return Column(
+ children: [
+ Expanded(
+ child: widget.widgetAboveMask ?? Container(),
+ ),
+ Container(
+ height: widget.maskHeight,
+ ),
+ Expanded(
+ child: widget.widgetBelowMask ?? Container(),
+ ),
+ ],
+ );
+ },
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ switch (_permissionStatus) {
+ case PermissionStatus.denied:
+ return widget.uiBuilder.buildRequestPermission(
+ context,
+ _requestPermission,
+ );
+
+ case PermissionStatus.granted:
+ if (_unableToOpenCamera) {
+ return widget.uiBuilder.buildCameraNotAvailable(
+ context,
+ );
+ }
+ if (textureId == null) {
+ return widget.uiBuilder.buildCameraNotOpened(
+ context,
+ );
+ }
+
+ if (widget.followRotation) {
+ return _buildCamera();
+ } else {
+ return _buildStaticCamera();
+ }
+
+ case PermissionStatus.permanentlyDenied:
+ case PermissionStatus.restricted:
+ case PermissionStatus.limited:
+ case PermissionStatus.provisional:
+ return widget.uiBuilder.buildNoPermission(context, _openSettings);
+ }
+ }
+}
diff --git a/lib/src/perspective.dart b/lib/src/perspective.dart
new file mode 100644
index 0000000..1734a78
--- /dev/null
+++ b/lib/src/perspective.dart
@@ -0,0 +1,202 @@
+import 'dart:collection';
+import 'dart:typed_data';
+
+import 'package:equatable/equatable.dart';
+import 'package:flutter/material.dart';
+import 'package:mtrust_barcode_kit/mtrust_barcode_kit.dart';
+
+// Adapted from https://raw.githubusercontent.com/alesdi/qr_code_vision/master/lib/helpers/perspective_transform.dart
+
+/// A representation of a perspective transformation, that can be applied to
+/// single points. Common linear transformation operations (such as inversion
+/// and comCornerPoint) are also supported.
+class PerspectiveTransform extends Equatable {
+ /// Creates a [PerspectiveTransform] from a list of 9 elements
+ PerspectiveTransform(List elements)
+ : _matrix = _PerspectiveMatrix.fromList(elements);
+
+ /// Creates a [PerspectiveTransform] that transforms a 1x1 square into the
+ /// given quadrilateral, expressed as a list of its vertices.
+ factory PerspectiveTransform.fromTransformedSquare(
+ List vertices,
+ ) {
+ assert(vertices.length == 4, 'Exactly 4 vertices are required');
+ final p1 = vertices[0];
+ final p2 = vertices[1];
+ final p3 = vertices[2];
+ final p4 = vertices[3];
+
+ final dx3 = p1.x - p2.x + p3.x - p4.x;
+ final dy3 = p1.y - p2.y + p3.y - p4.y;
+ if (dx3 == 0 && dy3 == 0) {
+ // Affine
+ return PerspectiveTransform([
+ p2.x - p1.x, p2.y - p1.y, 0, //
+ p3.x - p2.x, p3.y - p2.y, 0, //
+ p1.x, p1.y, 1, //
+ ]);
+ } else {
+ final dx1 = p2.x - p3.x;
+ final dx2 = p4.x - p3.x;
+ final dy1 = p2.y - p3.y;
+ final dy2 = p4.y - p3.y;
+ final denominator = dx1 * dy2 - dx2 * dy1;
+ final a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
+ final a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
+ return PerspectiveTransform([
+ p2.x - p1.x + a13 * p2.x, p2.y - p1.y + a13 * p2.y, a13, //
+ p4.x - p1.x + a23 * p4.x, p4.y - p1.y + a23 * p4.y, a23, //
+ p1.x, p1.y, 1, //
+ ]);
+ }
+ }
+
+ /// Creates a [PerspectiveTransform] that transforms a given quadrilateral
+ /// into another given quadrilateral, expressed as a list of its vertices.
+ factory PerspectiveTransform.fromQuadrilaterals(
+ List originVertices,
+ List destinationVertices,
+ ) {
+ final fromOriginToSquare =
+ PerspectiveTransform.fromTransformedSquare(originVertices).inverse();
+ final fromSquareToDestination =
+ PerspectiveTransform.fromTransformedSquare(destinationVertices);
+ return fromSquareToDestination.compose(fromOriginToSquare);
+ }
+ final _PerspectiveMatrix _matrix;
+
+ /// Creates a [PerspectiveTransform] that is the comCornerPoint of this
+ /// transformation and a given [other] transformation.
+ PerspectiveTransform compose(PerspectiveTransform other) {
+ final A = other._matrix.toList();
+ final B = _matrix.toList();
+ final result = List.filled(9, 0);
+ for (var i = 0; i < 3; i++) {
+ for (var j = 0; j < 3; j++) {
+ for (var k = 0; k < 3; k++) {
+ result[i * 3 + j] += A[i * 3 + k] * B[k * 3 + j];
+ }
+ }
+ }
+
+ return PerspectiveTransform(result);
+ }
+
+ /// Creates a [PerspectiveTransform] that is the inverse of this
+ PerspectiveTransform inverse() {
+ final A = _matrix;
+
+ // Compute the adjoint matrix (transposed co-factors matrix)
+ return PerspectiveTransform(
+ _PerspectiveMatrix([
+ [
+ A(2, 2) * A(3, 3) - A(2, 3) * A(3, 2),
+ A(1, 3) * A(3, 2) - A(1, 2) * A(3, 3),
+ A(1, 2) * A(2, 3) - A(1, 3) * A(2, 2),
+ ],
+ [
+ A(3, 1) * A(2, 3) - A(3, 3) * A(2, 1),
+ A(1, 1) * A(3, 3) - A(1, 3) * A(3, 1),
+ A(1, 3) * A(2, 1) - A(1, 1) * A(2, 3),
+ ],
+ [
+ A(2, 1) * A(3, 2) - A(2, 2) * A(3, 1),
+ A(1, 2) * A(3, 1) - A(1, 1) * A(3, 2),
+ A(1, 1) * A(2, 2) - A(1, 2) * A(2, 1),
+ ],
+ ]),
+ );
+
+ // transpose
+ }
+
+ /// Applies this transformation to a given [point] and returns the transformed
+ /// point.
+ CornerPoint apply(CornerPoint point) {
+ final x = point.x;
+ final y = point.y;
+ final denominator = _matrix(1, 3) * x + _matrix(2, 3) * y + _matrix(3, 3);
+ return CornerPoint(
+ x: (_matrix(1, 1) * x + _matrix(2, 1) * y + _matrix(3, 1)) / denominator,
+ y: (_matrix(1, 2) * x + _matrix(2, 2) * y + _matrix(3, 2)) / denominator,
+ );
+ }
+
+ /// Compute an equivalent 3D perspective matrix (4x4) that can be used to
+ /// transform a canvas or as argument for the Transform widget in Flutter.
+ Float64List to3DPerspectiveMatrix() {
+ return Float64List.fromList([
+ _matrix(1, 1), _matrix(1, 2), 0, _matrix(1, 3), //
+ _matrix(2, 1), _matrix(2, 2), 0, _matrix(2, 3), //
+ 0, 0, 1, 1, //
+ _matrix(3, 1), _matrix(3, 2), 0, _matrix(3, 3), //
+ ]);
+ }
+
+ @override
+ List