diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e063496
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# 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/
+
+/android/local.properties
+/android/.gradle/
+/android/gradlew
+/android/gradlew.bat
+/android/gradle/wrapper/gradle-wrapper.jar
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..e5c027b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 1.0.0
+
+First release with Android and iOS support.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e2ba081
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+BSD 3-Clause License
+
+Copyright (c) 2023, Freedelity
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f68bdd7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,41 @@
+# native_barcode_scanner
+
+A flutter plugin to scan barcodes using the device camera.
+
+## Platform Support
+
+| Android | iOS |
+| :-----: | :-----: |
+| ✅ | ✅ |
+
+## Getting Started
+
+Add this to your package's `pubspec.yaml` file:
+
+```yaml
+dependencies:
+ native_barcode_scanner: ^1.0.0
+```
+
+## Usage
+
+Then you just have to import the package with
+
+```dart
+import 'package:native_barcode_scanner/barcode_scanner.dart';
+```
+
+Then, create a `BarcodeScannerWidget` in your widget tree where you want to show the camera stream. This widget has a `onBarcodeDetected` callback which can be used to be notified when barcodes are detected and let you process them:
+
+```dart
+@override
+ Widget build(BuildContext context) {
+ return BarcodeScannerWidget(
+ onBarcodeDetected: (barcode) {
+ print('Barcode detected: ${barcode.value} (format: ${barcode.format.name})');
+ }
+ );
+ }
+```
+
+If you need to manipulate the behaviour of the barcode scanning process, you may use the static methods of the `BarcodeScanner` class.
diff --git a/analysis_options.yaml b/analysis_options.yaml
new file mode 100644
index 0000000..a5744c1
--- /dev/null
+++ b/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: package:flutter_lints/flutter.yaml
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/android/.gitignore b/android/.gitignore
new file mode 100644
index 0000000..c6cbe56
--- /dev/null
+++ b/android/.gitignore
@@ -0,0 +1,8 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 0000000..5c2e800
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,62 @@
+group 'be.freedelity.barcode_scanner'
+version '1.0-SNAPSHOT'
+
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.4.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+rootProject.allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ compileSdkVersion 31
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ minSdkVersion 21
+ }
+
+ buildTypes {
+ release {
+ aaptOptions {
+ ignoreAssetsPattern 'mlkit_pose:mlkit_odt_*:hobbes.tflite:tflite_*'
+ }
+ }
+ debug {
+ aaptOptions {
+ ignoreAssetsPattern 'mlkit_pose:mlkit_odt_*:hobbes.tflite:tflite_*'
+ }
+ }
+ }
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+
+ implementation 'com.google.android.material:material:1.5.0'
+ implementation 'com.google.mlkit:barcode-scanning:17.0.2'
+ implementation 'androidx.camera:camera-core:1.1.0-beta01'
+ implementation 'androidx.camera:camera-camera2:1.1.0-beta01'
+ implementation 'androidx.camera:camera-lifecycle:1.1.0-beta01'
+ implementation 'androidx.camera:camera-view:1.1.0-beta01'
+
+ implementation 'com.google.guava:guava:27.0.1-android'
+}
diff --git a/android/gradle.properties b/android/gradle.properties
new file mode 100644
index 0000000..5bac8ac
--- /dev/null
+++ b/android/gradle.properties
@@ -0,0 +1 @@
+android.useAndroidX=true
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..fae0804
--- /dev/null
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/android/settings.gradle b/android/settings.gradle
new file mode 100644
index 0000000..17f4459
--- /dev/null
+++ b/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'barcode_scanner'
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..cef932a
--- /dev/null
+++ b/android/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
diff --git a/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerController.kt b/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerController.kt
new file mode 100644
index 0000000..b882596
--- /dev/null
+++ b/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerController.kt
@@ -0,0 +1,276 @@
+// Copyright 2023 Freedelity. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package be.freedelity.barcode_scanner
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.Context
+import android.hardware.camera2.CameraAccessException
+import android.util.Log
+import android.view.View
+import android.view.ViewTreeObserver
+import androidx.camera.core.*
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.view.PreviewView
+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.common.InputImage
+import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.common.EventChannel
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import kotlinx.coroutines.runBlocking
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.TimeUnit
+
+class BarcodeScannerController(private val activity: Activity, messenger: BinaryMessenger, methodChannelName: String, scanEventChannelName: String) : MethodChannel.MethodCallHandler, EventChannel.StreamHandler {
+
+ private var methodChannel: MethodChannel = MethodChannel(messenger, methodChannelName)
+ private var scanStreamChannel: EventChannel = EventChannel(messenger, scanEventChannelName)
+ private var eventSink: EventChannel.EventSink? = null
+
+ private lateinit var cameraExecutor: ExecutorService
+ private lateinit var previewView: PreviewView
+ private lateinit var context: Context
+
+ private val imageAnalysis: ImageAnalysis = ImageAnalysis.Builder().build()
+
+ private var cameraParams: Map? = null
+ private var camera: Camera? = null
+
+ private var isScannerActive: Boolean = false
+ private var torchEnabled: Boolean = false
+
+ private var scanSucceedTimestamp: Long = System.currentTimeMillis()
+
+ init {
+ methodChannel.setMethodCallHandler(this)
+ scanStreamChannel.setStreamHandler(this)
+ }
+
+ fun stopListening() {
+ methodChannel.setMethodCallHandler(null)
+ }
+
+ override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
+ eventSink = events
+ }
+
+ override fun onCancel(arguments: Any?) {
+ eventSink = null
+ }
+
+ override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
+ runBlocking {
+ when (call.method) {
+ "toggleTorch" -> {
+ try {
+ torchEnabled = !torchEnabled
+ camera?.cameraControl?.enableTorch(torchEnabled)
+ result.success(null)
+ } catch (e: Exception) {
+ handleException(e, result)
+ }
+ }
+ "flipCamera" -> {
+ try {
+ if (cameraParams != null) {
+
+ val map: MutableMap = cameraParams!!.toMutableMap()
+ if (map["camera_selector"] == "front")
+ map["camera_selector"] = "back"
+ else
+ map["camera_selector"] = "front"
+
+ startCamera(map)
+ }
+ result.success(null)
+ } catch (e: Exception) {
+ handleException(e, result)
+ }
+ }
+ "startScanner" -> {
+ try {
+ if (!isScannerActive) {
+ startScanner()
+ }
+ result.success(null)
+ } catch (e: Exception) {
+ handleException(e, result)
+ }
+ }
+ "stopScanner" -> {
+ try {
+ if (isScannerActive) {
+ isScannerActive = false
+ imageAnalysis.clearAnalyzer()
+ }
+ result.success(null)
+ } catch (e: Exception) {
+ handleException(e, result)
+ }
+ }
+ else -> result.notImplemented()
+ }
+
+ }
+ }
+
+ fun startCamera(params: Map?, viewContext: Context? = null, viewPreviewView: PreviewView? = null, viewCameraExecutor: ExecutorService? = null) {
+
+ if (viewPreviewView != null)
+ previewView = viewPreviewView
+ if (viewContext != null)
+ context = viewContext
+ if (viewCameraExecutor != null)
+ cameraExecutor = viewCameraExecutor
+
+ cameraParams = params
+
+ val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
+
+ cameraProviderFuture.addListener({
+
+ if (cameraParams?.get("start_scanning") == true) {
+ startScanner()
+ }
+
+ try {
+
+ val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
+ cameraProvider.unbindAll()
+
+ val cameraSelector : CameraSelector = when (cameraParams?.get("camera_selector")) {
+ "front" -> CameraSelector.DEFAULT_FRONT_CAMERA
+ else -> CameraSelector.DEFAULT_BACK_CAMERA
+ }
+
+ val preview = Preview.Builder().build().also {
+ it.setSurfaceProvider(previewView.surfaceProvider)
+ }
+
+ camera = cameraProvider.bindToLifecycle(activity as LifecycleOwner, cameraSelector, preview, imageAnalysis)
+
+ configureAutofocus()
+
+ } catch(exc: Exception) {
+ Log.e("FREEDELITY", "Use case binding failed", exc)
+ }
+
+ }, ContextCompat.getMainExecutor(context))
+ }
+
+ private fun startScanner() {
+ isScannerActive = true
+ val options = BarcodeScannerOptions.Builder().setBarcodeFormats(
+ Barcode.FORMAT_CODE_39,
+ Barcode.FORMAT_CODE_93,
+ Barcode.FORMAT_CODE_128,
+ Barcode.FORMAT_EAN_8,
+ Barcode.FORMAT_EAN_13,
+ Barcode.FORMAT_ITF,
+ Barcode.FORMAT_CODABAR,
+ Barcode.FORMAT_DATA_MATRIX,
+ Barcode.FORMAT_QR_CODE
+ ).build()
+ val scanner = BarcodeScanning.getClient(options)
+ imageAnalysis.setAnalyzer(cameraExecutor) { imageProxy ->
+ processImageProxy(scanner, imageProxy)
+ }
+ }
+
+ private fun handleException(exception: Exception, result: MethodChannel.Result) {
+ if (exception is CameraAccessException) {
+ result.error("CameraAccess", exception.message, null)
+ return
+ }
+ throw (exception as RuntimeException)
+ }
+
+ private fun configureAutofocus() {
+ previewView.afterMeasured {
+ val factory = previewView.meteringPointFactory
+ val yCenter = previewView.height / 2f
+ val xCenter = previewView.width / 2f
+ val autofocusPoint = factory.createPoint(xCenter, yCenter, .01f)
+ try {
+ val autofocusAction = FocusMeteringAction.Builder(autofocusPoint ).apply {
+ setAutoCancelDuration(300, TimeUnit.MILLISECONDS)
+ addPoint(factory.createPoint(xCenter - 9f, yCenter, .01f))
+ addPoint(factory.createPoint(xCenter - 6f, yCenter, .01f))
+ addPoint(factory.createPoint(xCenter - 3f, yCenter, .01f))
+ addPoint(factory.createPoint(xCenter + 3f, yCenter, .01f))
+ addPoint(factory.createPoint(xCenter + 6f, yCenter, .01f))
+ addPoint(factory.createPoint(xCenter + 9f, yCenter, .01f))
+ }.build()
+ camera!!.cameraControl.startFocusAndMetering(autofocusAction)
+ } catch( e: CameraInfoUnavailableException) {
+ Log.e("FREEDELITY", "cannot access camera", e)
+ }
+ }
+ }
+
+ private inline fun View.afterMeasured(crossinline block: () -> Unit) {
+ if (measuredWidth > 0 && measuredHeight > 0) {
+ block()
+ } else {
+ viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
+ override fun onGlobalLayout() {
+ if (measuredWidth > 0 && measuredHeight > 0) {
+ viewTreeObserver.removeOnGlobalLayoutListener(this)
+ block()
+ }
+ }
+ })
+ }
+ }
+
+ private fun convertBarcodeType(mlKitType: Int): Int {
+ return when(mlKitType) {
+ Barcode.FORMAT_CODE_39 -> BarcodeFormats.CODE_39
+ Barcode.FORMAT_CODE_93 -> BarcodeFormats.CODE_93
+ Barcode.FORMAT_CODE_128 -> BarcodeFormats.CODE_128
+ Barcode.FORMAT_EAN_8 -> BarcodeFormats.EAN_8
+ Barcode.FORMAT_EAN_13 -> BarcodeFormats.EAN_13
+ Barcode.FORMAT_ITF -> BarcodeFormats.ITF
+ Barcode.FORMAT_CODABAR -> BarcodeFormats.CODABAR
+ Barcode.FORMAT_DATA_MATRIX -> BarcodeFormats.DATAMATRIX
+ Barcode.FORMAT_QR_CODE -> BarcodeFormats.QR_CODE
+ else -> -1
+ }
+ }
+
+ @SuppressLint("UnsafeOptInUsageError")
+ private fun processImageProxy(barcodeScanner: BarcodeScanner, imageProxy: ImageProxy) {
+ imageProxy.image?.let { image ->
+ if (System.currentTimeMillis() > (scanSucceedTimestamp + 2000)) {
+ val inputImage = InputImage.fromMediaImage(image, imageProxy.imageInfo.rotationDegrees)
+ barcodeScanner.process(inputImage).addOnSuccessListener { barcodeList ->
+ val barcode: Barcode? = barcodeList.getOrNull(0)
+ if (barcode != null) {
+ scanSucceedTimestamp = System.currentTimeMillis()
+ eventSink?.success(mapOf(
+ "barcode" to barcode.displayValue,
+ "format" to convertBarcodeType(barcode.format)
+ ))
+ }
+ }
+ .addOnFailureListener {
+ Log.e("FREEDELITY", it.message.orEmpty())
+ }
+ .addOnCompleteListener {
+ imageProxy.image?.close()
+ imageProxy.close()
+ }
+ } else {
+ imageProxy.image?.close()
+ imageProxy.close()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerPlugin.kt b/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerPlugin.kt
new file mode 100644
index 0000000..3542a00
--- /dev/null
+++ b/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerPlugin.kt
@@ -0,0 +1,61 @@
+// Copyright 2023 Freedelity. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package be.freedelity.barcode_scanner
+
+import android.app.Activity
+import androidx.annotation.NonNull
+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
+
+class BarcodeScannerPlugin: FlutterPlugin, ActivityAware {
+
+ private val platformViewChannel = "be.freedelity/scanner/view"
+ private val methodChannel = "be.freedelity/scanner/method"
+ private val scanEventChannel = "be.freedelity/scanner/imageStream";
+
+ private var barcodeScannerController: BarcodeScannerController? = null
+ private lateinit var activity: Activity
+ private lateinit var method: MethodChannel
+
+ private lateinit var flutterPluginBinding: FlutterPlugin.FlutterPluginBinding
+
+ override fun onAttachedToEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
+ method = MethodChannel(binding.binaryMessenger, methodChannel)
+ method.setMethodCallHandler(barcodeScannerController)
+
+ flutterPluginBinding = binding;
+ }
+
+ override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
+ method.setMethodCallHandler(null)
+ }
+
+ override fun onDetachedFromActivity() {
+ if (barcodeScannerController != null) {
+ barcodeScannerController!!.stopListening()
+ barcodeScannerController = null
+ }
+ }
+
+ override fun onAttachedToActivity(binding: ActivityPluginBinding) {
+ this.activity = binding.activity
+
+ barcodeScannerController = BarcodeScannerController(
+ activity,
+ flutterPluginBinding.binaryMessenger,
+ methodChannel,
+ scanEventChannel
+ )
+
+ flutterPluginBinding
+ .platformViewRegistry
+ .registerViewFactory(platformViewChannel, BarcodeScannerViewFactory(activity, barcodeScannerController!!))
+ }
+
+ override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {onAttachedToActivity(binding)}
+
+ override fun onDetachedFromActivityForConfigChanges() {onDetachedFromActivity()}
+}
diff --git a/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerView.kt b/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerView.kt
new file mode 100644
index 0000000..28fa993
--- /dev/null
+++ b/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerView.kt
@@ -0,0 +1,53 @@
+// Copyright 2023 Freedelity. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package be.freedelity.barcode_scanner
+
+import android.Manifest
+import android.app.Activity
+import android.content.Context
+import android.content.pm.PackageManager
+import android.view.View
+import androidx.camera.view.PreviewView
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+import io.flutter.plugin.platform.PlatformView
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+
+internal class BarcodeScannerView(activity: Activity, barcodeScannerController: BarcodeScannerController, context: Context, creationParams: Map?) : PlatformView {
+
+ private val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
+ private val previewView: PreviewView = PreviewView(context)
+
+ override fun getView(): View {
+ return previewView
+ }
+
+ override fun dispose() {
+ cameraExecutor.shutdown()
+ }
+
+ init {
+
+ if( allPermissionsGranted(context) ) {
+ barcodeScannerController.startCamera(creationParams, context, previewView, cameraExecutor)
+ } else {
+ ActivityCompat.requestPermissions(activity, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
+ }
+
+ barcodeScannerController.startCamera(creationParams, context, previewView, cameraExecutor)
+ }
+
+ companion object {
+ private const val REQUEST_CODE_PERMISSIONS = 10
+ private val REQUIRED_PERMISSIONS =
+ mutableListOf (
+ Manifest.permission.CAMERA
+ ).toTypedArray()
+ }
+
+ private fun allPermissionsGranted(context: Context) = REQUIRED_PERMISSIONS.all {
+ ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
+ }
+}
diff --git a/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerViewFactory.kt b/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerViewFactory.kt
new file mode 100644
index 0000000..68cc209
--- /dev/null
+++ b/android/src/main/kotlin/be/freedelity/barcode_scanner/BarcodeScannerViewFactory.kt
@@ -0,0 +1,17 @@
+// Copyright 2023 Freedelity. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package be.freedelity.barcode_scanner
+
+import android.app.Activity
+import android.content.Context
+import io.flutter.plugin.common.StandardMessageCodec
+import io.flutter.plugin.platform.PlatformView
+import io.flutter.plugin.platform.PlatformViewFactory
+
+class BarcodeScannerViewFactory(private val activity: Activity, private val barcodeScannerController: BarcodeScannerController) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
+
+ override fun create(context: Context?, viewId: Int, args: Any?): PlatformView {
+ return BarcodeScannerView(activity, barcodeScannerController, context!!, args as Map?)
+ }
+}
diff --git a/android/src/main/kotlin/be/freedelity/barcode_scanner/Constants.kt b/android/src/main/kotlin/be/freedelity/barcode_scanner/Constants.kt
new file mode 100644
index 0000000..e15580b
--- /dev/null
+++ b/android/src/main/kotlin/be/freedelity/barcode_scanner/Constants.kt
@@ -0,0 +1,22 @@
+// Copyright 2023 Freedelity. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Constants for serializing barcode formats in event channel
+// used between dart code and native code.
+//
+// Keep in sync with these other files:
+// - lib/barcode_scanner.dart
+// - ios/Classes/Constants.swift
+
+object BarcodeFormats {
+ const val CODE_39 : Int = 0;
+ const val CODE_93 : Int = 1;
+ const val CODE_128 : Int = 2;
+ const val EAN_8 : Int = 3;
+ const val EAN_13 : Int = 4;
+ const val ITF : Int = 5;
+ const val CODABAR : Int = 6;
+ const val DATAMATRIX : Int = 7;
+ const val QR_CODE : Int = 8;
+}
diff --git a/example/.gitignore b/example/.gitignore
new file mode 100644
index 0000000..248b6e4
--- /dev/null
+++ b/example/.gitignore
@@ -0,0 +1,52 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# 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/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# 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
+
+/android/local.properties
+/android/.gradle/
+/android/gradlew
+/android/gradlew.bat
+/android/gradle/wrapper/gradle-wrapper.jar
\ No newline at end of file
diff --git a/example/.metadata b/example/.metadata
new file mode 100644
index 0000000..0a999ee
--- /dev/null
+++ b/example/.metadata
@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: db747aa1331bd95bc9b3874c842261ca2d302cd5
+ channel: stable
+
+project_type: app
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..3983d57
--- /dev/null
+++ b/example/android/app/build.gradle
@@ -0,0 +1,68 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion flutter.compileSdkVersion
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "be.freedelity.barcode_scanner_example"
+ minSdkVersion 21
+ targetSdkVersion flutter.targetSdkVersion
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..525204c
--- /dev/null
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/android/app/src/main/kotlin/be/freedelity/barcode_scanner_example/MainActivity.kt b/example/android/app/src/main/kotlin/be/freedelity/barcode_scanner_example/MainActivity.kt
new file mode 100644
index 0000000..cccc1d4
--- /dev/null
+++ b/example/android/app/src/main/kotlin/be/freedelity/barcode_scanner_example/MainActivity.kt
@@ -0,0 +1,9 @@
+// Copyright 2023 Freedelity. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package be.freedelity.barcode_scanner_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..db77bb4
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..17987b7
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..09d4391
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..d5f1c8d
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..4d6372e
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..3db14bb
--- /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..d460d1e
--- /dev/null
+++ b/example/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/example/android/build.gradle b/example/android/build.gradle
new file mode 100644
index 0000000..b3b3508
--- /dev/null
+++ b/example/android/build.gradle
@@ -0,0 +1,31 @@
+buildscript {
+ ext.kotlin_version = '1.7.22'
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:7.4.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+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
+}
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..8b635de
--- /dev/null
+++ b/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
diff --git a/example/android/settings.gradle b/example/android/settings.gradle
new file mode 100644
index 0000000..44e62bc
--- /dev/null
+++ b/example/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
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..8d4492f
--- /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
+ 9.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..1e8c3c9
--- /dev/null
+++ b/example/ios/Podfile
@@ -0,0 +1,41 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '9.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)
+ end
+end
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
new file mode 100644
index 0000000..bfb2cc0
--- /dev/null
+++ b/example/ios/Podfile.lock
@@ -0,0 +1,22 @@
+PODS:
+ - Flutter (1.0.0)
+ - barcode_scanner (0.0.1):
+ - Flutter
+
+DEPENDENCIES:
+ - Flutter (from `Flutter`)
+ - barcode_scanner (from `.symlinks/plugins/barcode_scanner/ios`)
+
+EXTERNAL SOURCES:
+ Flutter:
+ :path: Flutter
+ barcode_scanner:
+ :path: ".symlinks/plugins/barcode_scanner/ios"
+
+SPEC CHECKSUMS:
+ Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
+ barcode_scanner: be3711b6ae5992660b0da74e47beb038d939fc59
+
+PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c
+
+COCOAPODS: 1.11.2
diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..48663cf
--- /dev/null
+++ b/example/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,560 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 51;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 2F3D6177F7C34B208A2EA34F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2420880C2DAB07E5DEEC1486 /* Pods_Runner.framework */; };
+ 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 */; };
+/* 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 */
+ 1464E318753945309F3592D3 /* 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 = ""; };
+ 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 = ""; };
+ 2420880C2DAB07E5DEEC1486 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; 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 = ""; };
+ 9B4E83C3C61C0A6550D9CC76 /* 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 = ""; };
+ DA776A6EACA443176FCB381E /* 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 = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 2F3D6177F7C34B208A2EA34F /* Pods_Runner.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 95526FDF5BDD66D005EB7E77 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 2420880C2DAB07E5DEEC1486 /* 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 */,
+ EF752C6E30ECCDA291A2A17C /* Pods */,
+ 95526FDF5BDD66D005EB7E77 /* 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 = "";
+ };
+ EF752C6E30ECCDA291A2A17C /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ DA776A6EACA443176FCB381E /* Pods-Runner.debug.xcconfig */,
+ 1464E318753945309F3592D3 /* Pods-Runner.release.xcconfig */,
+ 9B4E83C3C61C0A6550D9CC76 /* Pods-Runner.profile.xcconfig */,
+ );
+ path = Pods;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 59768B60390FCCA54AEEF3C8 /* [CP] Check Pods Manifest.lock */,
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ 468E531D6A87B1C14984271E /* [CP] Embed Pods Frameworks */,
+ );
+ 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 = 1330;
+ 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 */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 468E531D6A87B1C14984271E /* [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;
+ };
+ 59768B60390FCCA54AEEF3C8 /* [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;
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ 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 = 8Q9A5WHR5L;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = be.freedelity.barcodeScannerExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = 1;
+ 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ 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 = 8Q9A5WHR5L;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = be.freedelity.barcodeScannerExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = 1;
+ 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 = 8Q9A5WHR5L;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = be.freedelity.barcodeScannerExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = 1;
+ 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..2852ce3
--- /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..70693e4
--- /dev/null
+++ b/example/ios/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+import UIKit
+import Flutter
+
+@UIApplicationMain
+@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..d36b1fa
--- /dev/null
+++ b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
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..dc9ada4
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..28c6bf0
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..2ccbfd9
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..f091b6b
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..4cde121
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..d0ef06e
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..dcdc230
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..2ccbfd9
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..c8f9ed8
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..a6d6b86
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..a6d6b86
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..75b2d16
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..c4df70d
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..6a84f41
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..d0e1f58
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..50b28c1
--- /dev/null
+++ b/example/ios/Runner/Info.plist
@@ -0,0 +1,49 @@
+
+
+
+
+ NSCameraUsageDescription
+ Scan barcode and qr code
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Barcode Scanner
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ barcode_scanner_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
+
+
+
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..c14b273
--- /dev/null
+++ b/example/lib/main.dart
@@ -0,0 +1,181 @@
+// Copyright 2023 Freedelity. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:ui';
+
+import 'package:flutter/material.dart';
+import 'package:native_barcode_scanner/barcode_scanner.dart';
+
+
+void main() async {
+
+
+ runZonedGuarded(() async {
+
+ DartPluginRegistrant.ensureInitialized();
+ WidgetsFlutterBinding.ensureInitialized();
+
+ runApp(const MyApp());
+ }, (e, stacktrace) async {
+ debugPrint('$e, $stacktrace');
+ });
+}
+
+class MyApp extends StatefulWidget {
+ const MyApp({Key? key}) : super(key: key);
+
+ @override
+ State createState() => _MyAppState();
+}
+
+enum CameraActions {flipCamera, toggleFlashlight, stopScanner, startScanner, setOverlay }
+
+class _MyAppState extends State {
+ bool withOverlay = true;
+
+ @override
+ void initState() {
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+
+ return MaterialApp(
+ home: Scaffold(
+ appBar: AppBar(
+ title: const Text('Scanner plugin example app'),
+ actions: [
+ PopupMenuButton(
+ onSelected: (CameraActions result) {
+ switch (result) {
+ case CameraActions.flipCamera:
+ BarcodeScanner.flipCamera();
+ break;
+ case CameraActions.toggleFlashlight:
+ BarcodeScanner.toggleFlashlight();
+ break;
+ case CameraActions.stopScanner:
+ BarcodeScanner.stopScanner();
+ break;
+ case CameraActions.startScanner:
+ BarcodeScanner.startScanner();
+ break;
+ case CameraActions.setOverlay:
+ setState(() => withOverlay = !withOverlay);
+ break;
+ }
+ },
+ itemBuilder: (BuildContext context) => >[
+ const PopupMenuItem(
+ value: CameraActions.flipCamera,
+ child: Text('Flip camera'),
+ ),
+ const PopupMenuItem(
+ value: CameraActions.toggleFlashlight,
+ child: Text('Toggle flashlight'),
+ ),
+ const PopupMenuItem(
+ value: CameraActions.stopScanner,
+ child: Text('Stop scanner'),
+ ),
+ const PopupMenuItem(
+ value: CameraActions.startScanner,
+ child: Text('Start scanner'),
+ ),
+ PopupMenuItem(
+ value: CameraActions.setOverlay,
+ child: Text('${withOverlay ? 'Remove' : 'Add'} overlay'),
+ ),
+ ],
+ ),
+ ]
+ ),
+ body: Builder(
+ builder: (builderContext) {
+ Widget child = BarcodeScannerWidget(
+ onBarcodeDetected: (barcode) async {
+ await showDialog(context: builderContext, builder: (dialogContext) {
+ return Align(
+ alignment: Alignment.center,
+ child: Card(
+ margin: const EdgeInsets.all(24),
+ child: Container(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ 'barcode : ${barcode.value}'
+ ),
+ Text(
+ 'format : ${barcode.format.name}'
+ ),
+ ElevatedButton(onPressed: () => Navigator.pop(dialogContext), child: const Text('Close dialog'))
+ ]
+ )
+ )
+ )
+ );
+ });
+ },
+ onError: (dynamic error) {
+ debugPrint('$error');
+ },
+ );
+
+ if (withOverlay) {
+ return buildWithOverlay(builderContext, child);
+ }
+
+ return child;
+ }
+ )
+ )
+ );
+ }
+
+ buildWithOverlay(BuildContext builderContext, Widget scannerWidget) {
+ return Stack(
+ children: [
+ Positioned.fill(child: scannerWidget),
+ Align(
+ alignment: Alignment.center,
+ child: Divider(
+ color: Colors.red[400],
+ thickness: 0.8
+ )
+ ),
+ Center(
+ child: Container(
+ margin: const EdgeInsets.symmetric(horizontal: 64),
+ width: double.infinity,
+ height: 200,
+ decoration: BoxDecoration(
+ border: Border.all(color: Colors.white, width: 2),
+ borderRadius: BorderRadius.circular(15)
+ )
+ )
+ ),
+ Positioned(
+ top: 16,
+ right: 16,
+ child: ElevatedButton(
+ child: const Icon(Icons.refresh, size: 32),
+ style: ButtonStyle(
+ backgroundColor: MaterialStateProperty.all(Colors.purple),
+ foregroundColor: MaterialStateProperty.all(Colors.white),
+ shape: MaterialStateProperty.all(const CircleBorder()),
+ padding: MaterialStateProperty.all(const EdgeInsets.all(8))
+ ),
+ onPressed: () {
+ ScaffoldMessenger.of(builderContext).showSnackBar(const SnackBar(content: Text('Icon button pressed')));
+ }
+ )
+ ),
+ ]
+ );
+ }
+}
diff --git a/example/pubspec.lock b/example/pubspec.lock
new file mode 100644
index 0000000..080c0e2
--- /dev/null
+++ b/example/pubspec.lock
@@ -0,0 +1,196 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ 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: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.17.1"
+ cupertino_icons:
+ dependency: "direct main"
+ description:
+ name: cupertino_icons
+ sha256: "1989d917fbe8e6b39806207df5a3fdd3d816cbd090fac2ce26fb45e9a71476e5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.4"
+ 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_lints:
+ dependency: "direct dev"
+ description:
+ name: flutter_lints
+ sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.4"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ js:
+ dependency: transitive
+ description:
+ name: js
+ sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.6.7"
+ lints:
+ dependency: transitive
+ description:
+ name: lints
+ sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.1"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.12.15"
+ material_color_utilities:
+ dependency: transitive
+ description:
+ name: material_color_utilities
+ sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.0"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.9.1"
+ native_barcode_scanner:
+ dependency: "direct main"
+ description:
+ path: ".."
+ relative: true
+ source: path
+ version: "1.0.0"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.8.3"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.99"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.9.1"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.11.0"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
+ 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: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.5.1"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.4"
+sdks:
+ dart: ">=3.0.0-0 <4.0.0"
+ flutter: ">=3.0.0"
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
new file mode 100644
index 0000000..4e12ab6
--- /dev/null
+++ b/example/pubspec.yaml
@@ -0,0 +1,25 @@
+name: barcode_scanner_example
+description: Demonstrates how to use the barcode_scanner plugin.
+
+publish_to: 'none'
+
+environment:
+ sdk: ">=2.16.1 <3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+
+ native_barcode_scanner:
+ path: ../
+
+ cupertino_icons: ^1.0.2
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+
+ flutter_lints: ^1.0.0
+
+flutter:
+ uses-material-design: true
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/BarcodeScannerController.swift b/ios/Classes/BarcodeScannerController.swift
new file mode 100644
index 0000000..a8d91cd
--- /dev/null
+++ b/ios/Classes/BarcodeScannerController.swift
@@ -0,0 +1,241 @@
+import Foundation
+import Flutter
+import UIKit
+import AVFoundation
+
+class BarcodeScannerController: UIViewController, AVCaptureMetadataOutputObjectsDelegate, FlutterStreamHandler {
+
+ private let supportedCodeTypes = [AVMetadataObject.ObjectType.code39,
+ AVMetadataObject.ObjectType.code93,
+ AVMetadataObject.ObjectType.code128,
+ AVMetadataObject.ObjectType.ean8,
+ AVMetadataObject.ObjectType.ean13,
+ AVMetadataObject.ObjectType.itf14,
+ AVMetadataObject.ObjectType.dataMatrix,
+ AVMetadataObject.ObjectType.qr]
+
+ private var barcodeStream: FlutterEventSink?=nil
+
+ private var videoPreviewLayer: AVCaptureVideoPreviewLayer?
+ private let captureSession = AVCaptureSession()
+ private let captureMetadataOutput = AVCaptureMetadataOutput()
+
+ override func viewDidAppear(_ animated: Bool) {
+ super.viewDidAppear(animated)
+ self.initBarcodeComponents()
+ }
+
+ override public func viewDidDisappear(_ animated: Bool) {
+ captureSession.stopRunning()
+ }
+
+ public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
+ barcodeStream = events
+ return nil
+ }
+
+ public func onCancel(withArguments arguments: Any?) -> FlutterError? {
+ barcodeStream=nil
+ return nil
+ }
+
+ public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
+ switch call.method {
+ case "startScanner":
+ captureSession.startRunning()
+ result(nil)
+ case "stopScanner":
+ captureSession.stopRunning()
+ result(nil)
+ case "toggleTorch":
+ toggleFlash()
+ result(nil)
+ case "flipCamera":
+ switchCamera()
+ result(nil)
+ default:
+ result(FlutterMethodNotImplemented)
+ }
+ }
+
+ // Inititlize components
+ func initBarcodeComponents() {
+
+ do {
+
+ let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back)
+ // Get the back-facing camera for capturing videos
+ guard let captureDevice = deviceDiscoverySession.devices.first else {
+ print("Failed to get the camera device")
+ return
+ }
+
+ // Get an instance of the AVCaptureDeviceInput class using the previous device object.
+ let input = try AVCaptureDeviceInput(device: captureDevice)
+
+ if captureSession.inputs.isEmpty {
+ captureSession.addInput(input)
+ }
+
+ if captureSession.outputs.isEmpty {
+ captureSession.addOutput(captureMetadataOutput)
+ }
+
+ // Set delegate and use the default dispatch queue to execute the call back
+ captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
+ captureMetadataOutput.metadataObjectTypes = supportedCodeTypes
+
+ videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
+ videoPreviewLayer?.videoGravity = .resizeAspectFill
+ videoPreviewLayer?.connection?.videoOrientation = .portrait
+
+ view.contentMode = UIView.ContentMode.scaleAspectFill
+ view.layer.addSublayer(videoPreviewLayer!)
+
+ DispatchQueue.main.async {
+ self.videoPreviewLayer?.frame = self.view.bounds
+ }
+
+ DispatchQueue.global(qos: .userInitiated).async {
+ self.captureSession.startRunning()
+ }
+
+ } catch {
+ // If any error occurs, simply print it out and don't continue any more.
+ print(error)
+ return
+ }
+ }
+
+ public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
+ // Check if the metadataObjects array is not nil and it contains at least one object.
+ if metadataObjects.count == 0 {
+ print("DEBUG: metadataObjects array is nil or doesn't contains any object")
+ return
+ }
+ // Get the metadata object.
+ let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
+ if supportedCodeTypes.contains(metadataObj.type) {
+ if metadataObj.stringValue != nil {
+ barcodeStream?(["barcode": metadataObj.stringValue!, "format": convertBarcodeType(type: metadataObj.type)])
+ }
+ } else {
+ barcodeStream?(["barcode": "", "format": -1])
+ }
+ }
+
+ private func convertBarcodeType(type: AVMetadataObject.ObjectType) -> Int {
+ switch type {
+ case AVMetadataObject.ObjectType.code39:
+ return BarcodeFormats.CODE_39
+ case AVMetadataObject.ObjectType.code93:
+ return BarcodeFormats.CODE_93
+ case AVMetadataObject.ObjectType.code128:
+ return BarcodeFormats.CODE_128
+ case AVMetadataObject.ObjectType.ean8:
+ return BarcodeFormats.EAN_8
+ case AVMetadataObject.ObjectType.ean13:
+ return BarcodeFormats.EAN_13
+ case AVMetadataObject.ObjectType.itf14:
+ return BarcodeFormats.ITF
+ case AVMetadataObject.ObjectType.dataMatrix:
+ return BarcodeFormats.DATAMATRIX
+ case AVMetadataObject.ObjectType.qr:
+ return BarcodeFormats.QR_CODE
+ default:
+ return -1
+ }
+ }
+
+ func toggleFlash() {
+ guard let device = getCaptureDeviceFromCurrentSession(session: captureSession) else {
+ return
+ }
+
+ do {
+ try device.lockForConfiguration()
+
+ if (device.torchMode == AVCaptureDevice.TorchMode.off) {
+ setFlashStatus(device: device, mode: .on)
+ } else {
+ setFlashStatus(device: device, mode: .off)
+ }
+
+ device.unlockForConfiguration()
+ } catch {
+ print(error)
+ }
+ }
+
+ private func setFlashStatus(device: AVCaptureDevice, mode: AVCaptureDevice.TorchMode) {
+ guard device.hasTorch else {
+ return
+ }
+
+ do {
+ try device.lockForConfiguration()
+
+ if (mode == .off) {
+ device.torchMode = AVCaptureDevice.TorchMode.off
+ } else {
+ // Treat .auto & .on equally.
+ do {
+ try device.setTorchModeOn(level: 1.0)
+ } catch {
+ print(error)
+ }
+ }
+
+ device.unlockForConfiguration()
+ } catch {
+ print(error)
+ }
+ }
+
+ private func switchCamera() {
+ // Get the current active input.
+ guard let currentInput = captureSession.inputs.first as? AVCaptureDeviceInput else { return }
+ let newPosition = getInversePosition(position: currentInput.device.position);
+ guard let device = getCaptureDeviceByPosition(position: newPosition) else { return }
+ do {
+ let newInput = try AVCaptureDeviceInput(device: device)
+ // Replace current input with the new one.
+ captureSession.removeInput(currentInput)
+ captureSession.addInput(newInput)
+ // Disable flash by default
+ setFlashStatus(device: device, mode: .off)
+ } catch let error {
+ print(error)
+ return
+ }
+ }
+
+ private func getCaptureDeviceFromCurrentSession(session: AVCaptureSession) -> AVCaptureDevice? {
+ // Get the current active input.
+ guard let currentInput = captureSession.inputs.first as? AVCaptureDeviceInput else { return nil }
+ return currentInput.device;
+ }
+
+ private func getCaptureDeviceByPosition(position: AVCaptureDevice.Position) -> AVCaptureDevice? {
+ // List all capture devices
+ let devices = AVCaptureDevice.DiscoverySession(deviceTypes: [ .builtInWideAngleCamera ], mediaType: AVMediaType.video, position: .unspecified).devices
+ for device in devices {
+ if device.position == position {
+ return device
+ }
+ }
+
+ return nil;
+ }
+
+ private func getInversePosition(position: AVCaptureDevice.Position) -> AVCaptureDevice.Position {
+ if (position == .back) {
+ return AVCaptureDevice.Position.front;
+ }
+ if (position == .front) {
+ return AVCaptureDevice.Position.back;
+ }
+ // Fall back to camera in the back.
+ return AVCaptureDevice.Position.back;
+ }
+}
diff --git a/ios/Classes/BarcodeScannerPlugin.h b/ios/Classes/BarcodeScannerPlugin.h
new file mode 100644
index 0000000..a88e25b
--- /dev/null
+++ b/ios/Classes/BarcodeScannerPlugin.h
@@ -0,0 +1,4 @@
+#import
+
+@interface BarcodeScannerPlugin : NSObject
+@end
diff --git a/ios/Classes/BarcodeScannerPlugin.m b/ios/Classes/BarcodeScannerPlugin.m
new file mode 100644
index 0000000..3f5c2ee
--- /dev/null
+++ b/ios/Classes/BarcodeScannerPlugin.m
@@ -0,0 +1,15 @@
+#import "BarcodeScannerPlugin.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_scanner-Swift.h"
+#endif
+
+@implementation BarcodeScannerPlugin
++ (void)registerWithRegistrar:(NSObject*)registrar {
+ [BarcodeScannerPluginSwift registerWithRegistrar:registrar];
+}
+@end
diff --git a/ios/Classes/BarcodeScannerPluginSwift.swift b/ios/Classes/BarcodeScannerPluginSwift.swift
new file mode 100644
index 0000000..d767231
--- /dev/null
+++ b/ios/Classes/BarcodeScannerPluginSwift.swift
@@ -0,0 +1,31 @@
+import Flutter
+import UIKit
+
+public class BarcodeScannerPluginSwift: NSObject, FlutterPlugin {
+
+ private var cameraController: BarcodeScannerController
+
+ init(cameraController: BarcodeScannerController) {
+ self.cameraController = cameraController
+ }
+
+ public static func register(with registrar: FlutterPluginRegistrar) {
+
+ let viewController = (UIApplication.shared.delegate?.window??.rootViewController)!
+ let cameraController = BarcodeScannerController()
+ let factory = BarcodeScannerViewFactory(mainUIController: viewController, cameraController: cameraController)
+ let instance = BarcodeScannerPluginSwift(cameraController: cameraController)
+
+ registrar.register(factory, withId: "be.freedelity/scanner/view")
+
+ let channel = FlutterMethodChannel(name: "be.freedelity/scanner/method", binaryMessenger: registrar.messenger())
+ registrar.addMethodCallDelegate(instance, channel: channel)
+
+ let eventChannel = FlutterEventChannel(name: "be.freedelity/scanner/imageStream", binaryMessenger: registrar.messenger())
+ eventChannel.setStreamHandler(cameraController)
+ }
+
+ public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
+ cameraController.handle(call, result: result)
+ }
+}
diff --git a/ios/Classes/BarcodeScannerView.swift b/ios/Classes/BarcodeScannerView.swift
new file mode 100644
index 0000000..a2062ea
--- /dev/null
+++ b/ios/Classes/BarcodeScannerView.swift
@@ -0,0 +1,71 @@
+
+import Foundation
+import UIKit
+import AVFoundation
+
+class BarcodeScannerView: NSObject, FlutterPlatformView {
+
+ public var mainUIController: UIViewController
+ private var cameraController: BarcodeScannerController
+
+ init(
+ frame: CGRect,
+ viewIdentifier viewId: Int64,
+ arguments args: Any?,
+ cameraController: BarcodeScannerController,
+ mainUIController: UIViewController
+ ) {
+ self.mainUIController = mainUIController
+ self.cameraController = cameraController
+
+ super.init()
+ }
+
+ func view() -> UIView {
+ if checkCameraAvailability(){
+ if checkForCameraPermission() {
+ return cameraController.view
+ } else {
+ var view = UIView()
+ AVCaptureDevice.requestAccess(for: .video) {
+ success in DispatchQueue.main.sync {
+ if success {
+ view = self.cameraController.view
+ } else {
+ let alert = UIAlertController(title: "Action needed", message: "Please grant camera permission to use barcode scanner", preferredStyle: .alert)
+
+ alert.addAction(UIAlertAction(title: "Grant", style: .default, handler: { action in
+ UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
+ }))
+
+ alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
+
+ self.mainUIController.present(alert, animated: true)
+ }
+ }
+ }
+ return view
+ }
+ } else {
+ showAlertDialog(title: "Unable to proceed", message: "Camera not available")
+ return UIView()
+ }
+ }
+
+ /// Check for camera availability
+ func checkCameraAvailability()->Bool{
+ return UIImagePickerController.isSourceTypeAvailable(.camera)
+ }
+
+ func checkForCameraPermission()->Bool{
+ return AVCaptureDevice.authorizationStatus(for: .video) == .authorized
+ }
+
+ func showAlertDialog(title:String,message:String){
+ let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
+ let alertAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
+ alertController.addAction(alertAction)
+ self.mainUIController.present(alertController, animated: true, completion: nil)
+ }
+
+}
diff --git a/ios/Classes/BarcodeScannerViewFactory.swift b/ios/Classes/BarcodeScannerViewFactory.swift
new file mode 100644
index 0000000..3778d56
--- /dev/null
+++ b/ios/Classes/BarcodeScannerViewFactory.swift
@@ -0,0 +1,28 @@
+import Flutter
+import UIKit
+import AVFoundation
+
+class BarcodeScannerViewFactory: NSObject, FlutterPlatformViewFactory {
+
+ private var mainUIController: UIViewController
+ private var cameraController: BarcodeScannerController
+
+ init(mainUIController: UIViewController, cameraController: BarcodeScannerController) {
+ self.mainUIController = mainUIController
+ self.cameraController = cameraController
+ super.init()
+ }
+
+ func create(
+ withFrame frame: CGRect,
+ viewIdentifier viewId: Int64,
+ arguments args: Any?
+ ) -> FlutterPlatformView {
+ return BarcodeScannerView(
+ frame: frame,
+ viewIdentifier: viewId,
+ arguments: args,
+ cameraController: cameraController,
+ mainUIController: mainUIController)
+ }
+}
diff --git a/ios/Classes/Constants.swift b/ios/Classes/Constants.swift
new file mode 100644
index 0000000..42a79fa
--- /dev/null
+++ b/ios/Classes/Constants.swift
@@ -0,0 +1,22 @@
+// Copyright 2023 Freedelity. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Constants for serializing barcode formats in event channel
+// used between dart code and native code.
+//
+// Keep in sync with these other files:
+// - lib/barcode_scanner.dart
+// - android/src/main/kotlin/be/freedelity/barcode_scanner/Constants.kt
+
+struct BarcodeFormats {
+ static let CODE_39: Int = 0
+ static let CODE_93: Int = 1
+ static let CODE_128: Int = 2
+ static let EAN_8: Int = 3
+ static let EAN_13: Int = 4
+ static let ITF: Int = 5
+ static let CODABAR: Int = 6
+ static let DATAMATRIX: Int = 7
+ static let QR_CODE: Int = 8
+}
diff --git a/ios/barcode_scanner.podspec b/ios/barcode_scanner.podspec
new file mode 100644
index 0000000..718be02
--- /dev/null
+++ b/ios/barcode_scanner.podspec
@@ -0,0 +1,23 @@
+#
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
+# Run `pod lib lint barcode_scanner.podspec` to validate before publishing.
+#
+Pod::Spec.new do |s|
+ s.name = 'barcode_scanner'
+ s.version = '0.0.1'
+ s.summary = 'Barcode scanner plugin'
+ s.description = <<-DESC
+Barcode scanner plugin
+ 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/barcode_scanner.dart b/lib/barcode_scanner.dart
new file mode 100644
index 0000000..a618e06
--- /dev/null
+++ b/lib/barcode_scanner.dart
@@ -0,0 +1,93 @@
+// Copyright 2023 Freedelity. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:flutter/services.dart';
+
+export 'barcode_scanner.widget.dart';
+
+/// List of supported barcode formats.
+enum BarcodeFormat {
+ code39,
+ code93,
+ code128,
+ ean8,
+ ean13,
+ itf,
+ codabar,
+ dataMatrix,
+ qrCode;
+
+ /// @nodoc
+ static BarcodeFormat? unserialize(int constant) {
+ switch(constant) {
+ case _formatCode39:
+ return BarcodeFormat.code39;
+ case _formatCode93:
+ return BarcodeFormat.code93;
+ case _formatCode128:
+ return BarcodeFormat.code128;
+ case _formatEan8:
+ return BarcodeFormat.ean8;
+ case _formatEan13:
+ return BarcodeFormat.ean13;
+ case _formatItf:
+ return BarcodeFormat.itf;
+ case _formatCodabar:
+ return BarcodeFormat.codabar;
+ case _formatDataMatrix:
+ return BarcodeFormat.dataMatrix;
+ case _formatQrCode:
+ return BarcodeFormat.qrCode;
+ default:
+ return null;
+ }
+ }
+}
+
+/// This class encodes a barcode value.
+class Barcode {
+ /// Format of the barcode
+ final BarcodeFormat format;
+
+ /// Value of the barcode
+ final String value;
+
+ Barcode({required this.format, required this.value});
+}
+
+/// This provides static methods to alter how the barcode scanning process.
+abstract class BarcodeScanner {
+
+ static const MethodChannel _channel = MethodChannel('be.freedelity/scanner/method');
+
+ /// This allows to toggle the flashlight.
+ static Future toggleFlashlight() => _channel.invokeMethod('toggleTorch');
+
+ /// Go from back camera to front or vice versa.
+ static Future flipCamera() => _channel.invokeMethod('flipCamera');
+
+ /// Stop the scanner. No barcode will be produced until next call to `BarcodeScanner.startScanner`.
+ static Future stopScanner() => _channel.invokeMethod('stopScanner');
+
+ /// Start the scanning process. It is useful in case `BarcodeScanner.stopScanner` has been called before or if `BarcodeScannerWidget` has been created with `startScanning` set to `false`.
+ static Future startScanner() => _channel.invokeMethod('startScanner');
+}
+
+// Constants for serializing barcode formats in event channel
+// used between dart code and native code.
+//
+// Keep in sync with these other files:
+// - android/src/main/kotlin/be/freedelity/barcode_scanner/Constants.kt
+// - ios/Classes/Constants.swift
+const int _formatCode39 = 0;
+const int _formatCode93 = 1;
+const int _formatCode128 = 2;
+const int _formatEan8 = 3;
+const int _formatEan13 = 4;
+const int _formatItf = 5;
+const int _formatCodabar = 6;
+const int _formatDataMatrix = 7;
+const int _formatQrCode = 8;
diff --git a/lib/barcode_scanner.widget.dart b/lib/barcode_scanner.widget.dart
new file mode 100644
index 0000000..61aebc5
--- /dev/null
+++ b/lib/barcode_scanner.widget.dart
@@ -0,0 +1,124 @@
+// Copyright 2023 Freedelity. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+import 'package:flutter/services.dart';
+
+import 'barcode_scanner.dart';
+
+/// Defines if the camera is at the front or the back of the device
+enum CameraSelector {front, back}
+
+/// Widget displaying the camera stream while scanning barcodes.
+class BarcodeScannerWidget extends StatefulWidget {
+
+ /// Select which camera should be used when creating the widget.
+ final CameraSelector? cameraSelector;
+
+ /// Indicates if the barcode scanning process should start when creating the widget.
+ final bool startScanning;
+
+ /// Indicates if barcode scanning should stop after a barcode is detected. If `false`, `onBarcodeDetected` may be triggered multiple times for the same barcode.
+ final bool stopScanOnBarcodeDetected;
+
+ /// This function will be called when a barcode is detected.
+ final Function(Barcode barcode) onBarcodeDetected;
+
+ final Function(dynamic error) onError;
+
+ const BarcodeScannerWidget({
+ Key? key,
+ this.cameraSelector,
+ this.startScanning = true,
+ this.stopScanOnBarcodeDetected = true,
+ required this.onBarcodeDetected,
+ required this.onError
+ }) : super(key: key);
+
+ @override
+ _BarcodeScannerWidgetState createState() => _BarcodeScannerWidgetState();
+}
+
+class _BarcodeScannerWidgetState extends State {
+
+ static const String platformViewChannel = 'be.freedelity/scanner/view';
+ static const EventChannel eventChannel = EventChannel('be.freedelity/scanner/imageStream');
+
+ late Map creationParams;
+
+ @override
+ void initState() {
+ super.initState();
+
+ creationParams = {
+ 'camera_selector': widget.cameraSelector?.name,
+ 'start_scanning': widget.startScanning
+ };
+
+ eventChannel.receiveBroadcastStream().listen((dynamic event) async {
+ final format = BarcodeFormat.unserialize(event['format']);
+ if( format != null ) {
+ await BarcodeScanner.stopScanner();
+
+ await widget.onBarcodeDetected(Barcode(format: format, value: event['barcode'] as String));
+
+ if(!widget.stopScanOnBarcodeDetected) {
+ BarcodeScanner.startScanner();
+ }
+ }
+
+ }, onError: (dynamic error) {
+ widget.onError(error);
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+
+ if (Platform.isIOS) {
+ return UiKitView(
+ viewType: platformViewChannel,
+ layoutDirection: TextDirection.ltr,
+ creationParams: creationParams,
+ creationParamsCodec: const StandardMessageCodec()
+ );
+ }
+
+ return Stack(
+ children: [
+ PlatformViewLink(
+ viewType: platformViewChannel,
+ surfaceFactory: (context, controller) {
+ return AndroidViewSurface(
+ controller: controller as AndroidViewController,
+ gestureRecognizers: const >{},
+ hitTestBehavior: PlatformViewHitTestBehavior.opaque,
+ );
+ },
+ onCreatePlatformView: (params) {
+ return PlatformViewsService.initExpensiveAndroidView(
+ id: params.id,
+ viewType: platformViewChannel,
+ layoutDirection: TextDirection.ltr,
+ creationParams: creationParams,
+ creationParamsCodec: const StandardMessageCodec(),
+ onFocus: () {
+ params.onFocusChanged(true);
+ }
+ )
+ ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated);
+ }
+ ),
+ const Positioned.fill(
+ child: ModalBarrier(dismissible: false, color: Colors.transparent)
+ )
+ ]
+ );
+ }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
new file mode 100644
index 0000000..9daf16c
--- /dev/null
+++ b/pubspec.yaml
@@ -0,0 +1,26 @@
+name: native_barcode_scanner
+description: Flutter barcode scanner plugin
+version: 1.0.0
+repository: https://github.com/freedelity/flutter_native_barcode_scanner
+
+environment:
+ sdk: ">=2.17.0 <4.0.0"
+ flutter: ">=3.0.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ flutter_lints: ^1.0.0
+
+flutter:
+ plugin:
+ platforms:
+ android:
+ package: be.freedelity.barcode_scanner
+ pluginClass: BarcodeScannerPlugin
+ ios:
+ pluginClass: BarcodeScannerPlugin