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