[
  {
    "path": ".gitignore",
    "content": "# Credential files\n/example/ios/Runner/GoogleService-Info.plist\n/example/ios/Runner/cloudAnchorKey.json\n/example/android/app/google-services.json\n\n# Gitignore entries taken from https://github.com/flutter/flutter/blob/master/.gitignore :\n\n# Miscellaneous\n*.class\n*.lock\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# Visual Studio Code related\n.classpath\n.project\n.settings/\n.vscode/\n\n# Flutter repo-specific\n/bin/cache/\n/bin/internal/bootstrap.bat\n/bin/internal/bootstrap.sh\n/bin/mingit/\n/dev/benchmarks/mega_gallery/\n/dev/bots/.recipe_deps\n/dev/bots/android_tools/\n/dev/devicelab/ABresults*.json\n/dev/docs/doc/\n/dev/docs/flutter.docs.zip\n/dev/docs/lib/\n/dev/docs/pubspec.yaml\n/dev/integration_tests/**/xcuserdata\n/dev/integration_tests/**/Pods\n/packages/flutter/coverage/\nversion\nanalysis_benchmark.json\n\n# packages file containing multi-root paths\n.packages.generated\n\n# Flutter/Dart/Pub related\n**/doc/api/\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n**/generated_plugin_registrant.dart\n.packages\n.pub-cache/\n.pub/\nbuild/\nflutter_*.png\nlinked_*.ds\nunlinked.ds\nunlinked_spec.ds\n\n# Android related\n**/android/**/gradle-wrapper.jar\n**/android/.gradle\n**/android/captures/\n**/android/gradlew\n**/android/gradlew.bat\n**/android/local.properties\n**/android/**/GeneratedPluginRegistrant.java\n**/android/key.properties\n*.jks\n\n# iOS/XCode related\n**/ios/**/*.mode1v3\n**/ios/**/*.mode2v3\n**/ios/**/*.moved-aside\n**/ios/**/*.pbxuser\n**/ios/**/*.perspectivev3\n**/ios/**/*sync/\n**/ios/**/.sconsign.dblite\n**/ios/**/.tags*\n**/ios/**/.vagrant/\n**/ios/**/DerivedData/\n**/ios/**/Icon?\n**/ios/**/Pods/\n**/ios/**/.symlinks/\n**/ios/**/profile\n**/ios/**/xcuserdata\n**/ios/.generated/\n**/ios/Flutter/.last_build_id\n**/ios/Flutter/App.framework\n**/ios/Flutter/Flutter.framework\n**/ios/Flutter/Flutter.podspec\n**/ios/Flutter/Generated.xcconfig\n**/ios/Flutter/app.flx\n**/ios/Flutter/app.zip\n**/ios/Flutter/flutter_assets/\n**/ios/Flutter/flutter_export_environment.sh\n**/ios/ServiceDefinitions.json\n**/ios/Runner/GeneratedPluginRegistrant.*\n\n# macOS\n**/macos/Flutter/GeneratedPluginRegistrant.swift\n\n# Coverage\ncoverage/\n\n# Symbols\napp.*.symbols\n\n# Exceptions to above rules.\n!**/ios/**/default.mode1v3\n!**/ios/**/default.mode2v3\n!**/ios/**/default.pbxuser\n!**/ios/**/default.perspectivev3\n!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages\n!/dev/ci/**/Gemfile.lock\n"
  },
  {
    "path": ".metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: 4b50ca7f7fbf56be72e54cd200825b760416a356\n  channel: beta\n\nproject_type: plugin\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n##0.7.3\n* Update the examples with null-safety\n\n## 0.7.2\n* Fixes missing texturing on iOS\n\n## 0.7.1\n* Adds config to fix iOS cloud anchors not being able to upload\n\n## 0.7.0\n* Adds support to calculate distance between device and anchor and distance between two anchors\n\n## 0.6.5\n* Fixes the 'addNode' function to return true when a node is added to an anchor.\n\n## 0.6.4\n* Flutter 3 compatibility\n\n## 0.6.3\n* The function 'addNode' returned only true. You have now modified it to return false as well.\n* Prevent apps from turning off when errors other than those on your camera occur.\n\n## 0.6.2\n* Slight changes in ```AndroidARView``` dispose methods to prevent memory overflow issues when AR view is closed and reopened multiple times\n\n## 0.6.1\n* Adds ```dispose``` method to ```ARSessionManager``` to prevent memory overflow issues when AR view is closed and reopened multiple times\n\n## 0.6.0\n* Adds handling of two gestures: panning and rotating\n* Adds example showcasing handling and panning of nodes\n* Adds animated coaching overlay for finding planes (uses standard animation from SceneKit/Sceneform on iOS/Android respectively) - active by default, can be turned off using ```ARSessionManager```\n* Updates ```ARCore/CloudAnchors``` to 1.26.0 on iOS\n* Removes overly restrictive permission for background location access on Android\n\n## 0.5.1\n* Removes overly restrictive background location permission on Android\n\n## 0.5.0\n\n* Adds new nodetypes ```fileSystemAppFolderGLB``` and ```fileSystemAppFolderGLTF2``` to load renderables from the device's local storage assigned to the current app\n* Extends the ```localandwebobjectsexample``` of the example app to showcase the new form of model loading\n* Adds snapshot functionality to the session manager to take screenshots of the ARView\n* Adds ```screenshotexample``` to the example app to showcase the snapshot functionality\n* Updates package versions of Flutter packages ```geolocator```, ```permission_handler```, ```vector_math```, and iOS package ```ARCore/CloudAnchors``` in the plugin\n* Updates package versions of Flutter packages ```firebase_core```, ```cloud_firestore```, ```geoflutterfire```, and ```FirebaseSDKVersion ``` on iOS in the example application\n\n## 0.4.3\n\n* Updates documentation after publishing to [pub.dev](https://pub.dev)\n\n## 0.4.2\n\n* Updates documentation\n* Deletes unnecessary files\n\n## 0.4.1\n\n* Adds External Model Management Example: A firebase database is used to store a list of 3D models (name, preview image, URI of the file location in a github repo). These models can be scrolled through and selected from in the example and can then be placed into the scene, uploaded through the Google Cloud Anchor API and downloaded on any other device with the app installed. The downloading user does not need to have the model \"pre-installed\", it is downloaded on the first occasion.\n\n## 0.4.0\n\n* Adds location manager which can be used to query the device's location (including permission and update handling)\n* Adds geoflutterfire to support uploading GPS coordinates alongside anchors and downloading anchors and objects by their location\n* Modifies cloud anchor example: Download button now queries and downloads all anchors along with the corresponding objects within a 100m radius of the device's current location\n* Bugfix: fixes bug on Android causing some examples to crash because the cloud anchor manager wasn't initialized\n\n## 0.3.0\n\n* BREAKING CHANGE: Converts plugin to adhere to Flutter null safety\n* Adds Cloud Anchor functionality (uploading and downloading anchors to/from the Google Cloud Anchor API), including keyless authentication\n* Adds Cloud Anchor example demonstrating how to use Firebase to manage cloud anchor IDs and corresponding data (e.g. on-tap texts)\n* Adds ```data``` member variable to ```ARNode``` as a flexible variable to hold any information associated with the node\n\n## 0.2.1\n\n* Bugfix: Handles singularities in affine transformation matrix deserialization on Android\n\n## 0.2.0\n\n* Adds AR Anchor as a common representation of anchors on all platforms\n* Implements AR Plane Anchor as subclass of AR Anchor: Plane Anchors can be created in Flutter, registered on the target platform and then be used to attach nodes to\n* Adds AR Hittest Result as a common representation of hittest results on all platforms. If the setting is activated in the session manager, taps on the platforms are registered and hit test results can be used to trigger callbacks in Flutter (example: hit result world coordinate transforms can be used to place anchors or nodes into the scene)\n* Adds option to trigger callbacks when nodes are tapped\n* Adds example to showcase hittests, creating and placing anchors and attaching nodes to anchors\n\n## 0.1.0\n\n* Adds AR Node as a common representation of nodes on all platforms\n* Custom objects (GLTF2 models from Flutter's asset folders or GLB models from the Internet) can be attached to nodes and are loaded / downloaded asynchronously during runtime to ensure maximum flexibility\n* AR Nodes (with attached objects) can be placed in the scene with respect to the world coordinate system, position, rotation and scale can be set and updated during runtime\n* Updates debug option functionality: options can be changed at runtime (see Debug Option example)\n* Updates examples to showcase current state of the plugin\n\n## 0.0.1\n\n* Coarse Plugin Architecture layed out\n* ARView supports iOS (ARKit) and Android (ARCore) devices\n* Camera Permission checks added\n* Debug options added on both platforms: Feature Points visualization, detected planes visualization and world origin visualization\n* Adds possibility to use own texture for plane visualization on both platforms\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Lars Carius\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "# ar_flutter_plugin\n[![pub package](https://img.shields.io/pub/v/ar_flutter_plugin.svg)](https://pub.dev/packages/ar_flutter_plugin)\n\nFlutter Plugin for (collaborative) Augmented Reality - Supports ARKit for iOS and ARCore for Android devices.\n\nMany thanks to Oleksandr Leuschenko for the [arkit_flutter_plugin](https://github.com/olexale/arkit_flutter_plugin) and to Gian Marco Di Francesco for the [arcore_flutter_plugin](https://github.com/giandifra/arcore_flutter_plugin) which both served as a great basis and starting point for this project.\n\n## Getting Started\n\n### Installing\n\nAdd the Flutter package to your project by running:\n\n```bash\nflutter pub add ar_flutter_plugin\n```\n\nOr manually add this to your `pubspec.yaml` file (and run `flutter pub get`):\n# ar_flutter_plugin package extension\n\n```yaml\ndependencies:\n  ar_flutter_plugin: ^0.7.3\n```\n\n### Importing\n\nAdd this to your code:\n\n```dart\nimport 'package:ar_flutter_plugin/ar_flutter_plugin.dart';\n```\n\nIf you have problems with permissions on iOS (e.g. with the camera view not showing up even though camera access is allowed), add this to the ```podfile``` of your app's ```ios``` directory:\n\n```pod\n  post_install do |installer|\n    installer.pods_project.targets.each do |target|\n      flutter_additional_ios_build_settings(target)\n      target.build_configurations.each do |config|\n        # Additional configuration options could already be set here\n\n        # BEGINNING OF WHAT YOU SHOULD ADD\n        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [\n          '$(inherited)',\n\n          ## dart: PermissionGroup.camera\n          'PERMISSION_CAMERA=1',\n\n          ## dart: PermissionGroup.photos\n          'PERMISSION_PHOTOS=1',\n\n          ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]\n          'PERMISSION_LOCATION=1',\n\n          ## dart: PermissionGroup.sensors\n          'PERMISSION_SENSORS=1',\n\n          ## dart: PermissionGroup.bluetooth\n          'PERMISSION_BLUETOOTH=1',\n\n          # add additional permission groups if required\n        ]\n        # END OF WHAT YOU SHOULD ADD\n      end\n    end\n  end\n```\n\n\n### Example Applications\n\nTo try out the plugin, it is best to have a look at one of the following examples implemented in the `Example` app:\n\n\n| Example Name                   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | Link to Code                                                                                                                                         |\n| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Debug Options                  | Simple AR scene with toggles to visualize the world origin, feature points and tracked planes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | [Debug Options Code](https://github.com/CariusLars/ar_flutter_plugin/blob/main/example/lib/examples/debugoptionsexample.dart)                        |\n| Local & Online Objects          | AR scene with buttons to place GLTF objects from the flutter asset folders, GLB objects from the internet, or a GLB object from the app's Documents directory at a given position, rotation and scale. Additional buttons allow to modify scale, position and orientation with regard to the world origin after objects have been placed.                                                                                                                                                                                                                                                                | [Local & Online Objects Code](https://github.com/CariusLars/ar_flutter_plugin/blob/main/example/lib/examples/localandwebobjectsexample.dart)         |\n| Objects & Anchors on Planes    | AR Scene in which tapping on a plane creates an anchor with a 3D model attached to it                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | [Objects & Anchors on Planes Code](https://github.com/CariusLars/ar_flutter_plugin/blob/main/example/lib/examples/objectgesturesexample.dart)        |\n| Object Transformation Gestures | Same as Objects & Anchors on Planes example, but objects can be panned and rotated using gestures after being placed                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | [Objects & Anchors on Planes Code](https://github.com/CariusLars/ar_flutter_plugin/blob/main/example/lib/examples/objectsonplanesexample.dart)       |\n|                                |\n| Screenshots                    | Same as Objects & Anchors on Planes Example, but the snapshot function is used to take screenshots of the AR Scene                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | [Screenshots Code](https://github.com/CariusLars/ar_flutter_plugin/blob/main/example/lib/examples/screenshotexample.dart)                            |\n| Cloud Anchors                  | AR Scene in which objects can be placed, uploaded and downloaded, thus creating an interactive AR experience that can be shared between multiple devices. Currently, the example allows to upload the last placed object along with its anchor and download all anchors within a radius of 100m along with all the attached objects (independent of which device originally placed the objects). As sharing the objects is done by using the Google Cloud Anchor Service and Firebase, this requires some additional setup, please read [Getting Started with cloud anchors](cloudAnchorSetup.md)        | [Cloud Anchors Code](https://github.com/CariusLars/ar_flutter_plugin/blob/main/example/lib/examples/cloudanchorexample.dart)                         |\n| External Object Management     | Similar to the Cloud Anchors example, but contains UI to choose between different models. Rather than being hard-coded, an external database (Firestore) is used to manage the available models. As sharing the objects is done by using the Google Cloud Anchor Service and Firebase, this requires some additional setup, please read [Getting Started with cloud anchors](cloudAnchorSetup.md). Also make sure that in your Firestore database, the collection \"models\" contains some entries with the fields \"name\", \"image\", and \"uri\", where \"uri\" points to the raw file of a model in GLB format | [External Model Management Code](https://github.com/CariusLars/ar_flutter_plugin/blob/main/example/lib/examples/externalmodelmanagementexample.dart) |\n\n## Contributing\n\nContributions to this plugin are very welcome. To contribute code and discuss ideas, [create a pull request](https://github.com/CariusLars/ar_flutter_plugin/compare), [open an issue](https://github.com/CariusLars/ar_flutter_plugin/issues/new), or [start a discussion](https://github.com/CariusLars/ar_flutter_plugin/discussions).\n\n## Plugin Architecture\n\nThis is a rough sketch of the architecture the plugin implements:\n\n![ar_plugin_architecture](./AR_Plugin_Architecture_highlevel.svg)\n\n"
  },
  {
    "path": "android/.gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n"
  },
  {
    "path": "android/build.gradle",
    "content": "group 'io.carius.lars.ar_flutter_plugin'\nversion '1.0-SNAPSHOT'\n\nbuildscript {\n    ext.kotlin_version = '1.3.50'\n    repositories {\n        google()\n        jcenter()\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:4.1.0'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n    }\n}\n\nrootProject.allprojects {\n    repositories {\n        google()\n        jcenter()\n    }\n}\n\napply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\n\nandroid {\n    compileSdkVersion 30\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n    }\n    defaultConfig {\n        minSdkVersion 24\n    }\n}\n\ndependencies {\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n    implementation \"com.google.ar:core:1.22.0\"\n\n    implementation 'com.google.ar.sceneform:core:1.15.0'\n    implementation 'com.google.ar.sceneform:assets:1.15.0'\n\n    implementation 'com.google.android.gms:play-services-auth:16+'\n    implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.17.1'\n    implementation 'androidx.appcompat:appcompat:1.3.0'\n}\n\nafterEvaluate {\n    def containsEmbeddingDependencies = false\n    for (def configuration : configurations.all) {\n        for (def dependency : configuration.dependencies) {\n            if (dependency.group == 'io.flutter' &&\n                    dependency.name.startsWith('flutter_embedding') &&\n                    dependency.isTransitive())\n            {\n                containsEmbeddingDependencies = true\n                break\n            }\n        }\n    }\n    if (!containsEmbeddingDependencies) {\n        android {\n            dependencies {\n                def lifecycle_version = \"1.1.1\"\n                compileOnly \"android.arch.lifecycle:runtime:$lifecycle_version\"\n                compileOnly \"android.arch.lifecycle:common:$lifecycle_version\"\n                compileOnly \"android.arch.lifecycle:common-java8:$lifecycle_version\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-6.7-all.zip\n"
  },
  {
    "path": "android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1536M\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
  },
  {
    "path": "android/settings.gradle",
    "content": "rootProject.name = 'ar_flutter_plugin'\n"
  },
  {
    "path": "android/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" package=\"io.carius.lars.ar_flutter_plugin\">\n\n  <uses-permission android:name=\"android.permission.CAMERA\" />\n  <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n\n  <!-- Sceneform requires OpenGL ES 3.0 or later. -->\n  <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n\n  <application>\n    <!-- \"AR Optional\" app, contains non-AR features that can be used when\n         \"Google Play Services for AR\" (ARCore) is not available. -->\n    <meta-data android:name=\"com.google.ar.core\" android:value=\"optional\" />\n  </application>\n\n</manifest>\n"
  },
  {
    "path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/AndroidARView.kt",
    "content": "package io.carius.lars.ar_flutter_plugin\n\nimport android.app.Activity\nimport android.app.Application\nimport android.content.Context\nimport android.graphics.Bitmap\nimport android.net.Uri\nimport android.os.Bundle\nimport android.os.Handler\nimport android.os.HandlerThread\nimport android.util.Log\nimport android.view.MotionEvent\nimport android.view.PixelCopy\nimport android.view.View\nimport android.widget.Toast\nimport com.google.ar.core.*\nimport com.google.ar.core.exceptions.*\nimport com.google.ar.sceneform.*\nimport com.google.ar.sceneform.math.Vector3\nimport com.google.ar.sceneform.ux.*\nimport io.carius.lars.ar_flutter_plugin.Serialization.deserializeMatrix4\nimport io.carius.lars.ar_flutter_plugin.Serialization.serializeAnchor\nimport io.carius.lars.ar_flutter_plugin.Serialization.serializeHitResult\nimport io.carius.lars.ar_flutter_plugin.Serialization.serializePose\nimport io.flutter.FlutterInjector\nimport io.flutter.embedding.engine.loader.FlutterLoader\nimport io.flutter.plugin.common.BinaryMessenger\nimport io.flutter.plugin.common.MethodCall\nimport io.flutter.plugin.common.MethodChannel\nimport io.flutter.plugin.platform.PlatformView\nimport java.io.ByteArrayOutputStream\nimport java.io.IOException\nimport java.nio.FloatBuffer\nimport java.util.concurrent.CompletableFuture\n\nimport android.R\nimport com.google.ar.sceneform.rendering.*\n\nimport android.view.ViewGroup\n\nimport com.google.ar.core.TrackingState\n\n\n\n\n\n\n\n\n\n\n\n\n\n\ninternal class AndroidARView(\n        val activity: Activity,\n        context: Context,\n        messenger: BinaryMessenger,\n        id: Int,\n        creationParams: Map<String?, Any?>?\n) : PlatformView {\n    // constants\n    private val TAG: String = AndroidARView::class.java.name\n    // Lifecycle variables\n    private var mUserRequestedInstall = true\n    lateinit var activityLifecycleCallbacks: Application.ActivityLifecycleCallbacks\n    private val viewContext: Context\n    // Platform channels\n    private val sessionManagerChannel: MethodChannel = MethodChannel(messenger, \"arsession_$id\")\n    private val objectManagerChannel: MethodChannel = MethodChannel(messenger, \"arobjects_$id\")\n    private val anchorManagerChannel: MethodChannel = MethodChannel(messenger, \"aranchors_$id\")\n    // UI variables\n    private lateinit var arSceneView: ArSceneView\n    private lateinit var transformationSystem: TransformationSystem\n    private var showFeaturePoints = false\n    private var showAnimatedGuide = false\n    private lateinit var animatedGuide: View\n    private var pointCloudNode = Node()\n    private var worldOriginNode = Node()\n    // Setting defaults\n    private var enableRotation = false\n    private var enablePans = false\n    private var keepNodeSelected = true;\n    private var footprintSelectionVisualizer = FootprintSelectionVisualizer()\n    // Model builder\n    private var modelBuilder = ArModelBuilder()\n    // Cloud anchor handler\n    private lateinit var cloudAnchorHandler: CloudAnchorHandler\n\n    private lateinit var sceneUpdateListener: com.google.ar.sceneform.Scene.OnUpdateListener\n    private lateinit var onNodeTapListener: com.google.ar.sceneform.Scene.OnPeekTouchListener\n\n    // Method channel handlers\n    private val onSessionMethodCall =\n            object : MethodChannel.MethodCallHandler {\n                override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {\n                    Log.d(TAG, \"AndroidARView onsessionmethodcall reveived a call!\")\n                    when (call.method) {\n                        \"init\" -> {\n                            initializeARView(call, result)\n                        }\n                        \"getAnchorPose\" -> {\n                            val anchorNode = arSceneView.scene.findByName(call.argument(\"anchorId\")) as AnchorNode?\n                            if (anchorNode != null) {\n                                result.success(serializePose(anchorNode.anchor!!.pose))\n                            } else {\n                                result.error(\"Error\", \"could not get anchor pose\", null)\n                            }\n                        }\n                        \"getCameraPose\" -> {\n                            val cameraPose = arSceneView.arFrame?.camera?.displayOrientedPose\n                            if (cameraPose != null) {\n                                result.success(serializePose(cameraPose!!))\n                            } else {\n                                result.error(\"Error\", \"could not get camera pose\", null)\n                            }\n                        }\n                        \"snapshot\" -> {\n                            var bitmap = Bitmap.createBitmap(arSceneView.width, arSceneView.height,\n                                    Bitmap.Config.ARGB_8888);\n\n\n                            // Create a handler thread to offload the processing of the image.\n                            var handlerThread = HandlerThread(\"PixelCopier\");\n                            handlerThread.start();\n                            // Make the request to copy.\n                            PixelCopy.request(arSceneView, bitmap, { copyResult:Int ->\n                                Log.d(TAG, \"PIXELCOPY DONE\")\n                                if (copyResult == PixelCopy.SUCCESS) {\n                                    try {\n                                        val mainHandler = Handler(context.mainLooper)\n                                        val runnable = Runnable {\n                                            val stream = ByteArrayOutputStream()\n                                            bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream)\n                                            val data = stream.toByteArray()\n                                            result.success(data)\n                                        }\n                                        mainHandler.post(runnable)\n                                    } catch (e: IOException) {\n                                        result.error(\"e\", e.message, e.stackTrace);\n                                    }\n                                } else {\n                                    result.error(\"e\", \"failed to take screenshot\", null);\n                                }\n                                handlerThread.quitSafely();\n                            }, Handler(handlerThread.looper));\n                        }\n                        \"dispose\" -> {\n                            dispose()\n                        }\n                        else -> {}\n                    }\n                }\n            }\n    private val onObjectMethodCall =\n            object : MethodChannel.MethodCallHandler {\n                override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {\n                    Log.d(TAG, \"AndroidARView onobjectmethodcall reveived a call!\")\n                    when (call.method) {\n                        \"init\" -> {\n                            // objectManagerChannel.invokeMethod(\"onError\", listOf(\"ObjectTEST from\n                            // Android\"))\n                        }\n                        \"addNode\" -> {\n                            val dict_node: HashMap<String, Any>? = call.arguments as? HashMap<String, Any>\n                            dict_node?.let{\n                                addNode(it).thenAccept{status: Boolean ->\n                                    result.success(status)\n                                }.exceptionally { throwable ->\n                                    result.error(\"e\", throwable.message, throwable.stackTrace)\n                                    null\n                                }\n                            }\n                        }\n                        \"addNodeToPlaneAnchor\" -> {\n                            val dict_node: HashMap<String, Any>? = call.argument<HashMap<String, Any>>(\"node\")\n                            val dict_anchor: HashMap<String, Any>? = call.argument<HashMap<String, Any>>(\"anchor\")\n                            if (dict_node != null && dict_anchor != null) {\n                                addNode(dict_node, dict_anchor).thenAccept{status: Boolean ->\n                                    result.success(status)\n                                }.exceptionally { throwable ->\n                                    result.error(\"e\", throwable.message, throwable.stackTrace)\n                                    null\n                                }\n                            } else {\n                                result.success(false)\n                            }\n\n                        }\n                        \"removeNode\" -> {\n                            val nodeName: String? = call.argument<String>(\"name\")\n                            nodeName?.let{\n                                if (transformationSystem.selectedNode?.name == nodeName){\n                                    transformationSystem.selectNode(null)\n                                    keepNodeSelected = true\n                                }\n                                val node = arSceneView.scene.findByName(nodeName)\n                                node?.let{\n                                    arSceneView.scene.removeChild(node)\n                                    result.success(null)\n                                }\n                            }\n                        }\n                        \"transformationChanged\" -> {\n                            val nodeName: String? = call.argument<String>(\"name\")\n                            val newTransformation: ArrayList<Double>? = call.argument<ArrayList<Double>>(\"transformation\")\n                            nodeName?.let{ name ->\n                                newTransformation?.let{ transform ->\n                                    transformNode(name, transform)\n                                    result.success(null)\n                                }\n                            }\n                        }\n                        else -> {}\n                    }\n                }\n            }\n    private val onAnchorMethodCall =\n            object : MethodChannel.MethodCallHandler {\n                override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {\n                    when (call.method) {\n                        \"addAnchor\" -> {\n                            val anchorType: Int? = call.argument<Int>(\"type\")\n                            if (anchorType != null){\n                                when(anchorType) {\n                                    0 -> { // Plane Anchor\n                                        val transform: ArrayList<Double>? = call.argument<ArrayList<Double>>(\"transformation\")\n                                        val name: String? = call.argument<String>(\"name\")\n                                        if ( name != null && transform != null){\n                                            result.success(addPlaneAnchor(transform, name))\n                                        } else {\n                                            result.success(false)\n                                        }\n\n                                    }\n                                    else -> result.success(false)\n                                }\n                            } else {\n                                result.success(false)\n                            }\n                        }\n                        \"removeAnchor\" -> {\n                            val anchorName: String? = call.argument<String>(\"name\")\n                            anchorName?.let{ name ->\n                                removeAnchor(name)\n                            }\n                        }\n                        \"initGoogleCloudAnchorMode\" -> {\n                            if (arSceneView.session != null) {\n                                val config = Config(arSceneView.session)\n                                config.cloudAnchorMode = Config.CloudAnchorMode.ENABLED\n                                config.updateMode = Config.UpdateMode.LATEST_CAMERA_IMAGE\n                                config.focusMode = Config.FocusMode.AUTO\n                                arSceneView.session?.configure(config)\n\n                                cloudAnchorHandler = CloudAnchorHandler(arSceneView.session!!)\n                            } else {\n                                sessionManagerChannel.invokeMethod(\"onError\", listOf(\"Error initializing cloud anchor mode: Session is null\"))\n                            }\n                        }\n                        \"uploadAnchor\" ->  {\n                            val anchorName: String? = call.argument<String>(\"name\")\n                            val ttl: Int? = call.argument<Int>(\"ttl\")\n                            anchorName?.let {\n                                val anchorNode = arSceneView.scene.findByName(anchorName) as AnchorNode?\n                                if (ttl != null) {\n                                    cloudAnchorHandler.hostCloudAnchorWithTtl(anchorName, anchorNode!!.anchor, cloudAnchorUploadedListener(), ttl!!)\n                                } else {\n                                    cloudAnchorHandler.hostCloudAnchor(anchorName, anchorNode!!.anchor, cloudAnchorUploadedListener())\n                                }\n                                //Log.d(TAG, \"---------------- HOSTING INITIATED ------------------\")\n                                result.success(true)\n                            }\n\n                        }\n                        \"downloadAnchor\" -> {\n                            val anchorId: String? = call.argument<String>(\"cloudanchorid\")\n                            //Log.d(TAG, \"---------------- RESOLVING INITIATED ------------------\")\n                            anchorId?.let {\n                                cloudAnchorHandler.resolveCloudAnchor(anchorId, cloudAnchorDownloadedListener())\n                            }\n                        }\n                        else -> {}\n                    }\n                }\n            }\n\n    override fun getView(): View {\n        return arSceneView\n    }\n\n    override fun dispose() {\n        // Destroy AR session\n        Log.d(TAG, \"dispose called\")\n        try {\n            onPause()\n            onDestroy()\n            ArSceneView.destroyAllResources()\n        } catch (e: Exception) {\n            e.printStackTrace()\n        }\n    }\n\n    init {\n\n        Log.d(TAG, \"Initializing AndroidARView\")\n        viewContext = context\n\n        arSceneView = ArSceneView(context)\n\n        setupLifeCycle(context)\n\n        sessionManagerChannel.setMethodCallHandler(onSessionMethodCall)\n        objectManagerChannel.setMethodCallHandler(onObjectMethodCall)\n        anchorManagerChannel.setMethodCallHandler(onAnchorMethodCall)\n\n        //Original visualizer: com.google.ar.sceneform.ux.R.raw.sceneform_footprint\n\n        MaterialFactory.makeTransparentWithColor(context, Color(255f, 255f, 255f, 0.3f))\n                .thenAccept { mat ->\n                    footprintSelectionVisualizer.footprintRenderable = ShapeFactory.makeCylinder(0.7f,0.05f, Vector3(0f,0f,0f), mat)\n                }\n\n        transformationSystem =\n                TransformationSystem(\n                        activity.resources.displayMetrics,\n                        footprintSelectionVisualizer)\n\n        onResume() // call onResume once to setup initial session\n        // TODO: find out why this does not happen automatically\n    }\n\n    private fun setupLifeCycle(context: Context) {\n        activityLifecycleCallbacks =\n                object : Application.ActivityLifecycleCallbacks {\n                    override fun onActivityCreated(\n                            activity: Activity,\n                            savedInstanceState: Bundle?\n                    ) {\n                        Log.d(TAG, \"onActivityCreated\")\n                    }\n\n                    override fun onActivityStarted(activity: Activity) {\n                        Log.d(TAG, \"onActivityStarted\")\n                    }\n\n                    override fun onActivityResumed(activity: Activity) {\n                        Log.d(TAG, \"onActivityResumed\")\n                        onResume()\n                    }\n\n                    override fun onActivityPaused(activity: Activity) {\n                        Log.d(TAG, \"onActivityPaused\")\n                        onPause()\n                    }\n\n                    override fun onActivityStopped(activity: Activity) {\n                        Log.d(TAG, \"onActivityStopped\")\n                        // onStopped()\n                        onPause()\n                    }\n\n                    override fun onActivitySaveInstanceState(\n                            activity: Activity,\n                            outState: Bundle\n                    ) {}\n\n                    override fun onActivityDestroyed(activity: Activity) {\n                        Log.d(TAG, \"onActivityDestroyed\")\n//                        onPause()\n//                        onDestroy()\n                    }\n                }\n\n        activity.application.registerActivityLifecycleCallbacks(this.activityLifecycleCallbacks)\n    }\n\n    fun onResume() {\n        // Create session if there is none\n        if (arSceneView.session == null) {\n            Log.d(TAG, \"ARSceneView session is null. Trying to initialize\")\n            try {\n                var session: Session?\n                if (ArCoreApk.getInstance().requestInstall(activity, mUserRequestedInstall) ==\n                        ArCoreApk.InstallStatus.INSTALL_REQUESTED) {\n                    Log.d(TAG, \"Install of ArCore APK requested\")\n                    session = null\n                } else {\n                    session = Session(activity)\n                }\n\n                if (session == null) {\n                    // Ensures next invocation of requestInstall() will either return\n                    // INSTALLED or throw an exception.\n                    mUserRequestedInstall = false\n                    return\n                } else {\n                    val config = Config(session)\n                    config.updateMode = Config.UpdateMode.LATEST_CAMERA_IMAGE\n                    config.focusMode = Config.FocusMode.AUTO\n                    session.configure(config)\n                    arSceneView.setupSession(session)\n                }\n            } catch (ex: UnavailableUserDeclinedInstallationException) {\n                // Display an appropriate message to the user zand return gracefully.\n                Toast.makeText(\n                        activity,\n                        \"TODO: handle exception \" + ex.localizedMessage,\n                        Toast.LENGTH_LONG)\n                        .show()\n                return\n            } catch (ex: UnavailableArcoreNotInstalledException) {\n                Toast.makeText(activity, \"Please install ARCore\", Toast.LENGTH_LONG).show()\n                return\n            } catch (ex: UnavailableApkTooOldException) {\n                Toast.makeText(activity, \"Please update ARCore\", Toast.LENGTH_LONG).show()\n                return\n            } catch (ex: UnavailableSdkTooOldException) {\n                Toast.makeText(activity, \"Please update this app\", Toast.LENGTH_LONG).show()\n                return\n            } catch (ex: UnavailableDeviceNotCompatibleException) {\n                Toast.makeText(activity, \"This device does not support AR\", Toast.LENGTH_LONG)\n                        .show()\n                return\n            } catch (e: Exception) {\n                Toast.makeText(activity, \"Failed to create AR session\", Toast.LENGTH_LONG).show()\n                return\n            }\n        }\n\n        try {\n            arSceneView.resume()\n        } catch (ex: CameraNotAvailableException) {\n            Log.d(TAG, \"Unable to get camera\" + ex)\n            activity.finish()\n            return\n        } catch (e : Exception){\n            return\n        }\n    }\n\n    fun onPause() {\n        // hide instructions view if no longer required\n        if (showAnimatedGuide){\n            val view = activity.findViewById(R.id.content) as ViewGroup\n            view.removeView(animatedGuide)\n            showAnimatedGuide = false\n        }\n        arSceneView.pause()\n    }\n\n    fun onDestroy() {\n        try {\n            arSceneView.session?.close()\n            arSceneView.destroy()\n            arSceneView.scene?.removeOnUpdateListener(sceneUpdateListener)\n            arSceneView.scene?.removeOnPeekTouchListener(onNodeTapListener)\n        }catch (e : Exception){\n            e.printStackTrace();\n        }\n    }\n\n    private fun initializeARView(call: MethodCall, result: MethodChannel.Result) {\n        // Unpack call arguments\n        val argShowFeaturePoints: Boolean? = call.argument<Boolean>(\"showFeaturePoints\")\n        val argPlaneDetectionConfig: Int? = call.argument<Int>(\"planeDetectionConfig\")\n        val argShowPlanes: Boolean? = call.argument<Boolean>(\"showPlanes\")\n        val argCustomPlaneTexturePath: String? = call.argument<String>(\"customPlaneTexturePath\")\n        val argShowWorldOrigin: Boolean? = call.argument<Boolean>(\"showWorldOrigin\")\n        val argHandleTaps: Boolean? = call.argument<Boolean>(\"handleTaps\")\n        val argHandleRotation: Boolean? = call.argument<Boolean>(\"handleRotation\")\n        val argHandlePans: Boolean? = call.argument<Boolean>(\"handlePans\")\n        val argShowAnimatedGuide: Boolean? = call.argument<Boolean>(\"showAnimatedGuide\")\n\n\n        sceneUpdateListener = com.google.ar.sceneform.Scene.OnUpdateListener {\n            frameTime: FrameTime -> onFrame(frameTime)\n        }\n        onNodeTapListener = com.google.ar.sceneform.Scene.OnPeekTouchListener { hitTestResult, motionEvent ->\n            //if (hitTestResult.node != null){\n                //transformationSystem.selectionVisualizer.applySelectionVisual(hitTestResult.node as TransformableNode)\n                //transformationSystem.selectNode(hitTestResult.node as TransformableNode)\n            //}\n            if (hitTestResult.node != null && motionEvent?.action == MotionEvent.ACTION_DOWN) {\n                objectManagerChannel.invokeMethod(\"onNodeTap\", listOf(hitTestResult.node?.name))\n            }\n            transformationSystem.onTouch(\n                hitTestResult,\n                motionEvent\n            )\n        }\n\n        arSceneView.scene?.addOnUpdateListener(sceneUpdateListener)\n        arSceneView.scene?.addOnPeekTouchListener(onNodeTapListener)\n\n\n        // Configure Plane scanning guide\n        if (argShowAnimatedGuide == true) { // explicit comparison necessary because of nullable type\n            showAnimatedGuide = true\n            val view = activity.findViewById(R.id.content) as ViewGroup\n            animatedGuide = activity.layoutInflater.inflate(com.google.ar.sceneform.ux.R.layout.sceneform_plane_discovery_layout, null)\n            view.addView(animatedGuide)\n        }\n\n        // Configure feature points\n        if (argShowFeaturePoints ==\n                true) { // explicit comparison necessary because of nullable type\n            arSceneView.scene.addChild(pointCloudNode)\n            showFeaturePoints = true\n        } else {\n            showFeaturePoints = false\n            while (pointCloudNode.children?.size\n                    ?: 0 > 0) {\n                pointCloudNode.children?.first()?.setParent(null)\n            }\n            pointCloudNode.setParent(null)\n        }\n\n        // Configure plane detection\n        val config = arSceneView.session?.config\n        if (config == null) {\n            sessionManagerChannel.invokeMethod(\"onError\", listOf(\"session is null\"))\n        }\n        when (argPlaneDetectionConfig) {\n            1 -> {\n                config?.planeFindingMode = Config.PlaneFindingMode.HORIZONTAL\n            }\n            2 -> {\n                config?.planeFindingMode = Config.PlaneFindingMode.VERTICAL\n            }\n            3 -> {\n                config?.planeFindingMode = Config.PlaneFindingMode.HORIZONTAL_AND_VERTICAL\n            }\n            else -> {\n                config?.planeFindingMode = Config.PlaneFindingMode.DISABLED\n            }\n        }\n        arSceneView.session?.configure(config)\n\n        // Configure whether or not detected planes should be shown\n        arSceneView.planeRenderer.isVisible = if (argShowPlanes == true) true else false\n        // Create custom plane renderer (use supplied texture & increase radius)\n        argCustomPlaneTexturePath?.let {\n            val loader: FlutterLoader = FlutterInjector.instance().flutterLoader()\n            val key: String = loader.getLookupKeyForAsset(it)\n\n            val sampler =\n                    Texture.Sampler.builder()\n                            .setMinFilter(Texture.Sampler.MinFilter.LINEAR)\n                            .setWrapMode(Texture.Sampler.WrapMode.REPEAT)\n                            .build()\n            Texture.builder()\n                    .setSource(viewContext, Uri.parse(key))\n                    .setSampler(sampler)\n                    .build()\n                    .thenAccept { texture: Texture? ->\n                        arSceneView.planeRenderer.material.thenAccept { material: Material ->\n                            material.setTexture(PlaneRenderer.MATERIAL_TEXTURE, texture)\n                            material.setFloat(PlaneRenderer.MATERIAL_SPOTLIGHT_RADIUS, 10f)\n                        }\n                    }\n            // Set radius to render planes in\n            arSceneView.scene.addOnUpdateListener { frameTime: FrameTime? ->\n                val planeRenderer = arSceneView.planeRenderer\n                planeRenderer.material.thenAccept { material: Material ->\n                    material.setFloat(\n                            PlaneRenderer.MATERIAL_SPOTLIGHT_RADIUS,\n                            10f) // Sets the radius in which to visualize planes\n                }\n            }\n        }\n\n        // Configure world origin\n        if (argShowWorldOrigin == true) {\n            worldOriginNode = modelBuilder.makeWorldOriginNode(viewContext)\n            arSceneView.scene.addChild(worldOriginNode)\n        } else {\n            worldOriginNode.setParent(null)\n        }\n\n        // Configure Tap handling\n        if (argHandleTaps == true) { // explicit comparison necessary because of nullable type\n            arSceneView.scene.setOnTouchListener{ hitTestResult: HitTestResult, motionEvent: MotionEvent? -> onTap(hitTestResult, motionEvent) }\n        }\n\n        // Configure gestures\n        if (argHandleRotation ==\n                true) { // explicit comparison necessary because of nullable type\n            enableRotation = true\n        } else {\n            enableRotation = false\n        }\n        if (argHandlePans ==\n                true) { // explicit comparison necessary because of nullable type\n            enablePans = true\n        } else {\n            enablePans = false\n        }\n\n        result.success(null)\n    }\n\n    private fun onFrame(frameTime: FrameTime) {\n        // hide instructions view if no longer required\n        if (showAnimatedGuide && arSceneView.arFrame != null){\n            for (plane in arSceneView.arFrame!!.getUpdatedTrackables(Plane::class.java)) {\n                if (plane.trackingState === TrackingState.TRACKING) {\n                    val view = activity.findViewById(R.id.content) as ViewGroup\n                    view.removeView(animatedGuide)\n                    showAnimatedGuide = false\n                    break\n                }\n            }\n        }\n\n        if (showFeaturePoints) {\n            // remove points from last frame\n            while (pointCloudNode.children?.size\n                    ?: 0 > 0) {\n                pointCloudNode.children?.first()?.setParent(null)\n            }\n            var pointCloud = arSceneView.arFrame?.acquirePointCloud()\n            // Access point cloud data (returns FloatBufferw with x,y,z coordinates and confidence\n            // value).\n            val points = pointCloud?.getPoints() ?: FloatBuffer.allocate(0)\n            // Check if there are any feature points\n            if (points.limit() / 4 >= 1) {\n                for (index in 0 until points.limit() / 4) {\n                    // Add feature point to scene\n                    val featurePoint =\n                            modelBuilder.makeFeaturePointNode(\n                                    viewContext,\n                                    points.get(4 * index),\n                                    points.get(4 * index + 1),\n                                    points.get(4 * index + 2))\n                    featurePoint.setParent(pointCloudNode)\n                }\n            }\n            // Release resources\n            pointCloud?.release()\n        }\n        val updatedAnchors = arSceneView.arFrame!!.updatedAnchors\n        // Notify the cloudManager of all the updates.\n        if (this::cloudAnchorHandler.isInitialized) {cloudAnchorHandler.onUpdate(updatedAnchors)}\n\n        if (keepNodeSelected && transformationSystem.selectedNode != null && transformationSystem.selectedNode!!.isTransforming){\n            // If the selected node is currently transforming, we want to deselect it as soon as the transformation is done\n            keepNodeSelected = false\n        }\n        if (!keepNodeSelected && transformationSystem.selectedNode != null && !transformationSystem.selectedNode!!.isTransforming){\n            // once the transformation is done, deselect the node and allow selection of another node\n            transformationSystem.selectNode(null)\n            keepNodeSelected = true\n        }\n        if (!enablePans && !enableRotation){\n            //unselect all nodes as we do not want the selection visualizer\n            transformationSystem.selectNode(null)\n        }\n\n    }\n\n    private fun addNode(dict_node: HashMap<String, Any>, dict_anchor: HashMap<String, Any>? = null): CompletableFuture<Boolean>{\n        val completableFutureSuccess: CompletableFuture<Boolean> = CompletableFuture()\n\n        try {\n            when (dict_node[\"type\"] as Int) {\n                0 -> { // GLTF2 Model from Flutter asset folder\n                    // Get path to given Flutter asset\n                    val loader: FlutterLoader = FlutterInjector.instance().flutterLoader()\n                    val key: String = loader.getLookupKeyForAsset(dict_node[\"uri\"] as String)\n\n                    // Add object to scene\n                    modelBuilder.makeNodeFromGltf(viewContext, transformationSystem, objectManagerChannel, enablePans, enableRotation, dict_node[\"name\"] as String, key, dict_node[\"transformation\"] as ArrayList<Double>)\n                            .thenAccept{node ->\n                                val anchorName: String? = dict_anchor?.get(\"name\") as? String\n                                val anchorType: Int? = dict_anchor?.get(\"type\") as? Int\n                                if (anchorName != null && anchorType != null) {\n                                    val anchorNode = arSceneView.scene.findByName(anchorName) as AnchorNode?\n                                    if (anchorNode != null) {\n                                        anchorNode.addChild(node)\n                                        completableFutureSuccess.complete(true)\n                                    } else {\n                                        completableFutureSuccess.complete(false)\n                                    }\n                                } else {\n                                    arSceneView.scene.addChild(node)\n                                    completableFutureSuccess.complete(true)\n                                }\n                                completableFutureSuccess.complete(false)\n                            }\n                            .exceptionally { throwable ->\n                                // Pass error to session manager (this has to be done on the main thread if this activity)\n                                val mainHandler = Handler(viewContext.mainLooper)\n                                val runnable = Runnable {sessionManagerChannel.invokeMethod(\"onError\", listOf(\"Unable to load renderable\" +  dict_node[\"uri\"] as String)) }\n                                mainHandler.post(runnable)\n                                completableFutureSuccess.completeExceptionally(throwable)\n                                null // return null because java expects void return (in java, void has no instance, whereas in Kotlin, this closure returns a Unit which has one instance)\n                            }\n                }\n                1 -> { // GLB Model from the web\n                    modelBuilder.makeNodeFromGlb(viewContext, transformationSystem, objectManagerChannel, enablePans, enableRotation, dict_node[\"name\"] as String, dict_node[\"uri\"] as String, dict_node[\"transformation\"] as ArrayList<Double>)\n                            .thenAccept{node ->\n                                val anchorName: String? = dict_anchor?.get(\"name\") as? String\n                                val anchorType: Int? = dict_anchor?.get(\"type\") as? Int\n                                if (anchorName != null && anchorType != null) {\n                                    val anchorNode = arSceneView.scene.findByName(anchorName) as AnchorNode?\n                                    if (anchorNode != null) {\n                                        anchorNode.addChild(node)\n                                        completableFutureSuccess.complete(true)\n                                    } else {\n                                        completableFutureSuccess.complete(false)\n                                    }\n                                } else {\n                                    arSceneView.scene.addChild(node)\n                                    completableFutureSuccess.complete(true)\n                                }\n                                completableFutureSuccess.complete(false)\n                            }\n                            .exceptionally { throwable ->\n                                // Pass error to session manager (this has to be done on the main thread if this activity)\n                                val mainHandler = Handler(viewContext.mainLooper)\n                                val runnable = Runnable {sessionManagerChannel.invokeMethod(\"onError\", listOf(\"Unable to load renderable\" +  dict_node[\"uri\"] as String)) }\n                                mainHandler.post(runnable)\n                                completableFutureSuccess.completeExceptionally(throwable)\n                                null // return null because java expects void return (in java, void has no instance, whereas in Kotlin, this closure returns a Unit which has one instance)\n                            }\n                }\n                2 -> { // fileSystemAppFolderGLB\n                    val documentsPath = viewContext.getApplicationInfo().dataDir\n                    val assetPath = documentsPath + \"/app_flutter/\" + dict_node[\"uri\"] as String\n\n                    modelBuilder.makeNodeFromGlb(viewContext, transformationSystem, objectManagerChannel, enablePans, enableRotation, dict_node[\"name\"] as String, assetPath as String, dict_node[\"transformation\"] as ArrayList<Double>) //\n                            .thenAccept{node ->\n                                val anchorName: String? = dict_anchor?.get(\"name\") as? String\n                                val anchorType: Int? = dict_anchor?.get(\"type\") as? Int\n                                if (anchorName != null && anchorType != null) {\n                                    val anchorNode = arSceneView.scene.findByName(anchorName) as AnchorNode?\n                                    if (anchorNode != null) {\n                                        anchorNode.addChild(node)\n                                        completableFutureSuccess.complete(true)\n                                    } else {\n                                        completableFutureSuccess.complete(false)\n                                    }\n                                } else {\n                                    arSceneView.scene.addChild(node)\n                                    completableFutureSuccess.complete(true)\n                                }\n                                completableFutureSuccess.complete(false)\n                            }\n                            .exceptionally { throwable ->\n                                // Pass error to session manager (this has to be done on the main thread if this activity)\n                                val mainHandler = Handler(viewContext.mainLooper)\n                                val runnable = Runnable {sessionManagerChannel.invokeMethod(\"onError\", listOf(\"Unable to load renderable \" +  dict_node[\"uri\"] as String)) }\n                                mainHandler.post(runnable)\n                                completableFutureSuccess.completeExceptionally(throwable)\n                                null // return null because java expects void return (in java, void has no instance, whereas in Kotlin, this closure returns a Unit which has one instance)\n                            }\n                }\n                3 -> { //fileSystemAppFolderGLTF2\n                    // Get path to given Flutter asset\n                    val documentsPath = viewContext.getApplicationInfo().dataDir\n                    val assetPath = documentsPath + \"/app_flutter/\" + dict_node[\"uri\"] as String\n\n                    // Add object to scene\n                    modelBuilder.makeNodeFromGltf(viewContext, transformationSystem, objectManagerChannel, enablePans, enableRotation, dict_node[\"name\"] as String, assetPath, dict_node[\"transformation\"] as ArrayList<Double>)\n                            .thenAccept{node ->\n                                val anchorName: String? = dict_anchor?.get(\"name\") as? String\n                                val anchorType: Int? = dict_anchor?.get(\"type\") as? Int\n                                if (anchorName != null && anchorType != null) {\n                                    val anchorNode = arSceneView.scene.findByName(anchorName) as AnchorNode?\n                                    if (anchorNode != null) {\n                                        anchorNode.addChild(node)\n                                        completableFutureSuccess.complete(true)\n                                    } else {\n                                        completableFutureSuccess.complete(false)\n                                    }\n                                } else {\n                                    arSceneView.scene.addChild(node)\n                                    completableFutureSuccess.complete(true)\n                                }\n                                completableFutureSuccess.complete(false)\n                            }\n                            .exceptionally { throwable ->\n                                // Pass error to session manager (this has to be done on the main thread if this activity)\n                                val mainHandler = Handler(viewContext.mainLooper)\n                                val runnable = Runnable {sessionManagerChannel.invokeMethod(\"onError\", listOf(\"Unable to load renderable\" +  dict_node[\"uri\"] as String)) }\n                                mainHandler.post(runnable)\n                                completableFutureSuccess.completeExceptionally(throwable)\n                                null // return null because java expects void return (in java, void has no instance, whereas in Kotlin, this closure returns a Unit which has one instance)\n                            }\n                }\n                else -> {\n                    completableFutureSuccess.complete(false)\n                }\n            }\n        } catch (e: java.lang.Exception) {\n            completableFutureSuccess.completeExceptionally(e)\n        }\n\n        return completableFutureSuccess\n    }\n\n    private fun transformNode(name: String, transform: ArrayList<Double>) {\n        val node = arSceneView.scene.findByName(name)\n        node?.let {\n            val transformTriple = deserializeMatrix4(transform)\n            it.localScale = transformTriple.first\n            it.localPosition = transformTriple.second\n            it.localRotation = transformTriple.third\n            //it.worldScale = transformTriple.first\n            //it.worldPosition = transformTriple.second\n            //it.worldRotation = transformTriple.third\n        }\n    }\n\n    private fun onTap(hitTestResult: HitTestResult, motionEvent: MotionEvent?): Boolean {\n        val frame = arSceneView.arFrame\n        if (hitTestResult.node != null && motionEvent?.action == MotionEvent.ACTION_DOWN) {\n            objectManagerChannel.invokeMethod(\"onNodeTap\", listOf(hitTestResult.node?.name))\n            return true\n        }\n        if (motionEvent != null && motionEvent.action == MotionEvent.ACTION_DOWN) {\n            if (transformationSystem.selectedNode == null || (!enablePans && !enableRotation)){\n                val allHitResults = frame?.hitTest(motionEvent) ?: listOf<HitResult>()\n                val planeAndPointHitResults =\n                    allHitResults.filter { ((it.trackable is Plane) || (it.trackable is Point)) }\n                val serializedPlaneAndPointHitResults: ArrayList<HashMap<String, Any>> =\n                    ArrayList(planeAndPointHitResults.map { serializeHitResult(it) })\n                sessionManagerChannel.invokeMethod(\n                    \"onPlaneOrPointTap\",\n                    serializedPlaneAndPointHitResults\n                )\n                return true\n            } else {\n                return false\n            }\n\n        }\n        return false\n    }\n\n    private fun addPlaneAnchor(transform: ArrayList<Double>, name: String): Boolean {\n        return try {\n            val position = floatArrayOf(deserializeMatrix4(transform).second.x, deserializeMatrix4(transform).second.y, deserializeMatrix4(transform).second.z)\n            val rotation = floatArrayOf(deserializeMatrix4(transform).third.x, deserializeMatrix4(transform).third.y, deserializeMatrix4(transform).third.z, deserializeMatrix4(transform).third.w)\n            val anchor: Anchor = arSceneView.session!!.createAnchor(Pose(position, rotation))\n            val anchorNode = AnchorNode(anchor)\n            anchorNode.name = name\n            anchorNode.setParent(arSceneView.scene)\n            true\n        } catch (e: Exception) {\n            false\n        }\n    }\n\n    private fun removeAnchor(name: String) {\n        val anchorNode = arSceneView.scene.findByName(name) as AnchorNode?\n        anchorNode?.let{\n            // Remove corresponding anchor from tracking\n            anchorNode.anchor?.detach()\n            // Remove children\n            for (node in anchorNode.children) {\n                if (transformationSystem.selectedNode?.name == node.name){\n                    transformationSystem.selectNode(null)\n                    keepNodeSelected = true\n                }\n                node.setParent(null)\n            }\n            // Remove anchor node\n            anchorNode.setParent(null)\n        }\n    }\n\n    private inner class cloudAnchorUploadedListener: CloudAnchorHandler.CloudAnchorListener {\n        override fun onCloudTaskComplete(anchorName: String?, anchor: Anchor?) {\n            val cloudState = anchor!!.cloudAnchorState\n            if (cloudState.isError) {\n                Log.e(TAG, \"Error uploading anchor, state $cloudState\")\n                sessionManagerChannel.invokeMethod(\"onError\", listOf(\"Error uploading anchor, state $cloudState\"))\n                return\n            }\n            // Swap old an new anchor of the respective AnchorNode\n            val anchorNode = arSceneView.scene.findByName(anchorName) as AnchorNode?\n            val oldAnchor = anchorNode?.anchor\n            anchorNode?.anchor = anchor\n            oldAnchor?.detach()\n\n            val args = HashMap<String, String?>()\n            args[\"name\"] = anchorName\n            args[\"cloudanchorid\"] = anchor.cloudAnchorId\n            anchorManagerChannel.invokeMethod(\"onCloudAnchorUploaded\", args)\n        }\n    }\n\n    private inner class cloudAnchorDownloadedListener: CloudAnchorHandler.CloudAnchorListener {\n        override fun onCloudTaskComplete(anchorName: String?, anchor: Anchor?) {\n            val cloudState = anchor!!.cloudAnchorState\n            if (cloudState.isError) {\n                Log.e(TAG, \"Error downloading anchor, state $cloudState\")\n                sessionManagerChannel.invokeMethod(\"onError\", listOf(\"Error downloading anchor, state $cloudState\"))\n                return\n            }\n            //Log.d(TAG, \"---------------- RESOLVING SUCCESSFUL ------------------\")\n            val newAnchorNode = AnchorNode(anchor)\n            // Register new anchor on the Flutter side of the plugin\n            anchorManagerChannel.invokeMethod(\"onAnchorDownloadSuccess\", serializeAnchor(newAnchorNode, anchor), object: MethodChannel.Result {\n                override fun success(result: Any?) {\n                    newAnchorNode.name = result.toString()\n                    newAnchorNode.setParent(arSceneView.scene)\n                    //Log.d(TAG, \"---------------- REGISTERING ANCHOR SUCCESSFUL ------------------\")\n                }\n\n                override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {\n                    sessionManagerChannel.invokeMethod(\"onError\", listOf(\"Error while registering downloaded anchor at the AR Flutter plugin: $errorMessage\"))\n                }\n\n                override fun notImplemented() {\n                    sessionManagerChannel.invokeMethod(\"onError\", listOf(\"Error while registering downloaded anchor at the AR Flutter plugin\"))\n                }\n            })\n        }\n    }\n\n}\n\n\n"
  },
  {
    "path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/AndroidARViewFactory.kt",
    "content": "package io.carius.lars.ar_flutter_plugin\n\nimport android.app.Activity\nimport android.content.Context\nimport io.flutter.plugin.common.BinaryMessenger\nimport io.flutter.plugin.common.StandardMessageCodec\nimport io.flutter.plugin.platform.PlatformView\nimport io.flutter.plugin.platform.PlatformViewFactory\n\nclass AndroidARViewFactory(val activity: Activity, val messenger: BinaryMessenger) :\n        PlatformViewFactory(StandardMessageCodec.INSTANCE) {\n    override fun create(context: Context?, viewId: Int, args: Any?): PlatformView {\n        val creationParams = args as Map<String?, Any?>?\n        return AndroidARView(activity, context!!, messenger, viewId, creationParams)\n    }\n}\n"
  },
  {
    "path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/ArFlutterPlugin.kt",
    "content": "package io.carius.lars.ar_flutter_plugin\n\nimport androidx.annotation.NonNull\nimport io.flutter.embedding.engine.plugins.FlutterPlugin\nimport io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding\nimport io.flutter.embedding.engine.plugins.activity.ActivityAware\nimport io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding\nimport io.flutter.plugin.common.MethodCall\nimport io.flutter.plugin.common.MethodChannel\nimport io.flutter.plugin.common.MethodChannel.MethodCallHandler\nimport io.flutter.plugin.common.MethodChannel.Result\n\n/** ArFlutterPlugin */\nclass ArFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {\n  /// The MethodChannel that will the communication between Flutter and native Android\n  ///\n  /// This local reference serves to register the plugin with the Flutter Engine and unregister it\n  /// when the Flutter Engine is detached from the Activity\n  private lateinit var channel: MethodChannel\n  private lateinit var flutterPluginBinding: FlutterPlugin.FlutterPluginBinding\n\n  override fun onAttachedToEngine(\n      @NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding\n  ) {\n    channel = MethodChannel(flutterPluginBinding.binaryMessenger, \"ar_flutter_plugin\")\n    channel.setMethodCallHandler(this)\n\n    this.flutterPluginBinding = flutterPluginBinding\n  }\n\n  override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {\n    if (call.method == \"getPlatformVersion\") {\n      result.success(\"Android ${android.os.Build.VERSION.RELEASE}\")\n    } else {\n      result.notImplemented()\n    }\n  }\n\n  override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {\n    channel.setMethodCallHandler(null)\n  }\n\n  override fun onDetachedFromActivity() {\n    channel.setMethodCallHandler(null)\n  }\n\n  override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {\n    onAttachedToActivity(binding)\n  }\n\n  override fun onAttachedToActivity(binding: ActivityPluginBinding) {\n    this.flutterPluginBinding.platformViewRegistry.registerViewFactory(\n        \"ar_flutter_plugin\", AndroidARViewFactory(binding.activity, flutterPluginBinding.binaryMessenger))\n  }\n\n  override fun onDetachedFromActivityForConfigChanges() {\n    onDetachedFromActivity()\n  }\n}\n"
  },
  {
    "path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/ArModelBuilder.kt",
    "content": "package io.carius.lars.ar_flutter_plugin\n\nimport android.R\nimport android.app.Activity\nimport android.content.Context\nimport com.google.ar.sceneform.Node\nimport com.google.ar.sceneform.math.Vector3\nimport com.google.ar.sceneform.math.Quaternion\nimport com.google.ar.sceneform.assets.RenderableSource\n\nimport java.util.concurrent.CompletableFuture\nimport android.net.Uri\nimport android.view.Gravity\nimport android.widget.Toast\nimport com.google.ar.core.*\nimport com.google.ar.sceneform.ArSceneView\nimport com.google.ar.sceneform.FrameTime\nimport com.google.ar.sceneform.math.MathHelper\nimport com.google.ar.sceneform.rendering.*\nimport com.google.ar.sceneform.utilities.Preconditions\nimport com.google.ar.sceneform.ux.*\n\nimport io.carius.lars.ar_flutter_plugin.Serialization.*\n\nimport io.flutter.FlutterInjector\nimport io.flutter.embedding.engine.loader.FlutterLoader\nimport io.flutter.plugin.common.BinaryMessenger\nimport io.flutter.plugin.common.MethodCall\nimport io.flutter.plugin.common.MethodChannel\nimport java.security.AccessController\n\n\n// Responsible for creating Renderables and Nodes\nclass ArModelBuilder {\n\n    // Creates feature point node\n    fun makeFeaturePointNode(context: Context, xPos: Float, yPos: Float, zPos: Float): Node {\n        val featurePoint = Node()                 \n        var cubeRenderable: ModelRenderable? = null      \n        MaterialFactory.makeOpaqueWithColor(context, Color(android.graphics.Color.YELLOW))\n        .thenAccept { material ->\n            val vector3 = Vector3(0.01f, 0.01f, 0.01f)\n            cubeRenderable = ShapeFactory.makeCube(vector3, Vector3(xPos, yPos, zPos), material)\n            cubeRenderable?.isShadowCaster = false\n            cubeRenderable?.isShadowReceiver = false\n        }\n        featurePoint.renderable = cubeRenderable\n\n        return featurePoint\n    }\n\n    // Creates a coordinate system model at the world origin (X-axis: red, Y-axis: green, Z-axis:blue)\n    // The code for this function is adapted from Alexander's stackoverflow answer (https://stackoverflow.com/questions/48908358/arcore-how-to-display-world-origin-or-axes-in-debug-mode) \n    fun makeWorldOriginNode(context: Context): Node {\n        val axisSize = 0.1f\n        val axisRadius = 0.005f\n\n        val rootNode = Node()\n        val xNode = Node()\n        val yNode = Node()\n        val zNode = Node()\n\n        rootNode.addChild(xNode)\n        rootNode.addChild(yNode)\n        rootNode.addChild(zNode)\n\n        xNode.worldPosition = Vector3(axisSize / 2, 0f, 0f)\n        xNode.worldRotation = Quaternion.axisAngle(Vector3(0f, 0f, 1f), 90f)\n\n        yNode.worldPosition = Vector3(0f, axisSize / 2, 0f)\n\n        zNode.worldPosition = Vector3(0f, 0f, axisSize / 2)\n        zNode.worldRotation = Quaternion.axisAngle(Vector3(1f, 0f, 0f), 90f)\n\n        MaterialFactory.makeOpaqueWithColor(context, Color(255f, 0f, 0f))\n                .thenAccept { redMat ->\n                    xNode.renderable = ShapeFactory.makeCylinder(axisRadius, axisSize, Vector3.zero(), redMat)\n                }\n\n        MaterialFactory.makeOpaqueWithColor(context, Color(0f, 255f, 0f))\n                .thenAccept { greenMat ->\n                    yNode.renderable = ShapeFactory.makeCylinder(axisRadius, axisSize, Vector3.zero(), greenMat)\n                }\n\n        MaterialFactory.makeOpaqueWithColor(context, Color(0f, 0f, 255f))\n                .thenAccept { blueMat ->\n                    zNode.renderable = ShapeFactory.makeCylinder(axisRadius, axisSize, Vector3.zero(), blueMat)\n                }\n\n        return rootNode\n    }\n\n    // Creates a node form a given gltf model path or URL. The gltf asset loading in Scenform is asynchronous, so the function returns a completable future of type Node\n    fun makeNodeFromGltf(context: Context, transformationSystem: TransformationSystem, objectManagerChannel: MethodChannel, enablePans: Boolean, enableRotation: Boolean, name: String, modelPath: String, transformation: ArrayList<Double>): CompletableFuture<CustomTransformableNode> {\n        val completableFutureNode: CompletableFuture<CustomTransformableNode> = CompletableFuture()\n\n        val gltfNode = CustomTransformableNode(transformationSystem, objectManagerChannel, enablePans, enableRotation)\n\n        ModelRenderable.builder()\n                .setSource(context, RenderableSource.builder().setSource(\n                        context,\n                        Uri.parse(modelPath),\n                        RenderableSource.SourceType.GLTF2)\n                        .build())\n                .setRegistryId(modelPath)\n                .build()\n                .thenAccept{ renderable ->\n                    gltfNode.renderable = renderable\n                    gltfNode.name = name\n                    val transform = deserializeMatrix4(transformation)\n                    gltfNode.worldScale = transform.first\n                    gltfNode.worldPosition = transform.second\n                    gltfNode.worldRotation = transform.third\n                    completableFutureNode.complete(gltfNode)\n                }\n                .exceptionally { throwable ->\n                    completableFutureNode.completeExceptionally(throwable)\n                    null // return null because java expects void return (in java, void has no instance, whereas in Kotlin, this closure returns a Unit which has one instance)\n                }\n\n    return completableFutureNode\n    }\n\n    // Creates a node form a given glb model path or URL. The gltf asset loading in Sceneform is asynchronous, so the function returns a compleatable future of type Node\n    fun makeNodeFromGlb(context: Context, transformationSystem: TransformationSystem, objectManagerChannel: MethodChannel, enablePans: Boolean, enableRotation: Boolean, name: String, modelPath: String, transformation: ArrayList<Double>): CompletableFuture<CustomTransformableNode> {\n        val completableFutureNode: CompletableFuture<CustomTransformableNode> = CompletableFuture()\n\n        val gltfNode = CustomTransformableNode(transformationSystem, objectManagerChannel, enablePans, enableRotation)\n        //gltfNode.scaleController.isEnabled = false\n        //gltfNode.translationController.isEnabled = false\n\n        /*gltfNode.removeTransformationController(translationController)\n        gltfNode.addTra\n        val customTranslationController = DragController(\n            gltfNode,\n            transformationSystem.dragRecognizer,\n            objectManagerChannel,\n            transformationSystem\n        )*/\n\n        ModelRenderable.builder()\n                .setSource(context, RenderableSource.builder().setSource(\n                        context,\n                        Uri.parse(modelPath),\n                        RenderableSource.SourceType.GLB)\n                        .build())\n                .setRegistryId(modelPath)\n                .build()\n                .thenAccept{ renderable ->\n                    gltfNode.renderable = renderable\n                    gltfNode.name = name\n                    val transform = deserializeMatrix4(transformation)\n                    gltfNode.worldScale = transform.first\n                    gltfNode.worldPosition = transform.second\n                    gltfNode.worldRotation = transform.third\n                    completableFutureNode.complete(gltfNode)\n                }\n                .exceptionally{throwable ->\n                    completableFutureNode.completeExceptionally(throwable)\n                    null // return null because java expects void return (in java, void has no instance, whereas in Kotlin, this closure returns a Unit which has one instance)\n                }\n\n        return completableFutureNode\n    }\n}\n\nclass CustomTransformableNode(transformationSystem: TransformationSystem, objectManagerChannel: MethodChannel, enablePans: Boolean, enableRotation: Boolean) :\n    TransformableNode(transformationSystem) { //\n\n    private lateinit var customTranslationController: CustomTranslationController\n\n    private lateinit var customRotationController: CustomRotationController\n\n    init {\n        // Remove standard controllers\n        translationController.isEnabled = false\n        rotationController.isEnabled = false\n        scaleController.isEnabled = false\n        removeTransformationController(translationController)\n        removeTransformationController(rotationController)\n        removeTransformationController(scaleController)\n\n\n        // Add custom controllers if needed\n        if (enablePans) {\n            customTranslationController = CustomTranslationController(\n                this,\n                transformationSystem.dragRecognizer,\n                objectManagerChannel\n            )\n            addTransformationController(customTranslationController)\n        }\n        if (enableRotation) {\n            customRotationController = CustomRotationController(\n                this,\n                transformationSystem.twistRecognizer,\n                objectManagerChannel\n            )\n            addTransformationController(customRotationController)\n        }\n    }\n}\n\nclass CustomTranslationController(transformableNode: BaseTransformableNode, gestureRecognizer: DragGestureRecognizer, objectManagerChannel: MethodChannel) :\n    TranslationController(transformableNode, gestureRecognizer) {\n\n    val platformChannel: MethodChannel = objectManagerChannel\n\n    override fun canStartTransformation(gesture: DragGesture): Boolean {\n        platformChannel.invokeMethod(\"onPanStart\", transformableNode.name)\n        super.canStartTransformation(gesture)\n        return transformableNode.isSelected\n    }\n\n    override fun onContinueTransformation(gesture: DragGesture) {\n        platformChannel.invokeMethod(\"onPanChange\", transformableNode.name)\n        super.onContinueTransformation(gesture)\n        }\n\n    override fun onEndTransformation(gesture: DragGesture) {\n        val serializedLocalTransformation = serializeLocalTransformation(transformableNode)\n        platformChannel.invokeMethod(\"onPanEnd\", serializedLocalTransformation)\n        super.onEndTransformation(gesture)\n     }\n}\n\nclass CustomRotationController(transformableNode: BaseTransformableNode, gestureRecognizer: TwistGestureRecognizer, objectManagerChannel: MethodChannel) :\n    RotationController(transformableNode, gestureRecognizer) {\n\n    val platformChannel: MethodChannel = objectManagerChannel\n\n    override fun canStartTransformation(gesture: TwistGesture): Boolean {\n        platformChannel.invokeMethod(\"onRotationStart\", transformableNode.name)\n        super.canStartTransformation(gesture)\n        return transformableNode.isSelected\n    }\n\n    override fun onContinueTransformation(gesture: TwistGesture) {\n        platformChannel.invokeMethod(\"onRotationChange\", transformableNode.name)\n        super.onContinueTransformation(gesture)\n    }\n\n    override fun onEndTransformation(gesture: TwistGesture) {\n        val serializedLocalTransformation = serializeLocalTransformation(transformableNode)\n        platformChannel.invokeMethod(\"onRotationEnd\", serializedLocalTransformation)\n        super.onEndTransformation(gesture)\n     }\n}\n"
  },
  {
    "path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/CloudAnchorHandler.kt",
    "content": "package io.carius.lars.ar_flutter_plugin\n\nimport com.google.ar.core.Anchor\nimport com.google.ar.core.Anchor.CloudAnchorState\nimport com.google.ar.core.Session\nimport java.util.*\n\n// Class for handling logic regarding the Google Cloud Anchor API\ninternal class CloudAnchorHandler( arSession: Session ) {\n\n    // Listener that can be attached to hosing or resolving processes\n    interface CloudAnchorListener {\n        // Callback to invoke when cloud anchor task finishes\n        fun onCloudTaskComplete(anchorName: String?, anchor: Anchor?)\n    }\n\n    private val TAG: String = CloudAnchorHandler::class.java.simpleName\n    private val pendingAnchors = HashMap<Anchor, Pair<String?, CloudAnchorListener?>>()\n    private val session: Session = arSession\n\n    @Synchronized\n    fun hostCloudAnchor(anchorName: String, anchor: Anchor?, listener: CloudAnchorListener?) {\n        val newAnchor = session.hostCloudAnchor(anchor)\n        // Register listener so it is invoked when the operation finishes\n        pendingAnchors[newAnchor] = Pair(anchorName, listener)\n    }\n\n    @Synchronized\n    fun hostCloudAnchorWithTtl(anchorName: String, anchor: Anchor?, listener: CloudAnchorListener?, ttl: Int) {\n        val newAnchor = session.hostCloudAnchorWithTtl(anchor, ttl)\n        // Register listener so it is invoked when the operation finishes\n        pendingAnchors[newAnchor] = Pair(anchorName, listener)\n    }\n\n    @Synchronized\n    fun resolveCloudAnchor(anchorId: String?, listener: CloudAnchorListener?) {\n        val newAnchor = session.resolveCloudAnchor(anchorId)\n        // Register listener so it is invoked when the operation finishes\n        pendingAnchors[newAnchor] = Pair(null, listener)\n    }\n\n    // Updating function that should be called after each session.update call\n    @Synchronized\n    fun onUpdate(updatedAnchors: Collection<Anchor>) {\n        for (anchor in updatedAnchors) {\n            if (pendingAnchors.containsKey(anchor)) {\n                if (anchor.cloudAnchorState != CloudAnchorState.NONE && anchor.cloudAnchorState != CloudAnchorState.TASK_IN_PROGRESS){\n                    val element: Pair<String?, CloudAnchorListener?>? = pendingAnchors.remove(anchor)\n                    element!!.second!!.onCloudTaskComplete(element.first, anchor)\n                }\n            }\n        }\n    }\n\n    // Remove all listeners\n    @Synchronized\n    fun clearListeners() {\n        pendingAnchors.clear()\n    }\n\n}\n"
  },
  {
    "path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/Serialization/Deserializers.kt",
    "content": "package io.carius.lars.ar_flutter_plugin.Serialization\n\nimport com.google.ar.sceneform.math.Quaternion\nimport com.google.ar.sceneform.math.Vector3\n\nfun deserializeMatrix4(transform: ArrayList<Double>): Triple<Vector3, Vector3, Quaternion> {\n  val scale = Vector3()\n  val position = Vector3()\n  val rotation: Quaternion\n\n  // Get the scale by calculating the length of each 3-dimensional column vector of the\n  // transformation matrix\n  // See\n  // https://math.stackexchange.com/questions/237369/given-this-transformation-matrix-how-do-i-decompose-it-into-translation-rotati for a mathematical explanation\n  scale.x = Vector3(transform[0].toFloat(), transform[1].toFloat(), transform[2].toFloat()).length()\n  scale.y = Vector3(transform[4].toFloat(), transform[5].toFloat(), transform[6].toFloat()).length()\n  scale.z =\n      Vector3(transform[8].toFloat(), transform[9].toFloat(), transform[10].toFloat()).length()\n\n  // Get the translation by taking the last column of the transformation matrix\n  // See\n  // https://math.stackexchange.com/questions/237369/given-this-transformation-matrix-how-do-i-decompose-it-into-translation-rotati for a mathematical explanation\n  position.x = transform[12].toFloat()\n  position.y = transform[13].toFloat()\n  position.z = transform[14].toFloat()\n\n  // Get the rotation matrix from the transformation matrix by normalizing with the scales\n  // See\n  // https://math.stackexchange.com/questions/237369/given-this-transformation-matrix-how-do-i-decompose-it-into-translation-rotati for a mathematical explanation\n  val rowWiseMatrix =\n      floatArrayOf(\n          transform[0].toFloat() / scale.x,\n          transform[4].toFloat() / scale.y,\n          transform[8].toFloat() / scale.z,\n          transform[1].toFloat() / scale.x,\n          transform[5].toFloat() / scale.y,\n          transform[9].toFloat() / scale.z,\n          transform[2].toFloat() / scale.x,\n          transform[6].toFloat() / scale.y,\n          transform[10].toFloat() / scale.z)\n\n  // Calculate the quaternion from the rotation matrix\n  // See https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/ for\n  // a mathematical explanation\n  val trace = rowWiseMatrix[0] + rowWiseMatrix[4] + rowWiseMatrix[8]\n\n  var w = 0.0\n  var x = 0.0\n  var y = 0.0\n  var z = 0.0\n\n  if (trace > 0) {\n    val scalefactor = Math.sqrt(trace + 1.0) * 2\n    w = 0.25 * scalefactor\n    x = (rowWiseMatrix[7] - rowWiseMatrix[5]) / scalefactor\n    y = (rowWiseMatrix[2] - rowWiseMatrix[6]) / scalefactor\n    z = (rowWiseMatrix[3] - rowWiseMatrix[1]) / scalefactor\n  } else if ((rowWiseMatrix[0] > rowWiseMatrix[4]) && (rowWiseMatrix[0] > rowWiseMatrix[8])) {\n    val scalefactor = Math.sqrt(1.0 + rowWiseMatrix[0] - rowWiseMatrix[4] - rowWiseMatrix[8]) * 2\n    w = (rowWiseMatrix[7] - rowWiseMatrix[5]) / scalefactor\n    x = 0.25 * scalefactor\n    y = (rowWiseMatrix[1] + rowWiseMatrix[3]) / scalefactor\n    z = (rowWiseMatrix[2] + rowWiseMatrix[6]) / scalefactor\n  } else if (rowWiseMatrix[4] > rowWiseMatrix[8]) {\n    val scalefactor = Math.sqrt(1.0 + rowWiseMatrix[4] - rowWiseMatrix[0] - rowWiseMatrix[8]) * 2\n    w = (rowWiseMatrix[2] - rowWiseMatrix[6]) / scalefactor\n    x = (rowWiseMatrix[1] + rowWiseMatrix[3]) / scalefactor\n    y = 0.25 * scalefactor\n    z = (rowWiseMatrix[5] + rowWiseMatrix[7]) / scalefactor\n  } else {\n    val scalefactor = Math.sqrt(1.0 + rowWiseMatrix[8] - rowWiseMatrix[0] - rowWiseMatrix[4]) * 2\n    w = (rowWiseMatrix[3] - rowWiseMatrix[1]) / scalefactor\n    x = (rowWiseMatrix[2] + rowWiseMatrix[6]) / scalefactor\n    y = (rowWiseMatrix[5] + rowWiseMatrix[7]) / scalefactor\n    z = 0.25 * scalefactor\n  }\n\n  val inputRotation = Quaternion(x.toFloat(), y.toFloat(), z.toFloat(), w.toFloat())\n\n  // Rotate by an additional 180 degrees around z and y to compensate for the different model\n  // coordinate system definition used in Sceneform (in comparison to Scenekit and the definition\n  // used for the Flutter API of this plugin)\n  val correction_z = Quaternion(0.0f, 0.0f, 1.0f, 180f)\n  val correction_y = Quaternion(0.0f, 1.0f, 0.0f, 180f)\n\n  // Calculate resulting rotation quaternion by multiplying input and corrections\n  rotation = Quaternion.multiply(Quaternion.multiply(inputRotation, correction_y), correction_z)\n\n  return Triple(scale, position, rotation)\n}\n"
  },
  {
    "path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/Serialization/Serializers.kt",
    "content": "package io.carius.lars.ar_flutter_plugin.Serialization\n\nimport com.google.ar.core.*\nimport com.google.ar.sceneform.AnchorNode\nimport com.google.ar.sceneform.math.Matrix\nimport com.google.ar.sceneform.math.Quaternion\nimport com.google.ar.sceneform.math.Vector3\nimport com.google.ar.sceneform.ux.BaseTransformableNode\nimport com.google.ar.sceneform.ux.TransformableNode\n\nfun serializeHitResult(hitResult: HitResult): HashMap<String, Any> {\n    val serializedHitResult = HashMap<String,Any>()\n\n    if (hitResult.trackable is Plane && (hitResult.trackable as Plane).isPoseInPolygon(hitResult.hitPose)) {\n        serializedHitResult[\"type\"] = 1 // Type plane\n    }\n    else if (hitResult.trackable is Point){\n        serializedHitResult[\"type\"] = 2 // Type point\n    } else {\n        serializedHitResult[\"type\"] = 0 // Type undefined\n    }\n\n    serializedHitResult[\"distance\"] = hitResult.distance.toDouble()\n    serializedHitResult[\"worldTransform\"] = serializePose(hitResult.hitPose)\n\n    return serializedHitResult\n}\n\nfun serializePose(pose: Pose): DoubleArray {\n    val serializedPose = FloatArray(16)\n    pose.toMatrix(serializedPose, 0)\n    // copy into double Array\n    val serializedPoseDouble = DoubleArray(serializedPose.size)\n    for (i in serializedPose.indices) {\n        serializedPoseDouble[i] = serializedPose[i].toDouble()\n    }\n    return serializedPoseDouble\n}\n\nfun serializePoseWithScale(pose: Pose, scale: Vector3): DoubleArray {\n    val serializedPose = FloatArray(16)\n    pose.toMatrix(serializedPose, 0)\n    // copy into double Array\n    val serializedPoseDouble = DoubleArray(serializedPose.size)\n    for (i in serializedPose.indices) {\n        serializedPoseDouble[i] = serializedPose[i].toDouble()\n        if (i == 0 || i == 4 || i == 8){\n            serializedPoseDouble[i] = serializedPoseDouble[i] * scale.x\n        }\n        if (i == 1 || i == 5 || i == 9){\n            serializedPoseDouble[i] = serializedPoseDouble[i] * scale.y\n        }\n        if (i == 2 || i == 7 || i == 10){\n            serializedPoseDouble[i] = serializedPoseDouble[i] * scale.z\n        }\n    }\n    return serializedPoseDouble\n}\n\nfun serializeAnchor(anchorNode: AnchorNode, anchor: Anchor?): HashMap<String, Any?> {\n    val serializedAnchor = HashMap<String, Any?>()\n    serializedAnchor[\"type\"] = 0 // index for plane anchors\n    serializedAnchor[\"name\"] = anchorNode.name\n    serializedAnchor[\"cloudanchorid\"] = anchor?.cloudAnchorId\n    serializedAnchor[\"transformation\"] = if (anchor != null) serializePose(anchor.pose) else null\n    serializedAnchor[\"childNodes\"] = anchorNode.children.map { child -> child.name }\n\n    return serializedAnchor\n}\n\nfun serializeLocalTransformation(node: BaseTransformableNode): HashMap<String, Any>{\n    val serializedLocalTransformation = HashMap<String, Any>()\n    serializedLocalTransformation[\"name\"] = node.name\n\n    val transform = Pose(floatArrayOf(node.localPosition.x, node.localPosition.y, node.localPosition.z), floatArrayOf(node.localRotation.x, node.localRotation.y, node.localRotation.z, node.localRotation.w))\n\n    serializedLocalTransformation[\"transform\"] = serializePoseWithScale(transform, node.localScale)\n\n    return serializedLocalTransformation\n}\n"
  },
  {
    "path": "cloudAnchorSetup.md",
    "content": "# Getting Started with Cloud Anchors\n\nThe usual method of authentication is setting an API key. However, this authentication method only allows to host anchors with a maximum lifetime of 24 hours. OAuth 2.0 authentication allows saving uploaded anchors for up to 365 days and needs to be set up to use the plugin.\nFollow the steps below to set up your application.\n\n## Set up Google Cloud Anchor Service\n\nThe Google Cloud Anchor API is used by the plugin to upload, store and download AR anchors. If your app uses the plugin's shared AR experience features, the following setup steps are required:\n\n1. Activate the [Cloud Anchor API](https://console.cloud.google.com/apis/api/arcorecloudanchor.googleapis.com) in your [Google Cloud Console](https://console.cloud.google.com) for the respective project\n2. Register the Android part of your Flutter Application\n   * Perform the following steps to create a OAuth2 project (based on the [Android Cloud Anchors Developer Guide](https://developers.google.com/ar/develop/java/cloud-anchors/developer-guide-android?hl=en) and the [Guide for setting up OAuth 2.0](https://support.google.com/cloud/answer/6158849#zippy=)):\n     * Go to the [Google Cloud Platform Console](https://console.cloud.google.com).\n     * If the APIs & services page isn't already open, open the console left side menu and select APIs & services.\n     * On the left, click Credentials.\n     * Click New Credentials, then select OAuth client ID.\n     * Select \"Android\" as the Application type\n     * Fill in an arbitrary name and make sure the field \"Package name\" matches the package name in the ```AndroidManifest.xml``` of the Android part of your Flutter application\n     * Fill in the SHA-1 certificate fingerprint. If you're still in development, you can get the debug keystore key for the Android app by executing ```keytool -keystore ~/.android/debug.keystore -list -v``` in your terminal\n     * Click Create client ID\n     * If this is your first time creating a client ID, you have to configure your consent screen by clicking Consent Screen. The following procedure explains how to set up the Consent screen. You won't be prompted to configure the consent screen after you do it the first time.\n       * Go to the Google API Console [OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent) page.\n       * Add required information like a product name and support email address.\n       * Click Add Scope.\n       * On the dialog that appears, select the ```ARCore Cloud Anchor API```scopes and any additional ones your project uses. Sensitive scopes display a lock icon next to the API name. (To select scopes for registration, you need to enable the API, like Drive or Gmail, from APIs & Services > API Library. You must select all scopes used by the project.)\n       * Finish the remaining steps of the OAuth consent screen setup.\n   * Enable keyless authentication in the Android part of your Flutter app (if you use the sample app of this plugin as a starting point, these steps are already done; the following steps are based on the [Android Cloud Anchors Developer Guide](https://developers.google.com/ar/develop/java/cloud-anchors/developer-guide-android?hl=en)):\n     * Add the dependency ```implementation 'com.google.android.gms:play-services-auth:16+'``` to the ```build.gradle``` file\n     * If you are using [ProGuard](https://www.guardsquare.com/en/products/proguard), add it to your app’s build.gradle file with\n  \n        ```java\n        buildTypes {\n          release {\n            ...\n              proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n            }\n          }\n        ```\n     * And add the following to your app’s proguard-rules.pro file:\n\n\n        ```java\n        -keep class com.google.android.gms.common.** { *; }\n        -keep class com.google.android.gms.auth.** { *; }\n        -keep class com.google.android.gms.tasks.** { *; }\n        ```\n\n3. Register the iOS part of your Flutter Application\n   * Perform the following steps to create a Google Service account and signing key (based on the [iOS Cloud Anchor Developer Guide](https://developers.google.com/ar/develop/ios/cloud-anchors/developer-guide?hl=en)):\n     * In the navigation menu of the Google Cloud Platform console, go to APIs & Services > Credentials.\n     * Select the desired project, then click Create Credentials > Service account.\n     * Under Service account details, type a name for the new account, then click Create.\n     * On the Service account permissions page, go to the Select a role dropdown. Select Service Accounts > Service Account Token Creator, then click Continue.\n     * On the Grant users access to this service account page, click Done. This takes you back to APIs & Services > Credentials.\n     * On the Credentials page, scroll down to the Service Accounts section and click the name of the account you just created.\n     * On the Service account details page, scroll down to the Keys section and select Add Key > Create new key.\n     * Select JSON as the key type and click Create. This downloads a JSON file containing the private key to your machine.\n   * Add the contents of the JSON file you just downloaded to the iOS part of your Flutter application:\n     * Rename the file to ```cloudAnchorKey.json```\n     * Move or copy ```cloudAnchorKey.json``` into the example/ios/Runner directory.\n     * Open Xcode, then right-click on Runner directory and select Add Files to \"Runner\".\n     * Select ```cloudAnchorKey.json```  from the file manager.\n     * A dialog will show up and ask you to select the targets, select the Runner target.\n\n## Set up Firebase\n\nGoogle's Firebase cloud platform is used by the plugin's sample app to distribute and manage shared anchors and related content. If you want to use the included examples with shared AR experience features (e.g. the ```Cloud Anchors```example), the following setup steps are required (in your own apps, you can implement any method you like to distribute and manage the cloud anchor IDs that the plugin returns after uploading an anchor):\n\n1. Create a new project in the [Firebase console](https://console.firebase.google.com/project/_/overview)\n2. Register the Android part of your Flutter Application (based on the [FlutterFire Android Installation Guide](https://firebase.flutter.dev/docs/installation/android/)):\n   * Add a new Android app to your project and make sure the ```Android package name``` matches your local project's package name which can be found within the ```AndroidManifest.xml```\n   * Fill in the debug signing certificate SHA-1 field. If you're still in development, you can get the debug keystore key for the Android app by executing ```keytool -keystore ~/.android/debug.keystore -list -v``` in your terminal\n   * Once your Android app has been registered, download the configuration file from the Firebase Console (the file is called ```google-services.json```). Add this file into the ```android/app``` directory within your Flutter project\n   * Add the dependency ```classpath 'com.google.gms:google-services:4.3.3'``` to the top-level ```build.gradle```file of the Android part of your Flutter application\n   * Add ```apply plugin: 'com.google.gms.google-services'``` to the app-level ```build.gradle```file of the Android part of your Flutter application\n3. Register the iOS part of your Flutter Application (based on the [FlutterFire iOS Installation Guide](https://firebase.flutter.dev/docs/installation/ios/)):\n   * Add a new iOS app to your project and make sure the ```iOS bundle ID``` matches your local project bundle ID which can be found within the \"General\" tab when opening ```ios/Runner.xcworkspace``` with Xcode.\n   * Download the file ```GoogleService-Info.plist``` \n   * Move or copy ``` GoogleService-Info.plist``` into the example/ios/Runner directory.\n   * Open Xcode, then right-click on Runner directory and select Add Files to \"Runner\".\n   * Select ``` GoogleService-Info.plist```  from the file manager.\n   * A dialog will show up and ask you to select the targets, select the Runner target.\n4. Enable Cloud Firestore for the project you created in step 1 (head to https://console.firebase.google.com/project/INSERT_YOUR_FIREBASE_PROJECT_NAME_HERE/firestore)\n\n## Set up Location Services\n\n* On the iOS part of your app, add the following to your Info.plist file (located under ios/Runner) in order to access the device's location:\n  ```\n  <key>NSLocationWhenInUseUsageDescription</key>\n  <string>This app needs access to location when open.</string>\n  <key>NSLocationAlwaysUsageDescription</key>\n  <string>This app needs access to location when in the background.</string>\n  ```\n   \n"
  },
  {
    "path": "example/.gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n**/doc/api/\n**/ios/Flutter/.last_build_id\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n.packages\n.pub-cache/\n.pub/\n/build/\n\n# Web related\nlib/generated_plugin_registrant.dart\n\n# Symbolication related\napp.*.symbols\n\n# Obfuscation related\napp.*.map.json\n\n# Android Studio will place build artifacts here\n/android/app/debug\n/android/app/profile\n/android/app/release\n"
  },
  {
    "path": "example/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: 4b50ca7f7fbf56be72e54cd200825b760416a356\n  channel: beta\n\nproject_type: app\n"
  },
  {
    "path": "example/Models/Chicken_01/Chicken_01.gltf",
    "content": "{\n   \"accessors\" : [\n      {\n         \"bufferView\" : 0,\n         \"byteOffset\" : 0,\n         \"componentType\" : 5123,\n         \"count\" : 558,\n         \"max\" : [ 557 ],\n         \"min\" : [ 0 ],\n         \"name\" : \"buffer-0-accessor-indices-buffer-0-mesh-0\",\n         \"type\" : \"SCALAR\"\n      },\n      {\n         \"bufferView\" : 2,\n         \"byteOffset\" : 0,\n         \"componentType\" : 5126,\n         \"count\" : 558,\n         \"max\" : [ 3.636018991470337, 152.5314636230469, 49.29042816162109 ],\n         \"min\" : [ -87.20170593261719, 0, -49.29042816162109 ],\n         \"name\" : \"buffer-0-accessor-position-buffer-0-mesh-0\",\n         \"type\" : \"VEC3\"\n      },\n      {\n         \"bufferView\" : 2,\n         \"byteOffset\" : 6696,\n         \"componentType\" : 5126,\n         \"count\" : 558,\n         \"max\" : [ 0.9146999716758728, 0.9926000237464905, 0.9937000274658203 ],\n         \"min\" : [ -0.9735000133514404, -1, -0.9937000274658203 ],\n         \"name\" : \"buffer-0-accessor-normal-buffer-0-mesh-0\",\n         \"type\" : \"VEC3\"\n      },\n      {\n         \"bufferView\" : 1,\n         \"byteOffset\" : 0,\n         \"componentType\" : 5126,\n         \"count\" : 558,\n         \"max\" : [ 1, 0 ],\n         \"min\" : [ 0, -1 ],\n         \"name\" : \"buffer-0-accessor-texcoord-buffer-0-mesh-0\",\n         \"type\" : \"VEC2\"\n      },\n      {\n         \"bufferView\" : 3,\n         \"byteOffset\" : 0,\n         \"componentType\" : 5126,\n         \"count\" : 0,\n         \"max\" : [ 0, 0, 0, 0 ],\n         \"min\" : [ 0, 0, 0, 0 ],\n         \"name\" : \"buffer-0-accessor-color-buffer-0-mesh-0\",\n         \"type\" : \"VEC4\"\n      },\n      {\n         \"bufferView\" : 0,\n         \"byteOffset\" : 1116,\n         \"componentType\" : 5123,\n         \"count\" : 414,\n         \"max\" : [ 413 ],\n         \"min\" : [ 0 ],\n         \"name\" : \"buffer-0-accessor-indices-buffer-0-mesh-0\",\n         \"type\" : \"SCALAR\"\n      },\n      {\n         \"bufferView\" : 2,\n         \"byteOffset\" : 13392,\n         \"componentType\" : 5126,\n         \"count\" : 414,\n         \"max\" : [ 80.11344909667969, 176.5484161376953, 40.12937164306641 ],\n         \"min\" : [ -75.36231994628906, 0, -40.12934494018555 ],\n         \"name\" : \"buffer-0-accessor-position-buffer-0-mesh-0\",\n         \"type\" : \"VEC3\"\n      },\n      {\n         \"bufferView\" : 2,\n         \"byteOffset\" : 18360,\n         \"componentType\" : 5126,\n         \"count\" : 414,\n         \"max\" : [ 0.9840999841690063, 0.9950000047683716, 1 ],\n         \"min\" : [ -0.942300021648407, -0.9502999782562256, -1 ],\n         \"name\" : \"buffer-0-accessor-normal-buffer-0-mesh-0\",\n         \"type\" : \"VEC3\"\n      },\n      {\n         \"bufferView\" : 1,\n         \"byteOffset\" : 4464,\n         \"componentType\" : 5126,\n         \"count\" : 414,\n         \"max\" : [ 1, 0 ],\n         \"min\" : [ 0, -1 ],\n         \"name\" : \"buffer-0-accessor-texcoord-buffer-0-mesh-0\",\n         \"type\" : \"VEC2\"\n      },\n      {\n         \"bufferView\" : 3,\n         \"byteOffset\" : 0,\n         \"componentType\" : 5126,\n         \"count\" : 0,\n         \"max\" : [ 0, 0, 0, 0 ],\n         \"min\" : [ 0, 0, 0, 0 ],\n         \"name\" : \"buffer-0-accessor-color-buffer-0-mesh-0\",\n         \"type\" : \"VEC4\"\n      },\n      {\n         \"bufferView\" : 0,\n         \"byteOffset\" : 1944,\n         \"componentType\" : 5123,\n         \"count\" : 144,\n         \"max\" : [ 143 ],\n         \"min\" : [ 0 ],\n         \"name\" : \"buffer-0-accessor-indices-buffer-0-mesh-0\",\n         \"type\" : \"SCALAR\"\n      },\n      {\n         \"bufferView\" : 2,\n         \"byteOffset\" : 23328,\n         \"componentType\" : 5126,\n         \"count\" : 144,\n         \"max\" : [ 0, 156.3618316650391, 14.85952568054199 ],\n         \"min\" : [ -65.07901763916016, 0, -14.85947513580322 ],\n         \"name\" : \"buffer-0-accessor-position-buffer-0-mesh-0\",\n         \"type\" : \"VEC3\"\n      },\n      {\n         \"bufferView\" : 2,\n         \"byteOffset\" : 25056,\n         \"componentType\" : 5126,\n         \"count\" : 144,\n         \"max\" : [ 0.9239000082015991, 0.9239000082015991, 1 ],\n         \"min\" : [ -0.9239000082015991, -0.9239000082015991, -1 ],\n         \"name\" : \"buffer-0-accessor-normal-buffer-0-mesh-0\",\n         \"type\" : \"VEC3\"\n      },\n      {\n         \"bufferView\" : 1,\n         \"byteOffset\" : 7776,\n         \"componentType\" : 5126,\n         \"count\" : 144,\n         \"max\" : [ 1, 0 ],\n         \"min\" : [ 0, -1 ],\n         \"name\" : \"buffer-0-accessor-texcoord-buffer-0-mesh-0\",\n         \"type\" : \"VEC2\"\n      },\n      {\n         \"bufferView\" : 3,\n         \"byteOffset\" : 0,\n         \"componentType\" : 5126,\n         \"count\" : 0,\n         \"max\" : [ 0, 0, 0, 0 ],\n         \"min\" : [ 0, 0, 0, 0 ],\n         \"name\" : \"buffer-0-accessor-color-buffer-0-mesh-0\",\n         \"type\" : \"VEC4\"\n      },\n      {\n         \"bufferView\" : 0,\n         \"byteOffset\" : 2232,\n         \"componentType\" : 5123,\n         \"count\" : 504,\n         \"max\" : [ 503 ],\n         \"min\" : [ 0 ],\n         \"name\" : \"buffer-0-accessor-indices-buffer-0-mesh-0\",\n         \"type\" : \"SCALAR\"\n      },\n      {\n         \"bufferView\" : 2,\n         \"byteOffset\" : 26784,\n         \"componentType\" : 5126,\n         \"count\" : 504,\n         \"max\" : [ 0, 190.3115692138672, 10.57294273376465 ],\n         \"min\" : [ -84.04743957519531, 0, -10.57290267944336 ],\n         \"name\" : \"buffer-0-accessor-position-buffer-0-mesh-0\",\n         \"type\" : \"VEC3\"\n      },\n      {\n         \"bufferView\" : 2,\n         \"byteOffset\" : 32832,\n         \"componentType\" : 5126,\n         \"count\" : 504,\n         \"max\" : [ 0.9994000196456909, 0.998199999332428, 0.9961000084877014 ],\n         \"min\" : [ -1, -0.9664000272750854, -0.9973000288009644 ],\n         \"name\" : \"buffer-0-accessor-normal-buffer-0-mesh-0\",\n         \"type\" : \"VEC3\"\n      },\n      {\n         \"bufferView\" : 1,\n         \"byteOffset\" : 8928,\n         \"componentType\" : 5126,\n         \"count\" : 504,\n         \"max\" : [ 1, 0 ],\n         \"min\" : [ 0, -1 ],\n         \"name\" : \"buffer-0-accessor-texcoord-buffer-0-mesh-0\",\n         \"type\" : \"VEC2\"\n      },\n      {\n         \"bufferView\" : 3,\n         \"byteOffset\" : 0,\n         \"componentType\" : 5126,\n         \"count\" : 0,\n         \"max\" : [ 0, 0, 0, 0 ],\n         \"min\" : [ 0, 0, 0, 0 ],\n         \"name\" : \"buffer-0-accessor-color-buffer-0-mesh-0\",\n         \"type\" : \"VEC4\"\n      },\n      {\n         \"bufferView\" : 0,\n         \"byteOffset\" : 3240,\n         \"componentType\" : 5123,\n         \"count\" : 324,\n         \"max\" : [ 323 ],\n         \"min\" : [ 0 ],\n         \"name\" : \"buffer-0-accessor-indices-buffer-0-mesh-0\",\n         \"type\" : \"SCALAR\"\n      },\n      {\n         \"bufferView\" : 2,\n         \"byteOffset\" : 38880,\n         \"componentType\" : 5126,\n         \"count\" : 324,\n         \"max\" : [ 6.280783176422119, 4.014040946960449, 51.71989440917969 ],\n         \"min\" : [ -26.94760704040527, 0, -51.71989440917969 ],\n         \"name\" : \"buffer-0-accessor-position-buffer-0-mesh-0\",\n         \"type\" : \"VEC3\"\n      },\n      {\n         \"bufferView\" : 2,\n         \"byteOffset\" : 42768,\n         \"componentType\" : 5126,\n         \"count\" : 324,\n         \"max\" : [ 0.9013000130653381, 0.9991000294685364, 0.9283000230789185 ],\n         \"min\" : [ -0.9253000020980835, -0.9991000294685364, -0.9283000230789185 ],\n         \"name\" : \"buffer-0-accessor-normal-buffer-0-mesh-0\",\n         \"type\" : \"VEC3\"\n      },\n      {\n         \"bufferView\" : 1,\n         \"byteOffset\" : 12960,\n         \"componentType\" : 5126,\n         \"count\" : 324,\n         \"max\" : [ 1, 0 ],\n         \"min\" : [ 0, -1 ],\n         \"name\" : \"buffer-0-accessor-texcoord-buffer-0-mesh-0\",\n         \"type\" : \"VEC2\"\n      },\n      {\n         \"bufferView\" : 3,\n         \"byteOffset\" : 0,\n         \"componentType\" : 5126,\n         \"count\" : 0,\n         \"max\" : [ 0, 0, 0, 0 ],\n         \"min\" : [ 0, 0, 0, 0 ],\n         \"name\" : \"buffer-0-accessor-color-buffer-0-mesh-0\",\n         \"type\" : \"VEC4\"\n      }\n   ],\n   \"asset\" : {\n      \"extras\" : {\n         \"GOOGLE_processor_version\" : 293297437\n      },\n      \"generator\" : \"Obj2GltfConverter\",\n      \"version\" : \"2.0\"\n   },\n   \"bufferViews\" : [\n      {\n         \"buffer\" : 0,\n         \"byteLength\" : 3888,\n         \"byteOffset\" : 0,\n         \"byteStride\" : 0,\n         \"name\" : \"buffer-0-bufferview-ushort\",\n         \"target\" : 34963\n      },\n      {\n         \"buffer\" : 0,\n         \"byteLength\" : 15552,\n         \"byteOffset\" : 3888,\n         \"byteStride\" : 8,\n         \"name\" : \"buffer-0-bufferview-vec2\",\n         \"target\" : 34962\n      },\n      {\n         \"buffer\" : 0,\n         \"byteLength\" : 46656,\n         \"byteOffset\" : 19440,\n         \"byteStride\" : 12,\n         \"name\" : \"buffer-0-bufferview-vec3\",\n         \"target\" : 34962\n      },\n      {\n         \"buffer\" : 0,\n         \"byteLength\" : 1,\n         \"byteOffset\" : 0,\n         \"name\" : \"buffer-0-bufferview-vec4\"\n      }\n   ],\n   \"buffers\" : [\n      {\n         \"byteLength\" : 66096,\n         \"name\" : \"buffer-0\",\n         \"uri\" : \"Chicken_01.bin\"\n      }\n   ],\n   \"cameras\" : [\n      {\n         \"perspective\" : {\n            \"yfov\" : 0.7853981633974483,\n            \"znear\" : 0.1000000014901161\n         },\n         \"type\" : \"perspective\"\n      }\n   ],\n   \"extensions\" : {\n      \"KHR_lights_punctual\" : {\n         \"lights\" : [\n            {\n               \"color\" : [ 1, 0.9333333333333333, 0.8666666666666667 ],\n               \"intensity\" : 0.25,\n               \"name\" : \"headLight\",\n               \"type\" : \"directional\"\n            },\n            {\n               \"color\" : [ 1, 0.9333333333333333, 0.8666666666666667 ],\n               \"intensity\" : 0.324999988079071,\n               \"name\" : \"keyLight\",\n               \"type\" : \"directional\"\n            }\n         ]\n      }\n   },\n   \"extensionsUsed\" : [ \"GOOGLE_backgrounds\", \"GOOGLE_camera_settings\", \"KHR_lights_punctual\" ],\n   \"extras\" : {\n      \"GOOGLE_initial_camera_motion\" : {\n         \"motionPath\" : \"FULL_ROTATION\"\n      },\n      \"GOOGLE_lighting_rig\" : {\n         \"disableShadows\" : false\n      }\n   },\n   \"materials\" : [\n      {\n         \"alphaMode\" : \"OPAQUE\",\n         \"doubleSided\" : true,\n         \"extras\" : {\n            \"__iag_\" : true\n         },\n         \"name\" : \"FF9800\",\n         \"pbrMetallicRoughness\" : {\n            \"baseColorFactor\" : [ 1, 0.355308982084, 0, 1 ],\n            \"metallicFactor\" : 0,\n            \"roughnessFactor\" : 0.7493216756721951\n         }\n      },\n      {\n         \"alphaMode\" : \"OPAQUE\",\n         \"doubleSided\" : true,\n         \"extras\" : {\n            \"__iag_\" : true\n         },\n         \"name\" : \"FFFFFF\",\n         \"pbrMetallicRoughness\" : {\n            \"baseColorFactor\" : [ 1, 1, 1, 1 ],\n            \"metallicFactor\" : 0,\n            \"roughnessFactor\" : 0.7493216756721951\n         }\n      },\n      {\n         \"alphaMode\" : \"OPAQUE\",\n         \"doubleSided\" : true,\n         \"extras\" : {\n            \"__iag_\" : true\n         },\n         \"name\" : \"1A1A1A\",\n         \"pbrMetallicRoughness\" : {\n            \"baseColorFactor\" : [ 0.010396045521, 0.010396045521, 0.010396045521, 1 ],\n            \"metallicFactor\" : 0,\n            \"roughnessFactor\" : 0.7493216756721951\n         }\n      },\n      {\n         \"alphaMode\" : \"OPAQUE\",\n         \"doubleSided\" : true,\n         \"extras\" : {\n            \"__iag_\" : true\n         },\n         \"name\" : \"F44336\",\n         \"pbrMetallicRoughness\" : {\n            \"baseColorFactor\" : [ 0.915586800769, 0.06903493502500001, 0.044844415225, 1 ],\n            \"metallicFactor\" : 0,\n            \"roughnessFactor\" : 0.7493216756721951\n         }\n      },\n      {\n         \"alphaMode\" : \"OPAQUE\",\n         \"doubleSided\" : true,\n         \"extras\" : {\n            \"__iag_\" : true\n         },\n         \"name\" : \"455A64\",\n         \"pbrMetallicRoughness\" : {\n            \"baseColorFactor\" : [ 0.07321786574399999, 0.124567349481, 0.153787112649, 1 ],\n            \"metallicFactor\" : 0,\n            \"roughnessFactor\" : 0.7493216756721951\n         }\n      }\n   ],\n   \"meshes\" : [\n      {\n         \"name\" : \"buffer-0-mesh-0\",\n         \"primitives\" : [\n            {\n               \"attributes\" : {\n                  \"NORMAL\" : 2,\n                  \"POSITION\" : 1,\n                  \"TEXCOORD_0\" : 3\n               },\n               \"indices\" : 0,\n               \"material\" : 0,\n               \"mode\" : 4\n            },\n            {\n               \"attributes\" : {\n                  \"NORMAL\" : 7,\n                  \"POSITION\" : 6,\n                  \"TEXCOORD_0\" : 8\n               },\n               \"indices\" : 5,\n               \"material\" : 1,\n               \"mode\" : 4\n            },\n            {\n               \"attributes\" : {\n                  \"NORMAL\" : 12,\n                  \"POSITION\" : 11,\n                  \"TEXCOORD_0\" : 13\n               },\n               \"indices\" : 10,\n               \"material\" : 2,\n               \"mode\" : 4\n            },\n            {\n               \"attributes\" : {\n                  \"NORMAL\" : 17,\n                  \"POSITION\" : 16,\n                  \"TEXCOORD_0\" : 18\n               },\n               \"indices\" : 15,\n               \"material\" : 3,\n               \"mode\" : 4\n            },\n            {\n               \"attributes\" : {\n                  \"NORMAL\" : 22,\n                  \"POSITION\" : 21,\n                  \"TEXCOORD_0\" : 23\n               },\n               \"indices\" : 20,\n               \"material\" : 4,\n               \"mode\" : 4\n            }\n         ]\n      }\n   ],\n   \"nodes\" : [\n      {\n         \"mesh\" : 0,\n         \"name\" : \"node-0\"\n      },\n      {\n         \"children\" : [ 2 ]\n      },\n      {\n         \"children\" : [ 3 ],\n         \"extras\" : {\n            \"isTransformNode\" : 1\n         },\n         \"translation\" : [ -10.33925953243352, 101.4766894350761, 0.4725487306714168 ]\n      },\n      {\n         \"children\" : [ 0 ],\n         \"translation\" : [ 10.33925953243352, -101.4766894350761, -0.4725487306714168 ]\n      },\n      {\n         \"camera\" : 0,\n         \"children\" : [ 5 ],\n         \"rotation\" : [\n            0.03161089902743668,\n            0.9405290359497446,\n            0.091286676120003,\n            -0.3256879278712723\n         ],\n         \"translation\" : [ -209.7092493748044, 164.720177932136, -253.0129743638226 ]\n      },\n      {\n         \"extensions\" : {\n            \"KHR_lights_punctual\" : {\n               \"light\" : 0\n            }\n         },\n         \"extras\" : {\n            \"isHeadLight\" : 1\n         },\n         \"name\" : \"headLightNode\",\n         \"translation\" : [ -137.6435827985261, 68.82179139926303, 68.82179139926303 ]\n      },\n      {\n         \"extensions\" : {\n            \"KHR_lights_punctual\" : {\n               \"light\" : 1\n            }\n         },\n         \"extras\" : {\n            \"isKeyLight\" : 1\n         },\n         \"name\" : \"keyLightNode\",\n         \"rotation\" : [ -0.7505326882858236, -0.3752663441429118, 0, 0.5439447166468927 ],\n         \"scale\" : [ 0, 0, 139.0200186265113 ],\n         \"translation\" : [ -169.8998424664395, 356.8894572597737, -139.0250173948788 ]\n      }\n   ],\n   \"scene\" : 0,\n   \"scenes\" : [\n      {\n         \"extensions\" : {\n            \"GOOGLE_backgrounds\" : {\n               \"color\" : [ 0.803921568627451, 0.8627450980392157, 0.2235294117647059 ]\n            },\n            \"GOOGLE_camera_settings\" : {}\n         },\n         \"extras\" : {\n            \"GOOGLE_camera_index\" : {\n               \"cameraIndex\" : 0,\n               \"nodeIndex\" : 4\n            },\n            \"GOOGLE_geometry_data\" : {\n               \"stats\" : {\n                  \"centroid\" : [ -30.87982383992815, 78.84942000675105, -0.004998768367502245 ],\n                  \"radius\" : 137.6435827985261,\n                  \"stdev\" : 86.13574977146493\n               },\n               \"visualCenterPoint\" : [ -10.33925953243352, 101.4766894350761, 0.4725487306714168 ]\n            },\n            \"GOOGLE_hemi_light\" : {\n               \"groundColor\" : [ 0.803921568627451, 0.8627450980392157, 0.2235294117647059 ]\n            }\n         },\n         \"name\" : \"scene-0\",\n         \"nodes\" : [ 1, 4, 6 ]\n      }\n   ]\n}\n"
  },
  {
    "path": "example/Models/Chicken_01/license.txt",
    "content": "Licensed under CC-BY 3.0 by \"Poly by Google\" (https://poly.google.com/user/4aEd8rQgKu2)"
  },
  {
    "path": "example/README.md",
    "content": "# ar_flutter_plugin_example\n\nDemonstrates how to use the ar_flutter_plugin plugin to create collaborative AR experiences.\n\n## Contributing\n\nContributions are very welcome. To contribute code and discuss ideas, [create a pull request](https://github.com/CariusLars/ar_flutter_plugin/compare) or [open an issue](https://github.com/CariusLars/ar_flutter_plugin/issues/new)."
  },
  {
    "path": "example/android/.gitignore",
    "content": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n\n# Remember to never publicly share your keystore.\n# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app\nkey.properties\n"
  },
  {
    "path": "example/android/app/build.gradle",
    "content": "def localProperties = new Properties()\ndef localPropertiesFile = rootProject.file('local.properties')\nif (localPropertiesFile.exists()) {\n    localPropertiesFile.withReader('UTF-8') { reader ->\n        localProperties.load(reader)\n    }\n}\n\ndef flutterRoot = localProperties.getProperty('flutter.sdk')\nif (flutterRoot == null) {\n    throw new GradleException(\"Flutter SDK not found. Define location with flutter.sdk in the local.properties file.\")\n}\n\ndef flutterVersionCode = localProperties.getProperty('flutter.versionCode')\nif (flutterVersionCode == null) {\n    flutterVersionCode = '1'\n}\n\ndef flutterVersionName = localProperties.getProperty('flutter.versionName')\nif (flutterVersionName == null) {\n    flutterVersionName = '1.0'\n}\n\napply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply from: \"$flutterRoot/packages/flutter_tools/gradle/flutter.gradle\"\n\nandroid {\n    compileSdkVersion flutter.compileSdkVersion\n    ndkVersion flutter.ndkVersion\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n    }\n\n    defaultConfig {\n        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).\n        applicationId \"io.carius.lars.ar_flutter_plugin_example\"\n        minSdkVersion 24\n        targetSdkVersion flutter.targetSdkVersion\n        versionCode flutterVersionCode.toInteger()\n        versionName flutterVersionName\n    }\n\n    buildTypes {\n        debug {\n            signingConfig signingConfigs.debug\n        }\n\n        release {\n            signingConfig signingConfigs.debug\n        }\n    }\n}\n\nflutter {\n    source '../..'\n}\n\ndependencies {\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n}\n\napply plugin: 'com.google.gms.google-services'"
  },
  {
    "path": "example/android/app/src/debug/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"io.carius.lars.ar_flutter_plugin_example\">\n    <!-- Flutter needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "example/android/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"io.carius.lars.ar_flutter_plugin_example\">\n   <application\n        android:label=\"ar_flutter_plugin_example\"\n        android:icon=\"@mipmap/ic_launcher\">\n        <activity\n            android:exported=\"true\"\n            android:name=\".MainActivity\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/LaunchTheme\"\n            android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode\"\n            android:hardwareAccelerated=\"true\"\n            android:windowSoftInputMode=\"adjustResize\">\n            <!-- Specifies an Android theme to apply to this Activity as soon as\n                 the Android process has started. This theme is visible to the user\n                 while the Flutter UI initializes. After that, this theme continues\n                 to determine the Window background behind the Flutter UI. -->\n            <meta-data\n              android:name=\"io.flutter.embedding.android.NormalTheme\"\n              android:resource=\"@style/NormalTheme\"\n              />\n            <!-- Displays an Android View that continues showing the launch screen\n                 Drawable until Flutter paints its first frame, then this splash\n                 screen fades out. A splash screen is useful to avoid any visual\n                 gap between the end of Android's launch screen and the painting of\n                 Flutter's first frame. -->\n            <meta-data\n              android:name=\"io.flutter.embedding.android.SplashScreenDrawable\"\n              android:resource=\"@drawable/launch_background\"\n              />\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n        <!-- Don't delete the meta-data below.\n             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->\n        <meta-data\n            android:name=\"flutterEmbedding\"\n            android:value=\"2\" />\n    </application>\n</manifest>\n"
  },
  {
    "path": "example/android/app/src/main/kotlin/io/carius/lars/ar_flutter_plugin_example/MainActivity.kt",
    "content": "package io.carius.lars.ar_flutter_plugin_example\n\nimport io.flutter.embedding.android.FlutterActivity\n\nclass MainActivity: FlutterActivity() {\n}\n"
  },
  {
    "path": "example/android/app/src/main/res/drawable/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@android:color/white\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\n    </item> -->\n</layer-list>\n"
  },
  {
    "path": "example/android/app/src/main/res/drawable-v21/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"?android:colorBackground\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\n    </item> -->\n</layer-list>\n"
  },
  {
    "path": "example/android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             Flutter draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n         \n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "example/android/app/src/main/res/values-night/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             Flutter draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n         \n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "example/android/app/src/profile/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"io.carius.lars.ar_flutter_plugin_example\">\n    <!-- Flutter needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "example/android/build.gradle",
    "content": "buildscript {\n    ext.kotlin_version = '1.6.10'\n    repositories {\n        google()\n        jcenter()\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:7.1.2'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        classpath 'com.google.gms:google-services:4.3.8'\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        jcenter()\n    }\n}\n\nrootProject.buildDir = '../build'\nsubprojects {\n    project.buildDir = \"${rootProject.buildDir}/${project.name}\"\n}\nsubprojects {\n    project.evaluationDependsOn(':app')\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "example/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Fri Jun 23 08:50:38 CEST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.4-all.zip\n"
  },
  {
    "path": "example/android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1536M\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
  },
  {
    "path": "example/android/settings.gradle",
    "content": "include ':app'\n\ndef localPropertiesFile = new File(rootProject.projectDir, \"local.properties\")\ndef properties = new Properties()\n\nassert localPropertiesFile.exists()\nlocalPropertiesFile.withReader(\"UTF-8\") { reader -> properties.load(reader) }\n\ndef flutterSdkPath = properties.getProperty(\"flutter.sdk\")\nassert flutterSdkPath != null, \"flutter.sdk not set in local.properties\"\napply from: \"$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle\"\n"
  },
  {
    "path": "example/ios/.gitignore",
    "content": "*.mode1v3\n*.mode2v3\n*.moved-aside\n*.pbxuser\n*.perspectivev3\n**/*sync/\n.sconsign.dblite\n.tags*\n**/.vagrant/\n**/DerivedData/\nIcon?\n**/Pods/\n**/.symlinks/\nprofile\nxcuserdata\n**/.generated/\nFlutter/App.framework\nFlutter/Flutter.framework\nFlutter/Flutter.podspec\nFlutter/Generated.xcconfig\nFlutter/app.flx\nFlutter/app.zip\nFlutter/flutter_assets/\nFlutter/flutter_export_environment.sh\nServiceDefinitions.json\nRunner/GeneratedPluginRegistrant.*\n\n# Exceptions to above rules.\n!default.mode1v3\n!default.mode2v3\n!default.pbxuser\n!default.perspectivev3\n"
  },
  {
    "path": "example/ios/Flutter/AppFrameworkInfo.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n  <key>CFBundleDevelopmentRegion</key>\n  <string>en</string>\n  <key>CFBundleExecutable</key>\n  <string>App</string>\n  <key>CFBundleIdentifier</key>\n  <string>io.flutter.flutter.app</string>\n  <key>CFBundleInfoDictionaryVersion</key>\n  <string>6.0</string>\n  <key>CFBundleName</key>\n  <string>App</string>\n  <key>CFBundlePackageType</key>\n  <string>FMWK</string>\n  <key>CFBundleShortVersionString</key>\n  <string>1.0</string>\n  <key>CFBundleSignature</key>\n  <string>????</string>\n  <key>CFBundleVersion</key>\n  <string>1.0</string>\n  <key>MinimumOSVersion</key>\n  <string>13.0</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "example/ios/Flutter/Debug.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "example/ios/Flutter/Release.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "example/ios/Podfile",
    "content": "# Uncomment this line to define a global platform for your project\n# platform :ios, '9.0'\n\n# Override firebase SDK version for compatibility reasons\n$FirebaseSDKVersion = '8.7.0'\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_ios_podfile_setup\n\ntarget 'Runner' do\n  use_frameworks!\n  use_modular_headers!\n\n  pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '8.7.0' # This line improves build times (see https://firebase.flutter.dev/docs/overview/)\n  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_ios_build_settings(target)\n    target.build_configurations.each do |config|\n      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'\n\n      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [\n        '$(inherited)',\n\n        ## dart: PermissionGroup.camera\n        'PERMISSION_CAMERA=1',\n\n        ## dart: PermissionGroup.photos\n        'PERMISSION_PHOTOS=1',\n\n        ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]\n        'PERMISSION_LOCATION=1',\n\n        ## dart: PermissionGroup.sensors\n        'PERMISSION_SENSORS=1',\n\n        ## dart: PermissionGroup.bluetooth\n        'PERMISSION_BLUETOOTH=1',\n\n      ]\n    end\n  end\nend\n"
  },
  {
    "path": "example/ios/Runner/AppDelegate.swift",
    "content": "import UIKit\nimport Flutter\n\n@UIApplicationMain\n@objc class AppDelegate: FlutterAppDelegate {\n  override func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    GeneratedPluginRegistrant.register(with: self)\n    return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n  }\n}\n"
  },
  {
    "path": "example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"83.5x83.5\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-83.5x83.5@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"1024x1024\",\n      \"idiom\" : \"ios-marketing\",\n      \"filename\" : \"Icon-App-1024x1024@1x.png\",\n      \"scale\" : \"1x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "example/ios/Runner/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md",
    "content": "# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in this directory.\n\nYou 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."
  },
  {
    "path": "example/ios/Runner/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"12121\" systemVersion=\"16G29\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"12089\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"Ydg-fD-yQy\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"xbc-2k-c8Z\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <imageView opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" image=\"LaunchImage\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"YRO-k0-Ey4\">\n                            </imageView>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerX\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerX\" id=\"1a2-6s-vTC\"/>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerY\" id=\"4X2-HB-R7a\"/>\n                        </constraints>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"LaunchImage\" width=\"168\" height=\"185\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "example/ios/Runner/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"10117\" systemVersion=\"15F34\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"10085\"/>\n    </dependencies>\n    <scenes>\n        <!--Flutter View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"FlutterViewController\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"y3c-jy-aDJ\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"wfy-db-euE\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"600\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"calibratedWhite\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "example/ios/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>ar_flutter_plugin_example</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(MARKETING_VERSION)</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>NSCameraUsageDescription</key>\n\t<string></string>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t<false/>\n\t<key>NSLocationWhenInUseUsageDescription</key>\n\t<string>This app needs access to location when open.</string>\n\t<key>NSLocationAlwaysUsageDescription</key>\n\t<string>This app needs access to location when in the background.</string>\n\t<key>CADisableMinimumFrameDurationOnPhone</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "example/ios/Runner/Runner-Bridging-Header.h",
    "content": "#import \"GeneratedPluginRegistrant.h\"\n"
  },
  {
    "path": "example/ios/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 51;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };\n\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };\n\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };\n\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };\n\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };\n\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };\n\t\tEF5AB5D5261C9BD900A60388 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = EF5AB5D4261C9BD900A60388 /* GoogleService-Info.plist */; };\n\t\tEF5AB736261F5EA600A60388 /* cloudAnchorKey.json in Resources */ = {isa = PBXBuildFile; fileRef = EF5AB735261F5EA600A60388 /* cloudAnchorKey.json */; };\n\t\tFC3B1579A1F9BBE343269DEF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A1F47AAC378FB2E799B13D8 /* Pods_Runner.framework */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t9705A1C41CF9048500538489 /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t0181363DB89EBAFA9858B1A3 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = \"<group>\"; };\n\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = \"<group>\"; };\n\t\t3A1F47AAC378FB2E799B13D8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = \"<group>\"; };\n\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"Runner-Bridging-Header.h\"; sourceTree = \"<group>\"; };\n\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = \"<group>\"; };\n\t\t97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tD424E5FE727DFB8DCB6BF1D8 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tEF5AB5D4261C9BD900A60388 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = \"GoogleService-Info.plist\"; sourceTree = \"<group>\"; };\n\t\tEF5AB5D6261DA4D800A60388 /* ARCoreCloudAnchors.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ARCoreCloudAnchors.framework; path = Pods/ARCore/CloudAnchors/Frameworks/ARCoreCloudAnchors.framework; sourceTree = \"<group>\"; };\n\t\tEF5AB5D8261DA54800A60388 /* ARCore */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ARCore; path = Pods/ARCore; sourceTree = \"<group>\"; };\n\t\tEF5AB735261F5EA600A60388 /* cloudAnchorKey.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = cloudAnchorKey.json; sourceTree = \"<group>\"; };\n\t\tF6A0B71292B6059CA94A3F18 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t97C146EB1CF9000F007C117D /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tFC3B1579A1F9BBE343269DEF /* Pods_Runner.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t4E95B0BED358A7C62F0A1158 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tEF5AB5D8261DA54800A60388 /* ARCore */,\n\t\t\t\tEF5AB5D6261DA4D800A60388 /* ARCoreCloudAnchors.framework */,\n\t\t\t\t3A1F47AAC378FB2E799B13D8 /* Pods_Runner.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9740EEB11CF90186004384FC /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */,\n\t\t\t);\n\t\t\tname = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146E51CF9000F007C117D = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9740EEB11CF90186004384FC /* Flutter */,\n\t\t\t\t97C146F01CF9000F007C117D /* Runner */,\n\t\t\t\t97C146EF1CF9000F007C117D /* Products */,\n\t\t\t\t9B7184BFA5C5D8AB5FFB1988 /* Pods */,\n\t\t\t\t4E95B0BED358A7C62F0A1158 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146EF1CF9000F007C117D /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146EE1CF9000F007C117D /* Runner.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146F01CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tEF5AB735261F5EA600A60388 /* cloudAnchorKey.json */,\n\t\t\t\tEF5AB5D4261C9BD900A60388 /* GoogleService-Info.plist */,\n\t\t\t\t97C146FA1CF9000F007C117D /* Main.storyboard */,\n\t\t\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */,\n\t\t\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,\n\t\t\t\t97C147021CF9000F007C117D /* Info.plist */,\n\t\t\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,\n\t\t\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,\n\t\t\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */,\n\t\t\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9B7184BFA5C5D8AB5FFB1988 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD424E5FE727DFB8DCB6BF1D8 /* Pods-Runner.debug.xcconfig */,\n\t\t\t\tF6A0B71292B6059CA94A3F18 /* Pods-Runner.release.xcconfig */,\n\t\t\t\t0181363DB89EBAFA9858B1A3 /* Pods-Runner.profile.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t97C146ED1CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tB6180CDA50A54DB55045479E /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t9740EEB61CF901F6004384FC /* Run Script */,\n\t\t\t\t97C146EA1CF9000F007C117D /* Sources */,\n\t\t\t\t97C146EB1CF9000F007C117D /* Frameworks */,\n\t\t\t\t97C146EC1CF9000F007C117D /* Resources */,\n\t\t\t\t9705A1C41CF9048500538489 /* Embed Frameworks */,\n\t\t\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */,\n\t\t\t\t94B0826727C2A055D78A9B66 /* [CP] Embed Pods Frameworks */,\n\t\t\t\tC226EEF590F236CBFE1746B9 /* [CP] Copy Pods Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 97C146EE1CF9000F007C117D /* Runner.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t97C146E61CF9000F007C117D /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 1300;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t97C146ED1CF9000F007C117D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 7.3.1;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 97C146E51CF9000F007C117D;\n\t\t\tproductRefGroup = 97C146EF1CF9000F007C117D /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t97C146ED1CF9000F007C117D /* Runner */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t97C146EC1CF9000F007C117D /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,\n\t\t\t\tEF5AB736261F5EA600A60388 /* cloudAnchorKey.json in Resources */,\n\t\t\t\tEF5AB5D5261C9BD900A60388 /* GoogleService-Info.plist in Resources */,\n\t\t\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,\n\t\t\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Thin Binary\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" embed_and_thin\";\n\t\t};\n\t\t94B0826727C2A055D78A9B66 /* [CP] Embed Pods Frameworks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Embed Pods Frameworks\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t9740EEB61CF901F6004384FC /* Run Script */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Run Script\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" build\";\n\t\t};\n\t\tB6180CDA50A54DB55045479E /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"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\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\tC226EEF590F236CBFE1746B9 /* [CP] Copy Pods Resources */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Copy Pods Resources\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t97C146EA1CF9000F007C117D /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,\n\t\t\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t97C146FA1CF9000F007C117D /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146FB1CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C147001CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t249021D3217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t249021D4217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = 5TD7B79GK6;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Pods/ARCore/CloudAnchors/Frameworks\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 0.0.1;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = io.carius.lars.arFlutterPluginExample;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t97C147031CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147041CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t97C147061CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = 5TD7B79GK6;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Pods/ARCore/CloudAnchors/Frameworks\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 0.0.1;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = io.carius.lars.arFlutterPluginExample;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147071CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = 5TD7B79GK6;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tFRAMEWORK_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"$(PROJECT_DIR)/Pods/ARCore/CloudAnchors/Frameworks\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 0.0.1;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = io.carius.lars.arFlutterPluginExample;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147031CF9000F007C117D /* Debug */,\n\t\t\t\t97C147041CF9000F007C117D /* Release */,\n\t\t\t\t249021D3217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147061CF9000F007C117D /* Debug */,\n\t\t\t\t97C147071CF9000F007C117D /* Release */,\n\t\t\t\t249021D4217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 97C146E61CF9000F007C117D /* Project object */;\n}\n"
  },
  {
    "path": "example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1300\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n               BuildableName = \"Runner.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "example/ios/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "example/lib/examples/cloudanchorexample.dart",
    "content": "import 'dart:convert';\n\nimport 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_session_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_object_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_anchor_manager.dart';\nimport 'package:ar_flutter_plugin/models/ar_anchor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:ar_flutter_plugin/ar_flutter_plugin.dart';\nimport 'package:ar_flutter_plugin/datatypes/config_planedetection.dart';\nimport 'package:ar_flutter_plugin/datatypes/node_types.dart';\nimport 'package:ar_flutter_plugin/datatypes/hittest_result_types.dart';\nimport 'package:ar_flutter_plugin/models/ar_node.dart';\nimport 'package:ar_flutter_plugin/models/ar_hittest_result.dart';\nimport 'package:vector_math/vector_math_64.dart';\nimport 'package:firebase_core/firebase_core.dart';\nimport 'package:cloud_firestore/cloud_firestore.dart';\nimport 'package:geoflutterfire/geoflutterfire.dart';\nimport 'package:geolocator/geolocator.dart';\n\nclass CloudAnchorWidget extends StatefulWidget {\n  CloudAnchorWidget({Key? key}) : super(key: key);\n  @override\n  _CloudAnchorWidgetState createState() => _CloudAnchorWidgetState();\n}\n\nclass _CloudAnchorWidgetState extends State<CloudAnchorWidget> {\n  // Firebase stuff\n  bool _initialized = false;\n  bool _error = false;\n  FirebaseManager firebaseManager = FirebaseManager();\n  Map<String, Map> anchorsInDownloadProgress = Map<String, Map>();\n\n  ARSessionManager? arSessionManager;\n  ARObjectManager? arObjectManager;\n  ARAnchorManager? arAnchorManager;\n  ARLocationManager? arLocationManager;\n\n  List<ARNode> nodes = [];\n  List<ARAnchor> anchors = [];\n  String lastUploadedAnchor = \"\";\n\n  bool readyToUpload = false;\n  bool readyToDownload = true;\n\n  @override\n  void initState() {\n    firebaseManager.initializeFlutterFire().then((value) => setState(() {\n          _initialized = value;\n          _error = !value;\n        }));\n\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    arSessionManager!.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    // Show error message if initialization failed\n    if (_error) {\n      return Scaffold(\n          appBar: AppBar(\n            title: const Text('Cloud Anchors'),\n          ),\n          body: Container(\n              child: Center(\n                  child: Column(\n            children: [\n              Text(\"Firebase initialization failed\"),\n              ElevatedButton(\n                  child: Text(\"Retry\"), onPressed: () => {initState()})\n            ],\n          ))));\n    }\n\n    // Show a loader until FlutterFire is initialized\n    if (!_initialized) {\n      return Scaffold(\n          appBar: AppBar(\n            title: const Text('Cloud Anchors'),\n          ),\n          body: Container(\n              child: Center(\n                  child: Column(children: [\n            CircularProgressIndicator(),\n            Text(\"Initializing Firebase\")\n          ]))));\n    }\n\n    return Scaffold(\n        appBar: AppBar(\n          title: const Text('Cloud Anchors'),\n        ),\n        body: Container(\n            child: Stack(children: [\n          ARView(\n            onARViewCreated: onARViewCreated,\n            planeDetectionConfig: PlaneDetectionConfig.horizontalAndVertical,\n          ),\n          Align(\n            alignment: FractionalOffset.bottomCenter,\n            child: Row(\n                mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n                children: [\n                  ElevatedButton(\n                      onPressed: onRemoveEverything,\n                      child: Text(\"Remove Everything\")),\n                ]),\n          ),\n          Align(\n            alignment: FractionalOffset.topCenter,\n            child: Row(\n                mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n                children: [\n                  Visibility(\n                      visible: readyToUpload,\n                      child: ElevatedButton(\n                          onPressed: onUploadButtonPressed,\n                          child: Text(\"Upload\"))),\n                  Visibility(\n                      visible: readyToDownload,\n                      child: ElevatedButton(\n                          onPressed: onDownloadButtonPressed,\n                          child: Text(\"Download\"))),\n                ]),\n          )\n        ])));\n  }\n\n  void onARViewCreated(\n      ARSessionManager arSessionManager,\n      ARObjectManager arObjectManager,\n      ARAnchorManager arAnchorManager,\n      ARLocationManager arLocationManager) {\n    this.arSessionManager = arSessionManager;\n    this.arObjectManager = arObjectManager;\n    this.arAnchorManager = arAnchorManager;\n    this.arLocationManager = arLocationManager;\n\n    this.arSessionManager!.onInitialize(\n          showFeaturePoints: false,\n          showPlanes: true,\n          customPlaneTexturePath: \"Images/triangle.png\",\n          showWorldOrigin: true,\n        );\n    this.arObjectManager!.onInitialize();\n    this.arAnchorManager!.initGoogleCloudAnchorMode();\n\n    this.arSessionManager!.onPlaneOrPointTap = onPlaneOrPointTapped;\n    this.arObjectManager!.onNodeTap = onNodeTapped;\n    this.arAnchorManager!.onAnchorUploaded = onAnchorUploaded;\n    this.arAnchorManager!.onAnchorDownloaded = onAnchorDownloaded;\n\n    this\n        .arLocationManager\n        !.startLocationUpdates()\n        .then((value) => null)\n        .onError((error, stackTrace) {\n      switch (error.toString()) {\n        case 'Location services disabled':\n          {\n            showAlertDialog(\n                context,\n                \"Action Required\",\n                \"To use cloud anchor functionality, please enable your location services\",\n                \"Settings\",\n                this.arLocationManager!.openLocationServicesSettings,\n                \"Cancel\");\n            break;\n          }\n\n        case 'Location permissions denied':\n          {\n            showAlertDialog(\n                context,\n                \"Action Required\",\n                \"To use cloud anchor functionality, please allow the app to access your device's location\",\n                \"Retry\",\n                this.arLocationManager!.startLocationUpdates,\n                \"Cancel\");\n            break;\n          }\n\n        case 'Location permissions permanently denied':\n          {\n            showAlertDialog(\n                context,\n                \"Action Required\",\n                \"To use cloud anchor functionality, please allow the app to access your device's location\",\n                \"Settings\",\n                this.arLocationManager!.openAppPermissionSettings,\n                \"Cancel\");\n            break;\n          }\n\n        default:\n          {\n            this.arSessionManager!.onError(error.toString());\n            break;\n          }\n      }\n      this.arSessionManager!.onError(error.toString());\n    });\n  }\n\n  Future<void> onRemoveEverything() async {\n    anchors.forEach((anchor) {\n      this.arAnchorManager!.removeAnchor(anchor);\n    });\n    anchors = [];\n    if (lastUploadedAnchor != \"\") {\n      setState(() {\n        readyToDownload = true;\n        readyToUpload = false;\n      });\n    } else {\n      setState(() {\n        readyToDownload = true;\n        readyToUpload = false;\n      });\n    }\n  }\n\n  Future<void> onNodeTapped(List<String> nodeNames) async {\n    var foregroundNode =\n        nodes.firstWhere((element) => element.name == nodeNames.first);\n    this.arSessionManager!.onError(foregroundNode.data![\"onTapText\"]);\n  }\n\n  Future<void> onPlaneOrPointTapped(\n      List<ARHitTestResult> hitTestResults) async {\n    var singleHitTestResult = hitTestResults.firstWhere(\n        (hitTestResult) => hitTestResult.type == ARHitTestResultType.plane);\n    if (singleHitTestResult != null) {\n      var newAnchor = ARPlaneAnchor(\n          transformation: singleHitTestResult.worldTransform, ttl: 2);\n      bool? didAddAnchor = await this.arAnchorManager!.addAnchor(newAnchor);\n      if (didAddAnchor ?? false) {\n        this.anchors.add(newAnchor);\n        // Add note to anchor\n        var newNode = ARNode(\n            type: NodeType.webGLB,\n            uri: \"https://github.com/KhronosGroup/glTF-Sample-Models/raw/master/2.0/Duck/glTF-Binary/Duck.glb\",\n            scale: Vector3(0.2, 0.2, 0.2),\n            position: Vector3(0.0, 0.0, 0.0),\n            rotation: Vector4(1.0, 0.0, 0.0, 0.0),\n            data: {\"onTapText\": \"Ouch, that hurt!\"});\n        bool? didAddNodeToAnchor =\n            await this.arObjectManager!.addNode(newNode, planeAnchor: newAnchor);\n        if (didAddNodeToAnchor ?? false) {\n          this.nodes.add(newNode);\n          setState(() {\n            readyToUpload = true;\n          });\n        } else {\n          this.arSessionManager!.onError(\"Adding Node to Anchor failed\");\n        }\n      } else {\n        this.arSessionManager!.onError(\"Adding Anchor failed\");\n      }\n    }\n  }\n\n  Future<void> onUploadButtonPressed() async {\n    this.arAnchorManager!.uploadAnchor(this.anchors.last);\n    setState(() {\n      readyToUpload = false;\n    });\n  }\n\n  onAnchorUploaded(ARAnchor anchor) {\n    // Upload anchor information to firebase\n    firebaseManager.uploadAnchor(anchor,\n        currentLocation: this.arLocationManager!.currentLocation);\n    // Upload child nodes to firebase\n    if (anchor is ARPlaneAnchor) {\n      anchor.childNodes.forEach((nodeName) => firebaseManager.uploadObject(\n          nodes.firstWhere((element) => element.name == nodeName)));\n    }\n    setState(() {\n      readyToDownload = true;\n      readyToUpload = false;\n    });\n    this.arSessionManager!.onError(\"Upload successful\");\n  }\n\n  ARAnchor onAnchorDownloaded(Map<String, dynamic>serializedAnchor) {\n    final anchor = ARPlaneAnchor.fromJson(anchorsInDownloadProgress[serializedAnchor[\"cloudanchorid\"]] as Map<String, dynamic>);\n    anchorsInDownloadProgress.remove(anchor.cloudanchorid);\n    this.anchors.add(anchor);\n\n    // Download nodes attached to this anchor\n    firebaseManager.getObjectsFromAnchor(anchor, (snapshot) {\n      snapshot.docs.forEach((objectDoc) {\n        ARNode object = ARNode.fromMap(objectDoc.data() as Map<String, dynamic>);\n        arObjectManager!.addNode(object, planeAnchor: anchor);\n        this.nodes.add(object);\n      });\n    });\n\n    return anchor;\n  }\n\n  Future<void> onDownloadButtonPressed() async {\n    //this.arAnchorManager.downloadAnchor(lastUploadedAnchor);\n    //firebaseManager.downloadLatestAnchor((snapshot) {\n    //  final cloudAnchorId = snapshot.docs.first.get(\"cloudanchorid\");\n    //  anchorsInDownloadProgress[cloudAnchorId] = snapshot.docs.first.data();\n    //  arAnchorManager.downloadAnchor(cloudAnchorId);\n    //});\n\n    // Get anchors within a radius of 100m of the current device's location\n    if (this.arLocationManager!.currentLocation != null) {\n      firebaseManager.downloadAnchorsByLocation((snapshot) {\n        final cloudAnchorId = snapshot.get(\"cloudanchorid\");\n        anchorsInDownloadProgress[cloudAnchorId] = snapshot.data() as Map<String, dynamic>;\n        arAnchorManager!.downloadAnchor(cloudAnchorId);\n      }, this.arLocationManager!.currentLocation, 0.1);\n      setState(() {\n        readyToDownload = false;\n      });\n    } else {\n      this\n          .arSessionManager!\n          .onError(\"Location updates not running, can't download anchors\");\n    }\n  }\n\n  void showAlertDialog(BuildContext context, String title, String content,\n      String buttonText, Function buttonFunction, String cancelButtonText) {\n    // set up the buttons\n    Widget cancelButton = ElevatedButton(\n      child: Text(cancelButtonText),\n      onPressed: () {\n        Navigator.of(context).pop();\n      },\n    );\n    Widget actionButton = ElevatedButton(\n      child: Text(buttonText),\n      onPressed: () {\n        buttonFunction();\n        Navigator.of(context).pop();\n      },\n    );\n\n    // set up the AlertDialog\n    AlertDialog alert = AlertDialog(\n      title: Text(title),\n      content: Text(content),\n      actions: [\n        cancelButton,\n        actionButton,\n      ],\n    );\n\n    // show the dialog\n    showDialog(\n      context: context,\n      builder: (BuildContext context) {\n        return alert;\n      },\n    );\n  }\n}\n\n// Class for managing interaction with Firebase (in your own app, this can be put in a separate file to keep everything clean and tidy)\ntypedef FirebaseListener = void Function(QuerySnapshot snapshot);\ntypedef FirebaseDocumentStreamListener = void Function(\n    DocumentSnapshot snapshot);\n\nclass FirebaseManager {\n  FirebaseFirestore? firestore;\n  Geoflutterfire? geo;\n  CollectionReference? anchorCollection;\n  CollectionReference? objectCollection;\n\n  // Firebase initialization function\n  Future<bool> initializeFlutterFire() async {\n    try {\n      // Wait for Firebase to initialize\n      await Firebase.initializeApp();\n      geo = Geoflutterfire();\n      firestore = FirebaseFirestore.instance;\n      anchorCollection = FirebaseFirestore.instance.collection('anchors');\n      objectCollection = FirebaseFirestore.instance.collection('objects');\n      return true;\n    } catch (e) {\n      return false;\n    }\n  }\n\n  void uploadAnchor(ARAnchor anchor, {Position? currentLocation}) {\n    if (firestore == null) return;\n\n    var serializedAnchor = anchor.toJson();\n    var expirationTime = DateTime.now().millisecondsSinceEpoch / 1000 +\n        serializedAnchor[\"ttl\"] * 24 * 60 * 60;\n    serializedAnchor[\"expirationTime\"] = expirationTime;\n    // Add location\n    if (currentLocation != null) {\n      GeoFirePoint myLocation = geo!.point(\n          latitude: currentLocation.latitude,\n          longitude: currentLocation.longitude);\n      serializedAnchor[\"position\"] = myLocation.data;\n    }\n\n    anchorCollection!\n        .add(serializedAnchor)\n        .then((value) =>\n            print(\"Successfully added anchor: \" + serializedAnchor[\"name\"]))\n        .catchError((error) => print(\"Failed to add anchor: $error\"));\n  }\n\n  void uploadObject(ARNode node) {\n    if (firestore == null) return;\n\n    var serializedNode = node.toMap();\n\n    objectCollection!\n        .add(serializedNode)\n        .then((value) =>\n            print(\"Successfully added object: \" + serializedNode[\"name\"]))\n        .catchError((error) => print(\"Failed to add object: $error\"));\n  }\n\n  void downloadLatestAnchor(FirebaseListener listener) {\n    anchorCollection!\n        .orderBy(\"expirationTime\", descending: false)\n        .limitToLast(1)\n        .get()\n        .then((value) => listener(value))\n        .catchError(\n            (error) => (error) => print(\"Failed to download anchor: $error\"));\n  }\n\n  void downloadAnchorsByLocation(FirebaseDocumentStreamListener listener,\n      Position location, double radius) {\n    GeoFirePoint center =\n        geo!.point(latitude: location.latitude, longitude: location.longitude);\n\n    Stream<List<DocumentSnapshot>> stream = geo!\n        .collection(collectionRef: anchorCollection!)\n        .within(center: center, radius: radius, field: 'position');\n\n    stream.listen((List<DocumentSnapshot> documentList) {\n      documentList.forEach((element) {\n        listener(element);\n      });\n    });\n  }\n\n  void downloadAnchorsByChannel() {}\n\n  void getObjectsFromAnchor(ARPlaneAnchor anchor, FirebaseListener listener) {\n    objectCollection!\n        .where(\"name\", whereIn: anchor.childNodes)\n        .get()\n        .then((value) => listener(value))\n        .catchError((error) => print(\"Failed to download objects: $error\"));\n  }\n\n  void deleteExpiredDatabaseEntries() {\n    WriteBatch batch = FirebaseFirestore.instance.batch();\n    anchorCollection!\n        .where(\"expirationTime\",\n            isLessThan: DateTime.now().millisecondsSinceEpoch / 1000)\n        .get()\n        .then((anchorSnapshot) => anchorSnapshot.docs.forEach((anchorDoc) {\n              // Delete all objects attached to the expired anchor\n              objectCollection!\n                  .where(\"name\", arrayContainsAny: anchorDoc.get(\"childNodes\"))\n                  .get()\n                  .then((objectSnapshot) => objectSnapshot.docs.forEach(\n                      (objectDoc) => batch.delete(objectDoc.reference)));\n              // Delete the expired anchor\n              batch.delete(anchorDoc.reference);\n            }));\n    batch.commit();\n  }\n}\n"
  },
  {
    "path": "example/lib/examples/debugoptionsexample.dart",
    "content": "import 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_session_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_object_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_anchor_manager.dart';\nimport 'package:flutter/material.dart';\nimport 'package:ar_flutter_plugin/ar_flutter_plugin.dart';\nimport 'package:ar_flutter_plugin/datatypes/config_planedetection.dart';\n\nclass DebugOptionsWidget extends StatefulWidget {\n  DebugOptionsWidget({Key? key}) : super(key: key);\n  @override\n  _DebugOptionsWidgetState createState() => _DebugOptionsWidgetState();\n}\n\nclass _DebugOptionsWidgetState extends State<DebugOptionsWidget> {\n  ARSessionManager? arSessionManager;\n  ARObjectManager? arObjectManager;\n  bool _showFeaturePoints = false;\n  bool _showPlanes = false;\n  bool _showWorldOrigin = false;\n  bool _showAnimatedGuide = true;\n  String _planeTexturePath = \"Images/triangle.png\";\n  bool _handleTaps = false;\n\n  @override\n  void dispose() {\n    super.dispose();\n    arSessionManager!.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n        appBar: AppBar(\n          title: const Text('Debug Options'),\n        ),\n        body: Container(\n            child: Stack(children: [\n          ARView(\n            onARViewCreated: onARViewCreated,\n            planeDetectionConfig: PlaneDetectionConfig.horizontalAndVertical,\n            showPlatformType: true,\n          ),\n          Align(\n            alignment: FractionalOffset.bottomRight,\n            child: Container(\n              width: MediaQuery.of(context).size.width * 0.5,\n              color: Color(0xFFFFFFF).withOpacity(0.5),\n              child: Column(\n                crossAxisAlignment: CrossAxisAlignment.end,\n                mainAxisSize: MainAxisSize.min,\n                children: [\n                  SwitchListTile(\n                    title: const Text('Feature Points'),\n                    value: _showFeaturePoints,\n                    onChanged: (bool value) {\n                      setState(() {\n                        _showFeaturePoints = value;\n                        updateSessionSettings();\n                      });\n                    },\n                  ),\n                  SwitchListTile(\n                    title: const Text('Planes'),\n                    value: _showPlanes,\n                    onChanged: (bool value) {\n                      setState(() {\n                        _showPlanes = value;\n                        updateSessionSettings();\n                      });\n                    },\n                  ),\n                  SwitchListTile(\n                    title: const Text('World Origin'),\n                    value: _showWorldOrigin,\n                    onChanged: (bool value) {\n                      setState(() {\n                        _showWorldOrigin = value;\n                        updateSessionSettings();\n                      });\n                    },\n                  ),\n                ],\n              ),\n            ),\n          ),\n        ])));\n  }\n\n  void onARViewCreated(\n      ARSessionManager arSessionManager,\n      ARObjectManager arObjectManager,\n      ARAnchorManager arAnchorManager,\n      ARLocationManager arLocationManager) {\n    this.arSessionManager = arSessionManager;\n    this.arObjectManager = arObjectManager;\n\n    this.arSessionManager!.onInitialize(\n          showFeaturePoints: _showFeaturePoints,\n          showPlanes: _showPlanes,\n          customPlaneTexturePath: _planeTexturePath,\n          showWorldOrigin: _showWorldOrigin,\n          showAnimatedGuide: _showAnimatedGuide,\n          handleTaps: _handleTaps,\n        );\n    this.arObjectManager!.onInitialize();\n  }\n\n  void updateSessionSettings() {\n    this.arSessionManager!.onInitialize(\n          showFeaturePoints: _showFeaturePoints,\n          showPlanes: _showPlanes,\n          customPlaneTexturePath: _planeTexturePath,\n          showWorldOrigin: _showWorldOrigin,\n        );\n  }\n}\n"
  },
  {
    "path": "example/lib/examples/externalmodelmanagementexample.dart",
    "content": "import 'dart:convert';\n\nimport 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_session_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_object_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_anchor_manager.dart';\nimport 'package:ar_flutter_plugin/models/ar_anchor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:ar_flutter_plugin/ar_flutter_plugin.dart';\nimport 'package:ar_flutter_plugin/datatypes/config_planedetection.dart';\nimport 'package:ar_flutter_plugin/datatypes/node_types.dart';\nimport 'package:ar_flutter_plugin/datatypes/hittest_result_types.dart';\nimport 'package:ar_flutter_plugin/models/ar_node.dart';\nimport 'package:ar_flutter_plugin/models/ar_hittest_result.dart';\nimport 'package:vector_math/vector_math_64.dart' as VectorMath;\nimport 'package:firebase_core/firebase_core.dart';\nimport 'package:cloud_firestore/cloud_firestore.dart';\nimport 'package:geoflutterfire/geoflutterfire.dart';\nimport 'package:geolocator/geolocator.dart';\n\nclass ExternalModelManagementWidget extends StatefulWidget {\n  ExternalModelManagementWidget({Key? key}) : super(key: key);\n  @override\n  _ExternalModelManagementWidgetState createState() =>\n      _ExternalModelManagementWidgetState();\n}\n\nclass _ExternalModelManagementWidgetState\n    extends State<ExternalModelManagementWidget> {\n  // Firebase stuff\n  bool _initialized = false;\n  bool _error = false;\n  FirebaseManager firebaseManager = FirebaseManager();\n  Map<String, Map> anchorsInDownloadProgress = Map<String, Map>();\n\n  ARSessionManager? arSessionManager;\n  ARObjectManager? arObjectManager;\n  ARAnchorManager? arAnchorManager;\n  ARLocationManager? arLocationManager;\n\n  List<ARNode> nodes = [];\n  List<ARAnchor> anchors = [];\n  String lastUploadedAnchor = \"\";\n  AvailableModel selectedModel = AvailableModel(\n      \"Duck\",\n      \"https://github.com/KhronosGroup/glTF-Sample-Models/raw/master/2.0/Duck/glTF-Binary/Duck.glb\",\n      \"\");\n\n  bool readyToUpload = false;\n  bool readyToDownload = true;\n  bool modelChoiceActive = false;\n\n  @override\n  void initState() {\n    firebaseManager.initializeFlutterFire().then((value) => setState(() {\n          _initialized = value;\n          _error = !value;\n        }));\n\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    arSessionManager!.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    // Show error message if initialization failed\n    if (_error) {\n      return Scaffold(\n          appBar: AppBar(\n            title: const Text('External Model Management'),\n          ),\n          body: Container(\n              child: Center(\n                  child: Column(\n            children: [\n              Text(\"Firebase initialization failed\"),\n              ElevatedButton(\n                  child: Text(\"Retry\"), onPressed: () => {initState()})\n            ],\n          ))));\n    }\n\n    // Show a loader until FlutterFire is initialized\n    if (!_initialized) {\n      return Scaffold(\n          appBar: AppBar(\n            title: const Text('External Model Management'),\n          ),\n          body: Container(\n              child: Center(\n                  child: Column(children: [\n            CircularProgressIndicator(),\n            Text(\"Initializing Firebase\")\n          ]))));\n    }\n\n    return Scaffold(\n        appBar: AppBar(\n            title: const Text('External Model Management'),\n            actions: <Widget>[\n              IconButton(\n                icon: Icon(Icons.pets),\n                onPressed: () {\n                  setState(() {\n                    modelChoiceActive = !modelChoiceActive;\n                  });\n                },\n              ),\n            ]),\n        body: Container(\n            child: Stack(children: [\n          ARView(\n            onARViewCreated: onARViewCreated,\n            planeDetectionConfig: PlaneDetectionConfig.horizontalAndVertical,\n          ),\n          Align(\n            alignment: FractionalOffset.bottomCenter,\n            child: Row(\n                mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n                children: [\n                  ElevatedButton(\n                      onPressed: onRemoveEverything,\n                      child: Text(\"Remove Everything\")),\n                ]),\n          ),\n          Align(\n            alignment: FractionalOffset.topCenter,\n            child: Row(\n                mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n                children: [\n                  Visibility(\n                      visible: readyToUpload,\n                      child: ElevatedButton(\n                          onPressed: onUploadButtonPressed,\n                          child: Text(\"Upload\"))),\n                  Visibility(\n                      visible: readyToDownload,\n                      child: ElevatedButton(\n                          onPressed: onDownloadButtonPressed,\n                          child: Text(\"Download\"))),\n                ]),\n          ),\n          Align(\n              alignment: FractionalOffset.centerLeft,\n              child: Visibility(\n                  visible: modelChoiceActive,\n                  child: ModelSelectionWidget(\n                      onTap: onModelSelected,\n                      firebaseManager: this.firebaseManager)))\n        ])));\n  }\n\n  void onARViewCreated(\n      ARSessionManager arSessionManager,\n      ARObjectManager arObjectManager,\n      ARAnchorManager arAnchorManager,\n      ARLocationManager arLocationManager) {\n    this.arSessionManager = arSessionManager;\n    this.arObjectManager = arObjectManager;\n    this.arAnchorManager = arAnchorManager;\n    this.arLocationManager = arLocationManager;\n\n    this.arSessionManager!.onInitialize(\n          showFeaturePoints: false,\n          showPlanes: true,\n          customPlaneTexturePath: \"Images/triangle.png\",\n          showWorldOrigin: true,\n        );\n    this.arObjectManager!.onInitialize();\n    this.arAnchorManager!.initGoogleCloudAnchorMode();\n\n    this.arSessionManager!.onPlaneOrPointTap = onPlaneOrPointTapped;\n    this.arObjectManager!.onNodeTap = onNodeTapped;\n    this.arAnchorManager!.onAnchorUploaded = onAnchorUploaded;\n    this.arAnchorManager!.onAnchorDownloaded = onAnchorDownloaded;\n\n    this\n        .arLocationManager!\n        .startLocationUpdates()\n        .then((value) => null)\n        .onError((error, stackTrace) {\n      switch (error.toString()) {\n        case 'Location services disabled':\n          {\n            showAlertDialog(\n                context,\n                \"Action Required\",\n                \"To use cloud anchor functionality, please enable your location services\",\n                \"Settings\",\n                this.arLocationManager!.openLocationServicesSettings,\n                \"Cancel\");\n            break;\n          }\n\n        case 'Location permissions denied':\n          {\n            showAlertDialog(\n                context,\n                \"Action Required\",\n                \"To use cloud anchor functionality, please allow the app to access your device's location\",\n                \"Retry\",\n                this.arLocationManager!.startLocationUpdates,\n                \"Cancel\");\n            break;\n          }\n\n        case 'Location permissions permanently denied':\n          {\n            showAlertDialog(\n                context,\n                \"Action Required\",\n                \"To use cloud anchor functionality, please allow the app to access your device's location\",\n                \"Settings\",\n                this.arLocationManager!.openAppPermissionSettings,\n                \"Cancel\");\n            break;\n          }\n\n        default:\n          {\n            this.arSessionManager!.onError(error.toString());\n            break;\n          }\n      }\n      this.arSessionManager!.onError(error.toString());\n    });\n  }\n\n  void onModelSelected(AvailableModel model) {\n    this.selectedModel = model;\n    this.arSessionManager!.onError(model.name + \" selected\");\n    setState(() {\n      modelChoiceActive = false;\n    });\n  }\n\n  Future<void> onRemoveEverything() async {\n    anchors.forEach((anchor) {\n      this.arAnchorManager!.removeAnchor(anchor);\n    });\n    anchors = [];\n    if (lastUploadedAnchor != \"\") {\n      setState(() {\n        readyToDownload = true;\n        readyToUpload = false;\n      });\n    } else {\n      setState(() {\n        readyToDownload = true;\n        readyToUpload = false;\n      });\n    }\n  }\n\n  Future<void> onNodeTapped(List<String> nodeNames) async {\n    var foregroundNode = nodes.firstWhere((element) => element.name == nodeNames.first);\n    this.arSessionManager!.onError(foregroundNode.data![\"onTapText\"]);\n  }\n\n  Future<void> onPlaneOrPointTapped(\n      List<ARHitTestResult> hitTestResults) async {\n    var singleHitTestResult = hitTestResults.firstWhere(\n        (hitTestResult) => hitTestResult.type == ARHitTestResultType.plane);\n    if (singleHitTestResult != null) {\n      var newAnchor = ARPlaneAnchor(\n          transformation: singleHitTestResult.worldTransform, ttl: 2);\n      bool? didAddAnchor = await this.arAnchorManager!.addAnchor(newAnchor);\n      if (didAddAnchor!) {\n        this.anchors.add(newAnchor);\n        // Add note to anchor\n        var newNode = ARNode(\n            type: NodeType.webGLB,\n            uri: this.selectedModel.uri,\n            scale: VectorMath.Vector3(0.2, 0.2, 0.2),\n            position: VectorMath.Vector3(0.0, 0.0, 0.0),\n            rotation: VectorMath.Vector4(1.0, 0.0, 0.0, 0.0),\n            data: {\"onTapText\": \"I am a \" + this.selectedModel.name});\n        bool? didAddNodeToAnchor =\n            await this.arObjectManager!.addNode(newNode, planeAnchor: newAnchor);\n        if (didAddNodeToAnchor!) {\n          this.nodes.add(newNode);\n          setState(() {\n            readyToUpload = true;\n          });\n        } else {\n          this.arSessionManager!.onError(\"Adding Node to Anchor failed\");\n        }\n      } else {\n        this.arSessionManager!.onError(\"Adding Anchor failed\");\n      }\n    }\n  }\n\n  Future<void> onUploadButtonPressed() async {\n    this.arAnchorManager!.uploadAnchor(this.anchors.last);\n    setState(() {\n      readyToUpload = false;\n    });\n  }\n\n  onAnchorUploaded(ARAnchor anchor) {\n    // Upload anchor information to firebase\n    firebaseManager.uploadAnchor(anchor,\n        currentLocation: this.arLocationManager!.currentLocation);\n    // Upload child nodes to firebase\n    if (anchor is ARPlaneAnchor) {\n      anchor.childNodes.forEach((nodeName) => firebaseManager.uploadObject(\n          nodes.firstWhere((element) => element.name == nodeName)));\n    }\n    setState(() {\n      readyToDownload = true;\n      readyToUpload = false;\n    });\n    this.arSessionManager!.onError(\"Upload successful\");\n  }\n\n  ARAnchor onAnchorDownloaded(Map<String,dynamic> serializedAnchor) {\n    final anchor = ARPlaneAnchor.fromJson(anchorsInDownloadProgress[serializedAnchor[\"cloudanchorid\"]] as Map<String,dynamic>);\n    anchorsInDownloadProgress.remove(anchor.cloudanchorid);\n    this.anchors.add(anchor);\n\n    // Download nodes attached to this anchor\n    firebaseManager.getObjectsFromAnchor(anchor, (snapshot) {\n      snapshot.docs.forEach((objectDoc) {\n        ARNode object = ARNode.fromMap(objectDoc.data() as Map<String, dynamic>);\n        arObjectManager!.addNode(object, planeAnchor: anchor);\n        this.nodes.add(object);\n      });\n    });\n\n    return anchor;\n  }\n\n  Future<void> onDownloadButtonPressed() async {\n    //this.arAnchorManager.downloadAnchor(lastUploadedAnchor);\n    //firebaseManager.downloadLatestAnchor((snapshot) {\n    //  final cloudAnchorId = snapshot.docs.first.get(\"cloudanchorid\");\n    //  anchorsInDownloadProgress[cloudAnchorId] = snapshot.docs.first.data();\n    //  arAnchorManager.downloadAnchor(cloudAnchorId);\n    //});\n\n    // Get anchors within a radius of 100m of the current device's location\n    if (this.arLocationManager!.currentLocation != null) {\n      firebaseManager.downloadAnchorsByLocation((snapshot) {\n        final cloudAnchorId = snapshot.get(\"cloudanchorid\");\n        anchorsInDownloadProgress[cloudAnchorId] = snapshot.data() as Map<String, dynamic>;\n        arAnchorManager!.downloadAnchor(cloudAnchorId);\n      }, this.arLocationManager!.currentLocation, 0.1);\n      setState(() {\n        readyToDownload = false;\n      });\n    } else {\n      this\n          .arSessionManager!\n          .onError(\"Location updates not running, can't download anchors\");\n    }\n  }\n\n  void showAlertDialog(BuildContext context, String title, String content,\n      String buttonText, Function buttonFunction, String cancelButtonText) {\n    // set up the buttons\n    Widget cancelButton = ElevatedButton(\n      child: Text(cancelButtonText),\n      onPressed: () {\n        Navigator.of(context).pop();\n      },\n    );\n    Widget actionButton = ElevatedButton(\n      child: Text(buttonText),\n      onPressed: () {\n        buttonFunction();\n        Navigator.of(context).pop();\n      },\n    );\n\n    // set up the AlertDialog\n    AlertDialog alert = AlertDialog(\n      title: Text(title),\n      content: Text(content),\n      actions: [\n        cancelButton,\n        actionButton,\n      ],\n    );\n\n    // show the dialog\n    showDialog(\n      context: context,\n      builder: (BuildContext context) {\n        return alert;\n      },\n    );\n  }\n}\n\n// Class for managing interaction with Firebase (in your own app, this can be put in a separate file to keep everything clean and tidy)\ntypedef FirebaseListener = void Function(QuerySnapshot snapshot);\ntypedef FirebaseDocumentStreamListener = void Function(\n    DocumentSnapshot snapshot);\n\nclass FirebaseManager {\n  FirebaseFirestore? firestore;\n  Geoflutterfire? geo;\n  CollectionReference? anchorCollection;\n  CollectionReference? objectCollection;\n  CollectionReference? modelCollection;\n\n  // Firebase initialization function\n  Future<bool> initializeFlutterFire() async {\n    try {\n      // Wait for Firebase to initialize\n      await Firebase.initializeApp();\n      geo = Geoflutterfire();\n      firestore = FirebaseFirestore.instance;\n      anchorCollection = FirebaseFirestore.instance.collection('anchors');\n      objectCollection = FirebaseFirestore.instance.collection('objects');\n      modelCollection = FirebaseFirestore.instance.collection('models');\n      return true;\n    } catch (e) {\n      return false;\n    }\n  }\n\n  void uploadAnchor(ARAnchor anchor, {Position? currentLocation}) {\n    if (firestore == null) return;\n\n    var serializedAnchor = anchor.toJson();\n    var expirationTime = DateTime.now().millisecondsSinceEpoch / 1000 +\n        serializedAnchor[\"ttl\"] * 24 * 60 * 60;\n    serializedAnchor[\"expirationTime\"] = expirationTime;\n    // Add location\n    if (currentLocation != null) {\n      GeoFirePoint myLocation = geo!.point(\n          latitude: currentLocation.latitude,\n          longitude: currentLocation.longitude);\n      serializedAnchor[\"position\"] = myLocation.data;\n    }\n\n    anchorCollection!\n        .add(serializedAnchor)\n        .then((value) =>\n            print(\"Successfully added anchor: \" + serializedAnchor[\"name\"]))\n        .catchError((error) => print(\"Failed to add anchor: $error\"));\n  }\n\n  void uploadObject(ARNode node) {\n    if (firestore == null) return;\n\n    var serializedNode = node.toMap();\n\n    objectCollection!\n        .add(serializedNode)\n        .then((value) =>\n            print(\"Successfully added object: \" + serializedNode[\"name\"]))\n        .catchError((error) => print(\"Failed to add object: $error\"));\n  }\n\n  void downloadLatestAnchor(FirebaseListener listener) {\n    anchorCollection!\n        .orderBy(\"expirationTime\", descending: false)\n        .limitToLast(1)\n        .get()\n        .then((value) => listener(value))\n        .catchError(\n            (error) => (error) => print(\"Failed to download anchor: $error\"));\n  }\n\n  void downloadAnchorsByLocation(FirebaseDocumentStreamListener listener,\n      Position location, double radius) {\n    GeoFirePoint center =\n        geo!.point(latitude: location.latitude, longitude: location.longitude);\n\n    Stream<List<DocumentSnapshot>> stream = geo!\n        .collection(collectionRef: anchorCollection!)\n        .within(center: center, radius: radius, field: 'position');\n\n    stream.listen((List<DocumentSnapshot> documentList) {\n      documentList.forEach((element) {\n        listener(element);\n      });\n    });\n  }\n\n  void downloadAnchorsByChannel() {}\n\n  void getObjectsFromAnchor(ARPlaneAnchor anchor, FirebaseListener listener) {\n    objectCollection!\n        .where(\"name\", whereIn: anchor.childNodes)\n        .get()\n        .then((value) => listener(value))\n        .catchError((error) => print(\"Failed to download objects: $error\"));\n  }\n\n  void deleteExpiredDatabaseEntries() {\n    WriteBatch batch = FirebaseFirestore.instance.batch();\n    anchorCollection!\n        .where(\"expirationTime\",\n            isLessThan: DateTime.now().millisecondsSinceEpoch / 1000)\n        .get()\n        .then((anchorSnapshot) => anchorSnapshot.docs.forEach((anchorDoc) {\n              // Delete all objects attached to the expired anchor\n              objectCollection!\n                  .where(\"name\", arrayContainsAny: anchorDoc.get(\"childNodes\"))\n                  .get()\n                  .then((objectSnapshot) => objectSnapshot.docs.forEach(\n                      (objectDoc) => batch.delete(objectDoc.reference)));\n              // Delete the expired anchor\n              batch.delete(anchorDoc.reference);\n            }));\n    batch.commit();\n  }\n\n  void downloadAvailableModels(FirebaseListener listener) {\n    modelCollection!\n        .get()\n        .then((value) => listener(value))\n        .catchError((error) => print(\"Failed to download objects: $error\"));\n  }\n}\n\nclass AvailableModel {\n  String name;\n  String uri;\n  String image;\n  AvailableModel(this.name, this.uri, this.image);\n}\n\nclass ModelSelectionWidget extends StatefulWidget {\n  final Function onTap;\n  final FirebaseManager firebaseManager;\n\n  ModelSelectionWidget({required this.onTap, required this.firebaseManager});\n\n  @override\n  _ModelSelectionWidgetState createState() => _ModelSelectionWidgetState();\n}\n\nclass _ModelSelectionWidgetState extends State<ModelSelectionWidget> {\n  List<AvailableModel> models = [];\n\n  String? selected;\n\n  @override\n  void initState() {\n    super.initState();\n    widget.firebaseManager.downloadAvailableModels((snapshot) {\n      snapshot.docs.forEach((element) {\n        setState(() {\n          models.add(AvailableModel(element.get(\"name\"), element.get(\"uri\"),\n              element.get(\"image\").first[\"downloadURL\"]));\n        });\n      });\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n        mainAxisAlignment: MainAxisAlignment.center,\n        crossAxisAlignment: CrossAxisAlignment.center,\n        children: [\n          DecoratedBox(\n            decoration: BoxDecoration(\n              color: Colors.white,\n              border: Border.all(\n                color: Colors.white,\n                style: BorderStyle.solid,\n                width: 4.0,\n              ),\n              borderRadius: BorderRadius.all(Radius.circular(5)),\n              shape: BoxShape.rectangle,\n              boxShadow: const <BoxShadow>[\n                BoxShadow(\n                  color: Color(0x66000000),\n                  blurRadius: 10.0,\n                  spreadRadius: 4.0,\n                )\n              ],\n            ),\n            child: Text('Choose a Model',\n                style: DefaultTextStyle.of(context)\n                    .style\n                    .apply(fontSizeFactor: 2.0)),\n          ),\n          Container(\n            height: MediaQuery.of(context).size.width * 0.65,\n            child: ListView.builder(\n              itemCount: models.length,\n              scrollDirection: Axis.horizontal,\n              itemBuilder: (context, index) {\n                return GestureDetector(\n                  onTap: () {\n                    widget.onTap(models[index]);\n                  },\n                  child: Card(\n                    elevation: 4.0,\n                    shape: RoundedRectangleBorder(\n                      borderRadius: BorderRadius.all(\n                        Radius.circular(5),\n                      ),\n                    ),\n                    child: Column(\n                      children: [\n                        Padding(\n                            padding: EdgeInsets.all(20),\n                            child: Image.network(models[index].image)),\n                        Text(\n                          models[index].name,\n                          style: DefaultTextStyle.of(context)\n                              .style\n                              .apply(fontSizeFactor: 2.0),\n                        )\n                      ],\n                    ),\n                  ),\n                );\n              },\n            ),\n          )\n        ]);\n  }\n}\n"
  },
  {
    "path": "example/lib/examples/localandwebobjectsexample.dart",
    "content": "import 'dart:io';\n\nimport 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_session_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_object_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_anchor_manager.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/material.dart';\nimport 'package:ar_flutter_plugin/ar_flutter_plugin.dart';\nimport 'package:ar_flutter_plugin/datatypes/config_planedetection.dart';\nimport 'package:ar_flutter_plugin/datatypes/node_types.dart';\nimport 'package:ar_flutter_plugin/models/ar_node.dart';\nimport 'package:flutter/services.dart';\nimport 'package:vector_math/vector_math_64.dart';\nimport 'dart:math';\nimport 'package:path_provider/path_provider.dart';\nimport 'package:flutter_archive/flutter_archive.dart';\n\nclass LocalAndWebObjectsWidget extends StatefulWidget {\n  LocalAndWebObjectsWidget({Key? key}) : super(key: key);\n  @override\n  _LocalAndWebObjectsWidgetState createState() =>\n      _LocalAndWebObjectsWidgetState();\n}\n\nclass _LocalAndWebObjectsWidgetState extends State<LocalAndWebObjectsWidget> {\n  ARSessionManager? arSessionManager;\n  ARObjectManager? arObjectManager;\n  //String localObjectReference;\n  ARNode? localObjectNode;\n  //String webObjectReference;\n  ARNode? webObjectNode;\n  ARNode? fileSystemNode;\n  HttpClient? httpClient;\n\n  @override\n  void dispose() {\n    super.dispose();\n    arSessionManager!.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n        appBar: AppBar(\n          title: const Text('Local & Web Objects'),\n        ),\n        body: Container(\n            child: Stack(children: [\n          ARView(\n            onARViewCreated: onARViewCreated,\n            planeDetectionConfig: PlaneDetectionConfig.horizontalAndVertical,\n          ),\n          Align(\n              alignment: FractionalOffset.bottomCenter,\n              child:\n                  Column(mainAxisAlignment: MainAxisAlignment.end, children: [\n                Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n                  children: [\n                    ElevatedButton(\n                        onPressed: onFileSystemObjectAtOriginButtonPressed,\n                        child: Text(\"Add/Remove Filesystem\\nObject at Origin\")),\n                  ],\n                ),\n                Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n                  children: [\n                    ElevatedButton(\n                        onPressed: onLocalObjectAtOriginButtonPressed,\n                        child: Text(\"Add/Remove Local\\nObject at Origin\")),\n                    ElevatedButton(\n                        onPressed: onWebObjectAtOriginButtonPressed,\n                        child: Text(\"Add/Remove Web\\nObject at Origin\")),\n                  ],\n                ),\n                Row(\n                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n                  children: [\n                    ElevatedButton(\n                        onPressed: onLocalObjectShuffleButtonPressed,\n                        child: Text(\"Shuffle Local\\nobject at Origin\")),\n                    ElevatedButton(\n                        onPressed: onWebObjectShuffleButtonPressed,\n                        child: Text(\"Shuffle Web\\nObject at Origin\")),\n                  ],\n                )\n              ]))\n        ])));\n  }\n\n  void onARViewCreated(\n      ARSessionManager arSessionManager,\n      ARObjectManager arObjectManager,\n      ARAnchorManager arAnchorManager,\n      ARLocationManager arLocationManager) {\n    this.arSessionManager = arSessionManager;\n    this.arObjectManager = arObjectManager;\n\n    this.arSessionManager!.onInitialize(\n          showFeaturePoints: false,\n          showPlanes: true,\n          customPlaneTexturePath: \"Images/triangle.png\",\n          showWorldOrigin: true,\n          handleTaps: false,\n        );\n    this.arObjectManager!.onInitialize();\n\n    //Download model to file system\n    httpClient = new HttpClient();\n    _downloadFile(\n        \"https://github.com/KhronosGroup/glTF-Sample-Models/raw/master/2.0/Duck/glTF-Binary/Duck.glb\",\n        \"LocalDuck.glb\");\n    // Alternative to use type fileSystemAppFolderGLTF2:\n    //_downloadAndUnpack(\n    //    \"https://drive.google.com/uc?export=download&id=1fng7yiK0DIR0uem7XkV2nlPSGH9PysUs\",\n    //    \"Chicken_01.zip\");\n  }\n\n  Future<File> _downloadFile(String url, String filename) async {\n    var request = await httpClient!.getUrl(Uri.parse(url));\n    var response = await request.close();\n    var bytes = await consolidateHttpClientResponseBytes(response);\n    String dir = (await getApplicationDocumentsDirectory()).path;\n    File file = new File('$dir/$filename');\n    await file.writeAsBytes(bytes);\n    print(\"Downloading finished, path: \" + '$dir/$filename');\n    return file;\n  }\n\n  Future<void> _downloadAndUnpack(String url, String filename) async {\n    var request = await httpClient!.getUrl(Uri.parse(url));\n    var response = await request.close();\n    var bytes = await consolidateHttpClientResponseBytes(response);\n    String dir = (await getApplicationDocumentsDirectory()).path;\n    File file = new File('$dir/$filename');\n    await file.writeAsBytes(bytes);\n    print(\"Downloading finished, path: \" + '$dir/$filename');\n\n    // To print all files in the directory: print(Directory(dir).listSync());\n    try {\n      await ZipFile.extractToDirectory(\n          zipFile: File('$dir/$filename'), destinationDir: Directory(dir));\n      print(\"Unzipping successful\");\n    } catch (e) {\n      print(\"Unzipping failed: \" + e.toString());\n    }\n  }\n\n  Future<void> onLocalObjectAtOriginButtonPressed() async {\n    if (this.localObjectNode != null) {\n      this.arObjectManager!.removeNode(this.localObjectNode!);\n      this.localObjectNode = null;\n    } else {\n      var newNode = ARNode(\n          type: NodeType.localGLTF2,\n          uri: \"Models/Chicken_01/Chicken_01.gltf\",\n          scale: Vector3(0.2, 0.2, 0.2),\n          position: Vector3(0.0, 0.0, 0.0),\n          rotation: Vector4(1.0, 0.0, 0.0, 0.0));\n      bool? didAddLocalNode = await this.arObjectManager!.addNode(newNode);\n      this.localObjectNode = (didAddLocalNode!) ? newNode : null;\n    }\n  }\n\n  Future<void> onWebObjectAtOriginButtonPressed() async {\n    if (this.webObjectNode != null) {\n      this.arObjectManager!.removeNode(this.webObjectNode!);\n      this.webObjectNode = null;\n    } else {\n      var newNode = ARNode(\n          type: NodeType.webGLB,\n          uri:\n              \"https://github.com/KhronosGroup/glTF-Sample-Models/raw/master/2.0/Duck/glTF-Binary/Duck.glb\",\n          scale: Vector3(0.2, 0.2, 0.2));\n      bool? didAddWebNode = await this.arObjectManager!.addNode(newNode);\n      this.webObjectNode = (didAddWebNode!) ? newNode : null;\n    }\n  }\n\n  Future<void> onFileSystemObjectAtOriginButtonPressed() async {\n    if (this.fileSystemNode != null) {\n      this.arObjectManager!.removeNode(this.fileSystemNode!);\n      this.fileSystemNode = null;\n    } else {\n      var newNode = ARNode(\n          type: NodeType.fileSystemAppFolderGLB,\n          uri: \"LocalDuck.glb\",\n          scale: Vector3(0.2, 0.2, 0.2));\n      //Alternative to use type fileSystemAppFolderGLTF2:\n      //var newNode = ARNode(\n      //    type: NodeType.fileSystemAppFolderGLTF2,\n      //    uri: \"Chicken_01.gltf\",\n      //    scale: Vector3(0.2, 0.2, 0.2));\n      bool? didAddFileSystemNode = await this.arObjectManager!.addNode(newNode);\n      this.fileSystemNode = (didAddFileSystemNode!) ? newNode : null;\n    }\n  }\n\n  Future<void> onLocalObjectShuffleButtonPressed() async {\n    if (this.localObjectNode != null) {\n      var newScale = Random().nextDouble() / 3;\n      var newTranslationAxis = Random().nextInt(3);\n      var newTranslationAmount = Random().nextDouble() / 3;\n      var newTranslation = Vector3(0, 0, 0);\n      newTranslation[newTranslationAxis] = newTranslationAmount;\n      var newRotationAxisIndex = Random().nextInt(3);\n      var newRotationAmount = Random().nextDouble();\n      var newRotationAxis = Vector3(0, 0, 0);\n      newRotationAxis[newRotationAxisIndex] = 1.0;\n\n      final newTransform = Matrix4.identity();\n\n      newTransform.setTranslation(newTranslation);\n      newTransform.rotate(newRotationAxis, newRotationAmount);\n      newTransform.scale(newScale);\n\n      this.localObjectNode!.transform = newTransform;\n    }\n  }\n\n  Future<void> onWebObjectShuffleButtonPressed() async {\n    if (this.webObjectNode != null) {\n      var newScale = Random().nextDouble() / 3;\n      var newTranslationAxis = Random().nextInt(3);\n      var newTranslationAmount = Random().nextDouble() / 3;\n      var newTranslation = Vector3(0, 0, 0);\n      newTranslation[newTranslationAxis] = newTranslationAmount;\n      var newRotationAxisIndex = Random().nextInt(3);\n      var newRotationAmount = Random().nextDouble();\n      var newRotationAxis = Vector3(0, 0, 0);\n      newRotationAxis[newRotationAxisIndex] = 1.0;\n\n      final newTransform = Matrix4.identity();\n\n      newTransform.setTranslation(newTranslation);\n      newTransform.rotate(newRotationAxis, newRotationAmount);\n      newTransform.scale(newScale);\n\n      this.webObjectNode!.transform = newTransform;\n    }\n  }\n}\n"
  },
  {
    "path": "example/lib/examples/objectgesturesexample.dart",
    "content": "import 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_session_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_object_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_anchor_manager.dart';\nimport 'package:ar_flutter_plugin/models/ar_anchor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:ar_flutter_plugin/ar_flutter_plugin.dart';\nimport 'package:ar_flutter_plugin/datatypes/config_planedetection.dart';\nimport 'package:ar_flutter_plugin/datatypes/node_types.dart';\nimport 'package:ar_flutter_plugin/datatypes/hittest_result_types.dart';\nimport 'package:ar_flutter_plugin/models/ar_node.dart';\nimport 'package:ar_flutter_plugin/models/ar_hittest_result.dart';\nimport 'package:flutter/services.dart';\nimport 'package:vector_math/vector_math_64.dart';\nimport 'dart:math';\n\nclass ObjectGesturesWidget extends StatefulWidget {\n  ObjectGesturesWidget({Key? key}) : super(key: key);\n  @override\n  _ObjectGesturesWidgetState createState() => _ObjectGesturesWidgetState();\n}\n\nclass _ObjectGesturesWidgetState extends State<ObjectGesturesWidget> {\n  ARSessionManager? arSessionManager;\n  ARObjectManager? arObjectManager;\n  ARAnchorManager? arAnchorManager;\n\n  List<ARNode> nodes = [];\n  List<ARAnchor> anchors = [];\n\n  @override\n  void dispose() {\n    super.dispose();\n    arSessionManager!.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n        appBar: AppBar(\n          title: const Text('Object Transformation Gestures'),\n        ),\n        body: Container(\n            child: Stack(children: [\n          ARView(\n            onARViewCreated: onARViewCreated,\n            planeDetectionConfig: PlaneDetectionConfig.horizontalAndVertical,\n          ),\n          Align(\n            alignment: FractionalOffset.bottomCenter,\n            child: Row(\n                mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n                children: [\n                  ElevatedButton(\n                      onPressed: onRemoveEverything,\n                      child: Text(\"Remove Everything\")),\n                ]),\n          )\n        ])));\n  }\n\n  void onARViewCreated(\n      ARSessionManager arSessionManager,\n      ARObjectManager arObjectManager,\n      ARAnchorManager arAnchorManager,\n      ARLocationManager arLocationManager) {\n    this.arSessionManager = arSessionManager;\n    this.arObjectManager = arObjectManager;\n    this.arAnchorManager = arAnchorManager;\n\n    this.arSessionManager!.onInitialize(\n          showFeaturePoints: false,\n          showPlanes: true,\n          customPlaneTexturePath: \"Images/triangle.png\",\n          showWorldOrigin: true,\n          handlePans: true,\n          handleRotation: true,\n        );\n    this.arObjectManager!.onInitialize();\n\n    this.arSessionManager!.onPlaneOrPointTap = onPlaneOrPointTapped;\n    this.arObjectManager!.onPanStart = onPanStarted;\n    this.arObjectManager!.onPanChange = onPanChanged;\n    this.arObjectManager!.onPanEnd = onPanEnded;\n    this.arObjectManager!.onRotationStart = onRotationStarted;\n    this.arObjectManager!.onRotationChange = onRotationChanged;\n    this.arObjectManager!.onRotationEnd = onRotationEnded;\n  }\n\n  Future<void> onRemoveEverything() async {\n    /*nodes.forEach((node) {\n      this.arObjectManager.removeNode(node);\n    });*/\n    anchors.forEach((anchor) {\n      this.arAnchorManager!.removeAnchor(anchor);\n    });\n    anchors = [];\n  }\n\n  Future<void> onPlaneOrPointTapped(\n      List<ARHitTestResult> hitTestResults) async {\n    var singleHitTestResult = hitTestResults.firstWhere(\n        (hitTestResult) => hitTestResult.type == ARHitTestResultType.plane);\n    if (singleHitTestResult != null) {\n      var newAnchor =\n          ARPlaneAnchor(transformation: singleHitTestResult.worldTransform);\n      bool? didAddAnchor = await this.arAnchorManager!.addAnchor(newAnchor);\n      if (didAddAnchor!) {\n        this.anchors.add(newAnchor);\n        // Add note to anchor\n        var newNode = ARNode(\n            type: NodeType.webGLB,\n            uri:\n                \"https://github.com/KhronosGroup/glTF-Sample-Models/raw/master/2.0/Duck/glTF-Binary/Duck.glb\",\n            scale: Vector3(0.2, 0.2, 0.2),\n            position: Vector3(0.0, 0.0, 0.0),\n            rotation: Vector4(1.0, 0.0, 0.0, 0.0));\n        bool? didAddNodeToAnchor =\n            await this.arObjectManager!.addNode(newNode, planeAnchor: newAnchor);\n        if (didAddNodeToAnchor!) {\n          this.nodes.add(newNode);\n        } else {\n          this.arSessionManager!.onError(\"Adding Node to Anchor failed\");\n        }\n      } else {\n        this.arSessionManager!.onError(\"Adding Anchor failed\");\n      }\n    }\n  }\n\n  onPanStarted(String nodeName) {\n    print(\"Started panning node \" + nodeName);\n  }\n\n  onPanChanged(String nodeName) {\n    print(\"Continued panning node \" + nodeName);\n  }\n\n  onPanEnded(String nodeName, Matrix4 newTransform) {\n    print(\"Ended panning node \" + nodeName);\n    final pannedNode =\n        this.nodes.firstWhere((element) => element.name == nodeName);\n\n    /*\n    * Uncomment the following command if you want to keep the transformations of the Flutter representations of the nodes up to date\n    * (e.g. if you intend to share the nodes through the cloud)\n    */\n    //pannedNode.transform = newTransform;\n  }\n\n  onRotationStarted(String nodeName) {\n    print(\"Started rotating node \" + nodeName);\n  }\n\n  onRotationChanged(String nodeName) {\n    print(\"Continued rotating node \" + nodeName);\n  }\n\n  onRotationEnded(String nodeName, Matrix4 newTransform) {\n    print(\"Ended rotating node \" + nodeName);\n    final rotatedNode =\n        this.nodes.firstWhere((element) => element.name == nodeName);\n\n    /*\n    * Uncomment the following command if you want to keep the transformations of the Flutter representations of the nodes up to date\n    * (e.g. if you intend to share the nodes through the cloud)\n    */\n    //rotatedNode.transform = newTransform;\n  }\n}\n"
  },
  {
    "path": "example/lib/examples/objectsonplanesexample.dart",
    "content": "import 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_session_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_object_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_anchor_manager.dart';\nimport 'package:ar_flutter_plugin/models/ar_anchor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:ar_flutter_plugin/ar_flutter_plugin.dart';\nimport 'package:ar_flutter_plugin/datatypes/config_planedetection.dart';\nimport 'package:ar_flutter_plugin/datatypes/node_types.dart';\nimport 'package:ar_flutter_plugin/datatypes/hittest_result_types.dart';\nimport 'package:ar_flutter_plugin/models/ar_node.dart';\nimport 'package:ar_flutter_plugin/models/ar_hittest_result.dart';\nimport 'package:flutter/services.dart';\nimport 'package:vector_math/vector_math_64.dart';\nimport 'dart:math';\n\nclass ObjectsOnPlanesWidget extends StatefulWidget {\n  ObjectsOnPlanesWidget({Key? key}) : super(key: key);\n  @override\n  _ObjectsOnPlanesWidgetState createState() => _ObjectsOnPlanesWidgetState();\n}\n\nclass _ObjectsOnPlanesWidgetState extends State<ObjectsOnPlanesWidget> {\n  ARSessionManager? arSessionManager;\n  ARObjectManager? arObjectManager;\n  ARAnchorManager? arAnchorManager;\n\n  List<ARNode> nodes = [];\n  List<ARAnchor> anchors = [];\n\n  @override\n  void dispose() {\n    super.dispose();\n    arSessionManager!.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n        appBar: AppBar(\n          title: const Text('Anchors & Objects on Planes'),\n        ),\n        body: Container(\n            child: Stack(children: [\n          ARView(\n            onARViewCreated: onARViewCreated,\n            planeDetectionConfig: PlaneDetectionConfig.horizontalAndVertical,\n          ),\n          Align(\n            alignment: FractionalOffset.bottomCenter,\n            child: Row(\n                mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n                children: [\n                  ElevatedButton(\n                      onPressed: onRemoveEverything,\n                      child: Text(\"Remove Everything\")),\n                ]),\n          )\n        ])));\n  }\n\n  void onARViewCreated(\n      ARSessionManager arSessionManager,\n      ARObjectManager arObjectManager,\n      ARAnchorManager arAnchorManager,\n      ARLocationManager arLocationManager) {\n    this.arSessionManager = arSessionManager;\n    this.arObjectManager = arObjectManager;\n    this.arAnchorManager = arAnchorManager;\n\n    this.arSessionManager!.onInitialize(\n          showFeaturePoints: false,\n          showPlanes: true,\n          customPlaneTexturePath: \"Images/triangle.png\",\n          showWorldOrigin: true,\n        );\n    this.arObjectManager!.onInitialize();\n\n    this.arSessionManager!.onPlaneOrPointTap = onPlaneOrPointTapped;\n    this.arObjectManager!.onNodeTap = onNodeTapped;\n  }\n\n  Future<void> onRemoveEverything() async {\n    /*nodes.forEach((node) {\n      this.arObjectManager.removeNode(node);\n    });*/\n    anchors.forEach((anchor) {\n      this.arAnchorManager!.removeAnchor(anchor);\n    });\n    anchors = [];\n  }\n\n  Future<void> onNodeTapped(List<String> nodes) async {\n    var number = nodes.length;\n    this.arSessionManager!.onError(\"Tapped $number node(s)\");\n  }\n\n  Future<void> onPlaneOrPointTapped(\n      List<ARHitTestResult> hitTestResults) async {\n    var singleHitTestResult = hitTestResults.firstWhere(\n        (hitTestResult) => hitTestResult.type == ARHitTestResultType.plane);\n    if (singleHitTestResult != null) {\n      var newAnchor =\n          ARPlaneAnchor(transformation: singleHitTestResult.worldTransform);\n      bool? didAddAnchor = await this.arAnchorManager!.addAnchor(newAnchor);\n      if (didAddAnchor!) {\n        this.anchors.add(newAnchor);\n        // Add note to anchor\n        var newNode = ARNode(\n            type: NodeType.webGLB,\n            uri:\n                \"https://github.com/KhronosGroup/glTF-Sample-Models/raw/master/2.0/Duck/glTF-Binary/Duck.glb\",\n            scale: Vector3(0.2, 0.2, 0.2),\n            position: Vector3(0.0, 0.0, 0.0),\n            rotation: Vector4(1.0, 0.0, 0.0, 0.0));\n        bool? didAddNodeToAnchor =\n            await this.arObjectManager!.addNode(newNode, planeAnchor: newAnchor);\n        if (didAddNodeToAnchor!) {\n          this.nodes.add(newNode);\n        } else {\n          this.arSessionManager!.onError(\"Adding Node to Anchor failed\");\n        }\n      } else {\n        this.arSessionManager!.onError(\"Adding Anchor failed\");\n      }\n      /*\n      // To add a node to the tapped position without creating an anchor, use the following code (Please mind: the function onRemoveEverything has to be adapted accordingly!):\n      var newNode = ARNode(\n          type: NodeType.localGLTF2,\n          uri: \"Models/Chicken_01/Chicken_01.gltf\",\n          scale: Vector3(0.2, 0.2, 0.2),\n          transformation: singleHitTestResult.worldTransform);\n      bool didAddWebNode = await this.arObjectManager.addNode(newNode);\n      if (didAddWebNode) {\n        this.nodes.add(newNode);\n      }*/\n    }\n  }\n}\n"
  },
  {
    "path": "example/lib/examples/screenshotexample.dart",
    "content": "import 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_session_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_object_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_anchor_manager.dart';\nimport 'package:ar_flutter_plugin/models/ar_anchor.dart';\nimport 'package:flutter/material.dart';\nimport 'package:ar_flutter_plugin/ar_flutter_plugin.dart';\nimport 'package:ar_flutter_plugin/datatypes/config_planedetection.dart';\nimport 'package:ar_flutter_plugin/datatypes/node_types.dart';\nimport 'package:ar_flutter_plugin/datatypes/hittest_result_types.dart';\nimport 'package:ar_flutter_plugin/models/ar_node.dart';\nimport 'package:ar_flutter_plugin/models/ar_hittest_result.dart';\nimport 'package:vector_math/vector_math_64.dart';\n\nclass ScreenshotWidget extends StatefulWidget {\n  const ScreenshotWidget({Key? key}) : super(key: key);\n  @override\n  _ScreenshotWidgetState createState() => _ScreenshotWidgetState();\n}\n\nclass _ScreenshotWidgetState extends State<ScreenshotWidget> {\n  ARSessionManager? arSessionManager;\n  ARObjectManager? arObjectManager;\n  ARAnchorManager? arAnchorManager;\n\n  List<ARNode> nodes = [];\n  List<ARAnchor> anchors = [];\n\n  @override\n  void dispose() {\n    super.dispose();\n    arSessionManager!.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n        appBar: AppBar(\n          title: const Text('Screenshots'),\n        ),\n        body: \n        Container(\n            child:\n          Stack(children: [\n          ARView(\n            onARViewCreated: onARViewCreated,\n            planeDetectionConfig: PlaneDetectionConfig.horizontalAndVertical,\n          ),\n          Align(\n            alignment: FractionalOffset.bottomCenter,\n            child: Row(\n                mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n                children: [\n                  ElevatedButton(\n                      onPressed: onRemoveEverything,\n                      child: const Text(\"Remove Everything\")),\n                  ElevatedButton(\n                      onPressed: onTakeScreenshot,\n                      child: const Text(\"Take Screenshot\")),\n                ]),\n          )\n        ])));\n  }\n\n  void onARViewCreated(\n      ARSessionManager arSessionManager,\n      ARObjectManager arObjectManager,\n      ARAnchorManager arAnchorManager,\n      ARLocationManager arLocationManager) {\n    this.arSessionManager = arSessionManager;\n    this.arObjectManager = arObjectManager;\n    this.arAnchorManager = arAnchorManager;\n\n    this.arSessionManager!.onInitialize(\n          showFeaturePoints: false,\n          showPlanes: true,\n          customPlaneTexturePath: \"Images/triangle.png\",\n          showWorldOrigin: true,\n        );\n    this.arObjectManager!.onInitialize();\n\n    this.arSessionManager!.onPlaneOrPointTap = onPlaneOrPointTapped;\n    this.arObjectManager!.onNodeTap = onNodeTapped;\n  }\n\n  Future<void> onRemoveEverything() async {\n    /*nodes.forEach((node) {\n      this.arObjectManager.removeNode(node);\n    });*/\n    // anchors.forEach((anchor)\n    for (var anchor in anchors)\n     {\n      arAnchorManager!.removeAnchor(anchor);\n    };\n    anchors = [];\n  }\n\n  Future<void> onTakeScreenshot() async {\n    var image = await arSessionManager!.snapshot();\n    await showDialog(\n        context: context,\n        builder: (_) => Dialog(\n              child: Container(\n                decoration: BoxDecoration(\n                    image: DecorationImage(image: image, fit: BoxFit.cover)),\n              ),\n            ));\n  }\n\n  Future<void> onNodeTapped(List<String> nodes) async {\n    var number = nodes.length;\n    arSessionManager!.onError(\"Tapped $number node(s)\");\n  }\n\n  Future<void> onPlaneOrPointTapped(\n      List<ARHitTestResult> hitTestResults) async {\n    var singleHitTestResult = hitTestResults.firstWhere(\n        (hitTestResult) => hitTestResult.type == ARHitTestResultType.plane);\n    if (singleHitTestResult != null) {\n      var newAnchor =\n          ARPlaneAnchor(transformation: singleHitTestResult.worldTransform);\n      bool? didAddAnchor = await arAnchorManager!.addAnchor(newAnchor);\n      if (didAddAnchor != null && didAddAnchor) {\n        anchors.add(newAnchor);\n        // Add note to anchor\n        var newNode = ARNode(\n            type: NodeType.webGLB,\n            uri:\n                \"https://github.com/KhronosGroup/glTF-Sample-Models/raw/master/2.0/Duck/glTF-Binary/Duck.glb\",\n            scale: Vector3(0.2, 0.2, 0.2),\n            position: Vector3(0.0, 0.0, 0.0),\n            rotation: Vector4(1.0, 0.0, 0.0, 0.0));\n        bool? didAddNodeToAnchor =\n            await arObjectManager!.addNode(newNode, planeAnchor: newAnchor);\n        \n        if (didAddNodeToAnchor != null && didAddNodeToAnchor) {\n          nodes.add(newNode);\n        } else {\n          arSessionManager!.onError(\"Adding Node to Anchor failed\");\n        }\n      } else {\n        arSessionManager!.onError(\"Adding Anchor failed\");\n      }\n      /*\n      // To add a node to the tapped position without creating an anchor, use the following code (Please mind: the function onRemoveEverything has to be adapted accordingly!):\n      var newNode = ARNode(\n          type: NodeType.localGLTF2,\n          uri: \"Models/Chicken_01/Chicken_01.gltf\",\n          scale: Vector3(0.2, 0.2, 0.2),\n          transformation: singleHitTestResult.worldTransform);\n      bool didAddWebNode = await this.arObjectManager.addNode(newNode);\n      if (didAddWebNode) {\n        this.nodes.add(newNode);\n      }*/\n    }\n  }\n}"
  },
  {
    "path": "example/lib/main.dart",
    "content": "import 'package:ar_flutter_plugin_example/examples/externalmodelmanagementexample.dart';\nimport 'package:ar_flutter_plugin_example/examples/objectsonplanesexample.dart';\nimport 'package:flutter/material.dart';\nimport 'dart:async';\n\nimport 'package:flutter/services.dart';\nimport 'package:ar_flutter_plugin/ar_flutter_plugin.dart';\n\nimport 'package:ar_flutter_plugin_example/examples/cloudanchorexample.dart';\nimport 'package:ar_flutter_plugin_example/examples/localandwebobjectsexample.dart';\nimport 'package:ar_flutter_plugin_example/examples/debugoptionsexample.dart';\n\nimport 'examples/objectgesturesexample.dart';\nimport 'examples/screenshotexample.dart';\n\nvoid main() {\n  WidgetsFlutterBinding.ensureInitialized();\n  runApp(MyApp());\n}\n\nclass MyApp extends StatefulWidget {\n  @override\n  _MyAppState createState() => _MyAppState();\n}\n\nclass _MyAppState extends State<MyApp> {\n  String _platformVersion = 'Unknown';\n  static const String _title = 'AR Plugin Demo';\n\n  @override\n  void initState() {\n    super.initState();\n    initPlatformState();\n  }\n\n  // Platform messages are asynchronous, so we initialize in an async method.\n  Future<void> initPlatformState() async {\n    String platformVersion;\n    // Platform messages may fail, so we use a try/catch PlatformException.\n    try {\n      platformVersion = await ArFlutterPlugin.platformVersion;\n    } on PlatformException {\n      platformVersion = 'Failed to get platform version.';\n    }\n\n    // If the widget was removed from the tree while the asynchronous platform\n    // message was in flight, we want to discard the reply rather than calling\n    // setState to update our non-existent appearance.\n    if (!mounted) return;\n\n    setState(() {\n      _platformVersion = platformVersion;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      home: Scaffold(\n        appBar: AppBar(\n          title: const Text(_title),\n        ),\n        body: Column(children: [\n          Text('Running on: $_platformVersion\\n'),\n          Expanded(\n            child: ExampleList(),\n          ),\n        ]),\n      ),\n    );\n  }\n}\n\nclass ExampleList extends StatelessWidget {\n  ExampleList({Key? key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    final examples = [\n      Example(\n          'Debug Options',\n          'Visualize feature points, planes and world coordinate system',\n          () => Navigator.push(context,\n              MaterialPageRoute(builder: (context) => DebugOptionsWidget()))),\n      Example(\n          'Local & Online Objects',\n          'Place 3D objects from Flutter assets and the web into the scene',\n          () => Navigator.push(\n              context,\n              MaterialPageRoute(\n                  builder: (context) => LocalAndWebObjectsWidget()))),\n      Example(\n          'Anchors & Objects on Planes',\n          'Place 3D objects on detected planes using anchors',\n          () => Navigator.push(\n              context,\n              MaterialPageRoute(\n                  builder: (context) => ObjectsOnPlanesWidget()))),\n      Example(\n          'Object Transformation Gestures',\n          'Rotate and Pan Objects',\n          () => Navigator.push(context,\n              MaterialPageRoute(builder: (context) => ObjectGesturesWidget()))),\n      Example(\n          'Screenshots',\n          'Place 3D objects on planes and take screenshots',\n          () => Navigator.push(context,\n              MaterialPageRoute(builder: (context) => ScreenshotWidget()))),\n      Example(\n          'Cloud Anchors',\n          'Place and retrieve 3D objects using the Google Cloud Anchor API',\n          () => Navigator.push(context,\n              MaterialPageRoute(builder: (context) => CloudAnchorWidget()))),\n      Example(\n          'External Model Management',\n          'Similar to Cloud Anchors example, but uses external database to choose from available 3D models',\n          () => Navigator.push(\n              context,\n              MaterialPageRoute(\n                  builder: (context) => ExternalModelManagementWidget())))\n    ];\n    return ListView(\n      children:\n          examples.map((example) => ExampleCard(example: example)).toList(),\n    );\n  }\n}\n\nclass ExampleCard extends StatelessWidget {\n  ExampleCard({Key? key, required this.example}) : super(key: key);\n  final Example example;\n\n  @override\n  build(BuildContext context) {\n    return Card(\n      child: InkWell(\n        splashColor: Colors.blue.withAlpha(30),\n        onTap: () {\n          example.onTap();\n        },\n        child: ListTile(\n          title: Text(example.name),\n          subtitle: Text(example.description),\n        ),\n      ),\n    );\n  }\n}\n\nclass Example {\n  const Example(this.name, this.description, this.onTap);\n  final String name;\n  final String description;\n  final Function onTap;\n}\n"
  },
  {
    "path": "example/pubspec.yaml",
    "content": "name: ar_flutter_plugin_example\ndescription: Demonstrates how to use the ar_flutter_plugin plugin.\n\n# The following line prevents the package from being accidentally published to\n# pub.dev using `pub publish`. This is preferred for private packages.\npublish_to: 'none' # Remove this line if you wish to publish to pub.dev\n\nenvironment:\n  sdk: \">=2.16.1 <3.0.0\"\n\ndependencies:\n  flutter:\n    sdk: flutter\n\n  ar_flutter_plugin:\n    # When depending on this package from a real application you should use:\n    #   ar_flutter_plugin: ^x.y.z\n    # See https://dart.dev/tools/pub/dependencies#version-constraints\n    # The example app is bundled with the plugin so we use a path dependency on\n    # the parent directory to use the current plugin's version.\n    path: ../\n\n  # The following adds the Cupertino Icons font to your application.\n  # Use with the CupertinoIcons class for iOS style icons.\n  cupertino_icons: ^1.0.2\n\n  # Firebase dependencies to shared and manage cloud anchor IDs and related content\n  firebase_core: \"^1.6.0\"\n  cloud_firestore: \"^2.5.3\"\n  geoflutterfire: \"^3.0.1\"\n  path_provider: ^2.0.3\n  flutter_archive: ^4.0.1\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter.\nflutter:\n\n  # The following line ensures that the Material Icons font is\n  # included with your application, so that you can use the icons in\n  # the material Icons class.\n  uses-material-design: true\n\n  # To add assets to your application, add an assets section, like this:\n  assets:\n    - Images/triangle.png\n    - Models/Chicken_01/\n  #   - images/a_dot_ham.jpeg\n\n  # An image asset can refer to one or more resolution-specific \"variants\", see\n  # https://flutter.dev/assets-and-images/#resolution-aware.\n\n  # For details regarding adding assets from package dependencies, see\n  # https://flutter.dev/assets-and-images/#from-packages\n\n  # To add custom fonts to your application, add a fonts section here,\n  # in this \"flutter\" section. Each entry in this list should have a\n  # \"family\" key with the font family name, and a \"fonts\" key with a\n  # list giving the asset and other descriptors for the font. For\n  # example:\n  # fonts:\n  #   - family: Schyler\n  #     fonts:\n  #       - asset: fonts/Schyler-Regular.ttf\n  #       - asset: fonts/Schyler-Italic.ttf\n  #         style: italic\n  #   - family: Trajan Pro\n  #     fonts:\n  #       - asset: fonts/TrajanPro.ttf\n  #       - asset: fonts/TrajanPro_Bold.ttf\n  #         weight: 700\n  #\n  # For details regarding fonts from package dependencies,\n  # see https://flutter.dev/custom-fonts/#from-packages\n"
  },
  {
    "path": "example/test/widget_test.dart",
    "content": "// This is a basic Flutter widget test.\n//\n// To perform an interaction with a widget in your test, use the WidgetTester\n// utility that Flutter provides. For example, you can send tap and scroll\n// gestures. You can also use WidgetTester to find child widgets in the widget\n// tree, read text, and verify that the values of widget properties are correct.\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nimport 'package:ar_flutter_plugin_example/main.dart';\n\nvoid main() {\n  testWidgets('Verify Platform version', (WidgetTester tester) async {\n    // Build our app and trigger a frame.\n    await tester.pumpWidget(MyApp());\n\n    // Verify that platform version is retrieved.\n    expect(\n      find.byWidgetPredicate(\n        (Widget widget) => widget is Text &&\n                           widget.data!.startsWith('Running on:'),\n      ),\n      findsOneWidget,\n    );\n  });\n}\n"
  },
  {
    "path": "ios/.gitignore",
    "content": ".idea/\n.vagrant/\n.sconsign.dblite\n.svn/\n\n.DS_Store\n*.swp\nprofile\n\nDerivedData/\nbuild/\nGeneratedPluginRegistrant.h\nGeneratedPluginRegistrant.m\n\n.generated/\n\n*.pbxuser\n*.mode1v3\n*.mode2v3\n*.perspectivev3\n\n!default.pbxuser\n!default.mode1v3\n!default.mode2v3\n!default.perspectivev3\n\nxcuserdata\n\n*.moved-aside\n\n*.pyc\n*sync/\nIcon?\n.tags*\n\n/Flutter/Generated.xcconfig\n/Flutter/flutter_export_environment.sh"
  },
  {
    "path": "ios/Classes/ArFlutterPlugin.h",
    "content": "#import <Flutter/Flutter.h>\n\n@interface ArFlutterPlugin : NSObject<FlutterPlugin>\n@end\n"
  },
  {
    "path": "ios/Classes/ArFlutterPlugin.m",
    "content": "#import \"ArFlutterPlugin.h\"\n#if __has_include(<ar_flutter_plugin/ar_flutter_plugin-Swift.h>)\n#import <ar_flutter_plugin/ar_flutter_plugin-Swift.h>\n#else\n// Support project import fallback if the generated compatibility header\n// is not copied when this plugin is created as a library.\n// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816\n#import \"ar_flutter_plugin-Swift.h\"\n#endif\n\n@implementation ArFlutterPlugin\n+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {\n  [SwiftArFlutterPlugin registerWithRegistrar:registrar];\n}\n@end\n"
  },
  {
    "path": "ios/Classes/ArModelBuilder.swift",
    "content": "import UIKit\nimport Foundation\nimport ARKit\nimport GLTFSceneKit\nimport Combine\n\n// Responsible for creating Renderables and Nodes\nclass ArModelBuilder: NSObject {\n\n    func makePlane(anchor: ARPlaneAnchor, flutterAssetFile: String?) -> SCNNode {\n        let plane = SCNPlane(width: CGFloat(anchor.extent.x), height: CGFloat(anchor.extent.z))\n        //Create material\n        let material = SCNMaterial()\n        let opacity: CGFloat\n        \n        if let textureSourcePath = flutterAssetFile {\n            // Use given asset as plane texture\n            let key = FlutterDartProject.lookupKey(forAsset: textureSourcePath)\n            if let image = UIImage(named: key, in: Bundle.main,compatibleWith: nil){\n                // Asset was found so we can use it\n                material.diffuse.contents = image\n                material.diffuse.wrapS = .repeat\n                material.diffuse.wrapT = .repeat\n                plane.materials = [material]\n                opacity = 1.0\n            } else {\n                // Use standard planes\n                opacity = 0.3\n            }\n        } else {\n            // Use standard planes\n            opacity = 0.3\n        }\n        \n        let planeNode = SCNNode(geometry: plane)\n        planeNode.position = SCNVector3Make(anchor.center.x, 0, anchor.center.z)\n        // rotate plane by 90 degrees to match the anchor (planes are vertical by default)\n        planeNode.eulerAngles.x = -.pi / 2\n\n        planeNode.opacity = opacity\n\n        return planeNode\n    }\n\n    func updatePlaneNode(planeNode: SCNNode, anchor: ARPlaneAnchor){\n        if let plane = planeNode.geometry as? SCNPlane {\n            // Update plane dimensions\n            plane.width = CGFloat(anchor.extent.x)\n            plane.height = CGFloat(anchor.extent.z)\n            // Update texture of planes\n            let imageSize: Float = 65 // in mm\n            let repeatAmount: Float = 1000 / imageSize //how often per meter we need to repeat the image\n            if let gridMaterial = plane.materials.first {\n                gridMaterial.diffuse.contentsTransform = SCNMatrix4MakeScale(anchor.extent.x * repeatAmount, anchor.extent.z * repeatAmount, 1)\n            }\n        }\n       planeNode.position = SCNVector3Make(anchor.center.x, 0, anchor.center.z)\n    }\n\n    // Creates a node from a given gltf2 (.gltf) model in the Flutter assets folder\n    func makeNodeFromGltf(name: String, modelPath: String, transformation: Array<NSNumber>?) -> SCNNode? {\n        \n        var scene: SCNScene\n        let node: SCNNode = SCNNode()\n\n        do {\n            let sceneSource = try GLTFSceneSource(named: modelPath)\n            scene = try sceneSource.scene()\n\n            for child in scene.rootNode.childNodes {\n                child.scale = SCNVector3(0.01,0.01,0.01) // Compensate for the different model dimension definitions in iOS and Android (meters vs. millimeters)\n                //child.eulerAngles.z = -.pi // Compensate for the different model coordinate definitions in iOS and Android\n                //child.eulerAngles.y = -.pi // Compensate for the different model coordinate definitions in iOS and Android\n                node.addChildNode(child.flattenedClone())\n            }\n\n            node.name = name\n            if let transform = transformation {\n                node.transform = deserializeMatrix4(transform)\n            }\n\n            return node\n        } catch {\n            print(\"\\(error.localizedDescription)\")\n            return nil\n        }\n    }\n\n    // Creates a node from a given gltf2 (.gltf) model in the Flutter assets folder\n    func makeNodeFromFileSystemGltf(name: String, modelPath: String, transformation: Array<NSNumber>?) -> SCNNode? {\n        \n        var scene: SCNScene\n        let node: SCNNode = SCNNode()\n\n        do {\n            let sceneSource = try GLTFSceneSource(path: modelPath)\n            scene = try sceneSource.scene()\n\n            for child in scene.rootNode.childNodes {\n                child.scale = SCNVector3(0.01,0.01,0.01) // Compensate for the different model dimension definitions in iOS and Android (meters vs. millimeters)\n                //child.eulerAngles.z = -.pi // Compensate for the different model coordinate definitions in iOS and Android\n                //child.eulerAngles.y = -.pi // Compensate for the different model coordinate definitions in iOS and Android\n                node.addChildNode(child.flattenedClone())\n            }\n\n            node.name = name\n            if let transform = transformation {\n                node.transform = deserializeMatrix4(transform)\n            }\n\n            return node\n        } catch {\n            print(\"\\(error.localizedDescription)\")\n            return nil\n        }\n    }\n    \n    // Creates a node from a given glb model in the app's documents directory\n    func makeNodeFromFileSystemGLB(name: String, modelPath: String, transformation: Array<NSNumber>?) -> SCNNode? {\n        \n        var scene: SCNScene\n        let node: SCNNode = SCNNode()\n\n        do {\n            let sceneSource = try GLTFSceneSource(path: modelPath)\n            scene = try sceneSource.scene()\n\n            for child in scene.rootNode.childNodes {\n                child.scale = SCNVector3(0.01,0.01,0.01) // Compensate for the different model dimension definitions in iOS and Android (meters vs. millimeters)\n                //child.eulerAngles.z = -.pi // Compensate for the different model coordinate definitions in iOS and Android\n                //child.eulerAngles.y = -.pi // Compensate for the different model coordinate definitions in iOS and Android\n                node.addChildNode(child.flattenedClone())\n            }\n\n            node.name = name\n            if let transform = transformation {\n                node.transform = deserializeMatrix4(transform)\n            }\n\n            return node\n        } catch {\n            print(\"\\(error.localizedDescription)\")\n            return nil\n        }\n    }\n    \n    // Creates a node form a given glb model path\n    func makeNodeFromWebGlb(name: String, modelURL: String, transformation: Array<NSNumber>?) -> Future<SCNNode?, Never> {\n        \n        return Future {promise in\n            var node: SCNNode? = SCNNode()\n            \n            let handler: (URL?, URLResponse?, Error?) -> Void = {(url: URL?, urlResponse: URLResponse?, error: Error?) -> Void in\n                // If response code is not 200, link was invalid, so return\n                if ((urlResponse as? HTTPURLResponse)?.statusCode != 200) {\n                    print(\"makeNodeFromWebGltf received non-200 response code\")\n                    node = nil\n                    promise(.success(node))\n                } else {\n                    guard let fileURL = url else { return }\n                    do {\n                        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)\n                        let documentsDirectory = paths[0]\n                        let targetURL = documentsDirectory.appendingPathComponent(urlResponse!.url!.lastPathComponent)\n                        \n                        try? FileManager.default.removeItem(at: targetURL) //remove item if it's already there\n                        try FileManager.default.copyItem(at: fileURL, to: targetURL)\n\n                        do {\n                            let sceneSource = GLTFSceneSource(url: targetURL)\n                            let scene = try sceneSource.scene()\n\n                            for child in scene.rootNode.childNodes {\n                                child.scale = SCNVector3(0.01,0.01,0.01) // Compensate for the different model dimension definitions in iOS and Android (meters vs. millimeters)\n                                //child.eulerAngles.z = -.pi // Compensate for the different model coordinate definitions in iOS and Android\n                                //child.eulerAngles.y = -.pi // Compensate for the different model coordinate definitions in iOS and Android\n                                node?.addChildNode(child)\n                            }\n\n                            node?.name = name\n                            if let transform = transformation {\n                                node?.transform = deserializeMatrix4(transform)\n                            }\n                            /*node?.scale = worldScale\n                            node?.position = worldPosition\n                            node?.worldOrientation = worldRotation*/\n\n                        } catch {\n                            print(\"\\(error.localizedDescription)\")\n                            node = nil\n                        }\n                        \n                        // Delete file to avoid cluttering device storage (at some point, caching can be included)\n                        try FileManager.default.removeItem(at: targetURL)\n                        \n                        promise(.success(node))\n                    } catch {\n                        node = nil\n                        promise(.success(node))\n                    }\n                }\n                \n            }\n            \n    \n            let downloadTask = URLSession.shared.downloadTask(with: URL(string: modelURL)!, completionHandler: handler)\n            \n            downloadTask.resume()\n            \n        }\n        \n    }\n    \n}\n"
  },
  {
    "path": "ios/Classes/CloudAnchorHandler.swift",
    "content": "import Foundation\nimport ARCoreCloudAnchors\n\n// Listener that can be attached to hosing or resolving processes\nprotocol CloudAnchorListener {\n    // Callback to invoke when cloud anchor task finishes\n    func onCloudTaskComplete(anchorName: String?, anchor: GARAnchor?)\n}\n\n// Class for handling logic regarding the Google Cloud Anchor API\nclass CloudAnchorHandler: NSObject, GARSessionDelegate {\n    private var session: GARSession\n    private var pendingAnchors = [GARAnchor: (String?, CloudAnchorListener?)]()\n    \n    init(session: GARSession){\n        self.session = session\n    }\n    \n    func hostCloudAnchor(anchorName: String, anchor: ARAnchor, listener: CloudAnchorListener?) {\n        do {\n            let newAnchor = try self.session.hostCloudAnchor(anchor)\n            // Register listener so it is invoked when the operation finishes\n            pendingAnchors[newAnchor] = (anchorName, listener)\n        } catch {\n            print(error)\n        }\n    }\n    \n    func hostCloudAnchorWithTtl(anchorName: String, anchor: ARAnchor, listener: CloudAnchorListener?, ttl: Int) {\n        do {\n            let newAnchor = try self.session.hostCloudAnchor(anchor, ttlDays: ttl)\n            // Register listener so it is invoked when the operation finishes\n            pendingAnchors[newAnchor] = (anchorName, listener)\n        } catch {\n            print(error)\n        }\n    }\n    \n    func resolveCloudAnchor(anchorId: String, listener: CloudAnchorListener?) {\n        do {\n            let newAnchor = try self.session.resolveCloudAnchor(anchorId)\n            // Register listener so it is invoked when the operation finishes\n            pendingAnchors[newAnchor] = (nil, listener)\n        } catch {\n            print(error)\n        }\n        \n    }\n        \n    func session(_ session: GARSession, didHost anchor: GARAnchor) {\n        pendingAnchors[anchor]?.1?.onCloudTaskComplete(anchorName: pendingAnchors[anchor]?.0, anchor: anchor)\n    }\n    \n    func session(_ session: GARSession, didFailToHost anchor: GARAnchor) {\n        pendingAnchors[anchor]?.1?.onCloudTaskComplete(anchorName: pendingAnchors[anchor]?.0, anchor: anchor)\n    }\n    \n    func session(_ session: GARSession, didResolve anchor: GARAnchor) {\n        pendingAnchors[anchor]?.1?.onCloudTaskComplete(anchorName: pendingAnchors[anchor]?.0, anchor: anchor)\n    }\n    \n    func session(_ session: GARSession, didFailToResolve anchor: GARAnchor) {\n        pendingAnchors[anchor]?.1?.onCloudTaskComplete(anchorName: pendingAnchors[anchor]?.0, anchor: anchor)\n    }\n    \n    // Remove all listeners\n    func clearListeners() {\n        pendingAnchors.removeAll()\n    }\n}\n"
  },
  {
    "path": "ios/Classes/IosARView.swift",
    "content": "import Flutter\nimport UIKit\nimport Foundation\nimport ARKit\nimport Combine\nimport ARCoreCloudAnchors\n\nclass IosARView: NSObject, FlutterPlatformView, ARSCNViewDelegate, UIGestureRecognizerDelegate, ARSessionDelegate {\n    let sceneView: ARSCNView\n    let coachingView: ARCoachingOverlayView\n    let sessionManagerChannel: FlutterMethodChannel\n    let objectManagerChannel: FlutterMethodChannel\n    let anchorManagerChannel: FlutterMethodChannel\n    var showPlanes = false\n    var customPlaneTexturePath: String? = nil\n    private var trackedPlanes = [UUID: (SCNNode, SCNNode)]()\n    let modelBuilder = ArModelBuilder()\n    \n    var cancellableCollection = Set<AnyCancellable>() //Used to store all cancellables in (needed for working with Futures)\n    var anchorCollection = [String: ARAnchor]() //Used to bookkeep all anchors created by Flutter calls\n    \n    private var cloudAnchorHandler: CloudAnchorHandler? = nil\n    private var arcoreSession: GARSession? = nil\n    private var arcoreMode: Bool = false\n    private var configuration: ARWorldTrackingConfiguration!\n    private var tappedPlaneAnchorAlignment = ARPlaneAnchor.Alignment.horizontal // default alignment\n    \n    private var panStartLocation: CGPoint?\n    private var panCurrentLocation: CGPoint?\n    private var panCurrentVelocity: CGPoint?\n    private var panCurrentTranslation: CGPoint?\n    private var rotationStartLocation: CGPoint?\n    private var rotation: CGFloat?\n    private var rotationVelocity: CGFloat?\n    private var panningNode: SCNNode?\n    private var panningNodeCurrentWorldLocation: SCNVector3?\n\n    init(\n        frame: CGRect,\n        viewIdentifier viewId: Int64,\n        arguments args: Any?,\n        binaryMessenger messenger: FlutterBinaryMessenger\n    ) {\n        self.sceneView = ARSCNView(frame: frame)\n        self.coachingView = ARCoachingOverlayView(frame: frame)\n        \n        self.sessionManagerChannel = FlutterMethodChannel(name: \"arsession_\\(viewId)\", binaryMessenger: messenger)\n        self.objectManagerChannel = FlutterMethodChannel(name: \"arobjects_\\(viewId)\", binaryMessenger: messenger)\n        self.anchorManagerChannel = FlutterMethodChannel(name: \"aranchors_\\(viewId)\", binaryMessenger: messenger)\n        super.init()\n\n        let configuration = ARWorldTrackingConfiguration() // Create default configuration before initializeARView is called\n        self.sceneView.delegate = self\n        self.coachingView.delegate = self\n        self.sceneView.session.run(configuration)\n        self.sceneView.session.delegate = self\n\n        self.sessionManagerChannel.setMethodCallHandler(self.onSessionMethodCalled)\n        self.objectManagerChannel.setMethodCallHandler(self.onObjectMethodCalled)\n        self.anchorManagerChannel.setMethodCallHandler(self.onAnchorMethodCalled)\n    }\n\n    func view() -> UIView {\n        return self.sceneView\n    }\n\n    func onDispose(_ result:FlutterResult) {\n                sceneView.session.pause()\n                self.sessionManagerChannel.setMethodCallHandler(nil)\n                self.objectManagerChannel.setMethodCallHandler(nil)\n                self.anchorManagerChannel.setMethodCallHandler(nil)\n                result(nil)\n            }\n\n    func onSessionMethodCalled(_ call :FlutterMethodCall, _ result:FlutterResult) {\n        let arguments = call.arguments as? Dictionary<String, Any>\n\n        switch call.method {\n            case \"init\":\n                //self.sessionManagerChannel.invokeMethod(\"onError\", arguments: [\"SessionTEST from iOS\"])\n                //result(nil)\n                initializeARView(arguments: arguments!, result: result)\n                break\n            case \"getCameraPose\":\n                if let cameraPose = sceneView.session.currentFrame?.camera.transform {\n                    result(serializeMatrix(cameraPose))\n                } else {\n                    result(FlutterError())\n                }\n                break\n            case \"getAnchorPose\":\n            if let cameraPose = anchorCollection[arguments?[\"anchorId\"] as! String]?.transform {\n                    result(serializeMatrix(cameraPose))\n                } else {\n                    result(FlutterError())\n                }\n                break\n            case \"snapshot\":\n                // call the SCNView Snapshot method and return the Image\n                let snapshotImage = sceneView.snapshot()\n                if let bytes = snapshotImage.pngData() {\n                    let data = FlutterStandardTypedData(bytes:bytes)\n                    result(data)\n                } else {\n                    result(nil)\n                }\n            case \"dispose\":\n                onDispose(result)\n                result(nil)\n                break\n            default:\n                result(FlutterMethodNotImplemented)\n                break\n        }\n    }\n\n    func onObjectMethodCalled(_ call :FlutterMethodCall, _ result: @escaping FlutterResult) {\n        let arguments = call.arguments as? Dictionary<String, Any>\n          \n        switch call.method {\n            case \"init\":\n                self.objectManagerChannel.invokeMethod(\"onError\", arguments: [\"ObjectTEST from iOS\"])\n                result(nil)\n                break\n            case \"addNode\":\n                addNode(dict_node: arguments!).sink(receiveCompletion: {completion in }, receiveValue: { val in\n                       result(val)\n                    }).store(in: &self.cancellableCollection)\n                break\n            case \"addNodeToPlaneAnchor\":\n                if let dict_node = arguments![\"node\"] as? Dictionary<String, Any>, let dict_anchor = arguments![\"anchor\"] as? Dictionary<String, Any> {\n                    addNode(dict_node: dict_node, dict_anchor: dict_anchor).sink(receiveCompletion: {completion in }, receiveValue: { val in\n                           result(val)\n                        }).store(in: &self.cancellableCollection)\n                }\n                break\n            case \"removeNode\":\n                if let name = arguments![\"name\"] as? String {\n                    sceneView.scene.rootNode.childNode(withName: name, recursively: true)?.removeFromParentNode()\n                }\n                break\n            case \"transformationChanged\":\n                if let name = arguments![\"name\"] as? String, let transform = arguments![\"transformation\"] as? Array<NSNumber> {\n                    transformNode(name: name, transform: transform)\n                    result(nil)\n                }\n                break\n            default:\n                result(FlutterMethodNotImplemented)\n                break\n        }\n    }\n\n    func onAnchorMethodCalled(_ call :FlutterMethodCall, _ result: @escaping FlutterResult) {\n        let arguments = call.arguments as? Dictionary<String, Any>\n          \n        switch call.method {\n            case \"init\":\n                self.objectManagerChannel.invokeMethod(\"onError\", arguments: [\"ObjectTEST from iOS\"])\n                result(nil)\n                break\n            case \"addAnchor\":\n                if let type = arguments![\"type\"] as? Int {\n                    switch type {\n                    case 0: //Plane Anchor\n                        if let transform = arguments![\"transformation\"] as? Array<NSNumber>, let name = arguments![\"name\"] as? String {\n                            addPlaneAnchor(transform: transform, name: name)\n                            result(true)\n                        }\n                        result(false)\n                        break\n                    default:\n                        result(false)\n                    \n                    }\n                }\n                result(nil)\n                break\n            case \"removeAnchor\":\n                if let name = arguments![\"name\"] as? String {\n                    deleteAnchor(anchorName: name)\n                }\n                break\n            case \"initGoogleCloudAnchorMode\":\n                arcoreSession = try! GARSession.session()\n\n                if (arcoreSession != nil){\n                    let configuration = GARSessionConfiguration();\n                    configuration.cloudAnchorMode = .enabled;\n                    arcoreSession?.setConfiguration(configuration, error: nil);\n                    if let token = JWTGenerator().generateWebToken(){\n                        arcoreSession!.setAuthToken(token)\n                        \n                        cloudAnchorHandler = CloudAnchorHandler(session: arcoreSession!)\n                        arcoreSession!.delegate = cloudAnchorHandler\n                        arcoreSession!.delegateQueue = DispatchQueue.main\n                        \n                        arcoreMode = true\n                    } else {\n                        sessionManagerChannel.invokeMethod(\"onError\", arguments: [\"Error generating JWT, have you added cloudAnchorKey.json into the example/ios/Runner directory?\"])\n                    }\n                } else {\n                    sessionManagerChannel.invokeMethod(\"onError\", arguments: [\"Error initializing Google AR Session\"])\n                }\n                    \n                break\n            case \"uploadAnchor\":\n                if let anchorName = arguments![\"name\"] as? String, let anchor = anchorCollection[anchorName] {\n                    print(\"---------------- HOSTING INITIATED ------------------\")\n                    if let ttl = arguments![\"ttl\"] as? Int {\n                        cloudAnchorHandler?.hostCloudAnchorWithTtl(anchorName: anchorName, anchor: anchor, listener: cloudAnchorUploadedListener(parent: self), ttl: ttl)\n                    } else {\n                        cloudAnchorHandler?.hostCloudAnchor(anchorName: anchorName, anchor: anchor, listener: cloudAnchorUploadedListener(parent: self))\n                    }\n                }\n                result(true)\n                break\n            case \"downloadAnchor\":\n                if let anchorId = arguments![\"cloudanchorid\"] as? String {\n                    print(\"---------------- RESOLVING INITIATED ------------------\")\n                    cloudAnchorHandler?.resolveCloudAnchor(anchorId: anchorId, listener: cloudAnchorDownloadedListener(parent: self))\n                }\n                break\n            default:\n                result(FlutterMethodNotImplemented)\n                break\n        }\n    }\n\n    func initializeARView(arguments: Dictionary<String,Any>, result: FlutterResult){\n        // Set plane detection configuration\n        self.configuration = ARWorldTrackingConfiguration()\n        self.configuration.environmentTexturing = .automatic\n        if let planeDetectionConfig = arguments[\"planeDetectionConfig\"] as? Int {\n            switch planeDetectionConfig {\n                case 1: \n                    configuration.planeDetection = .horizontal\n                \n                case 2: \n                    if #available(iOS 11.3, *) {\n                        configuration.planeDetection = .vertical\n                    }\n                case 3: \n                    if #available(iOS 11.3, *) {\n                        configuration.planeDetection = [.horizontal, .vertical]\n                    }\n                default: \n                    configuration.planeDetection = []\n            }\n        }\n\n        // Set plane rendering options\n        if let configShowPlanes = arguments[\"showPlanes\"] as? Bool {\n            showPlanes = configShowPlanes\n            if (showPlanes){\n                // Visualize currently tracked planes\n                for plane in trackedPlanes.values {\n                    plane.0.addChildNode(plane.1)\n                }\n            } else {\n                // Remove currently visualized planes\n                for plane in trackedPlanes.values {\n                    plane.1.removeFromParentNode()\n                }\n            }\n        }\n        if let configCustomPlaneTexturePath = arguments[\"customPlaneTexturePath\"] as? String {\n            customPlaneTexturePath = configCustomPlaneTexturePath\n        }\n\n        // Set debug options\n        var debugOptions = ARSCNDebugOptions().rawValue\n        if let showFeaturePoints = arguments[\"showFeaturePoints\"] as? Bool {\n            if (showFeaturePoints) {\n                debugOptions |= ARSCNDebugOptions.showFeaturePoints.rawValue\n            }\n        }\n        if let showWorldOrigin = arguments[\"showWorldOrigin\"] as? Bool {\n            if (showWorldOrigin) {\n                debugOptions |= ARSCNDebugOptions.showWorldOrigin.rawValue\n            }\n        }\n        self.sceneView.debugOptions = ARSCNDebugOptions(rawValue: debugOptions)\n        \n        if let configHandleTaps = arguments[\"handleTaps\"] as? Bool {\n            if (configHandleTaps){\n                let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))\n                tapGestureRecognizer.delegate = self\n                self.sceneView.gestureRecognizers?.append(tapGestureRecognizer)\n            }\n        }\n\n        if let configHandlePans = arguments[\"handlePans\"] as? Bool {\n            if (configHandlePans){\n                let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))\n                panGestureRecognizer.maximumNumberOfTouches = 1\n                panGestureRecognizer.delegate = self\n                self.sceneView.gestureRecognizers?.append(panGestureRecognizer)\n            }\n        }\n        \n        if let configHandleRotation = arguments[\"handleRotation\"] as? Bool {\n            if (configHandleRotation){\n                let rotationGestureRecognizer = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation(_:)))\n                rotationGestureRecognizer.delegate = self\n                self.sceneView.gestureRecognizers?.append(rotationGestureRecognizer)\n            }\n        }\n        \n        // Add coaching view\n        if let configShowAnimatedGuide = arguments[\"showAnimatedGuide\"] as? Bool {\n            if configShowAnimatedGuide {\n                if self.sceneView.superview != nil && self.coachingView.superview == nil {\n                    self.sceneView.addSubview(self.coachingView)\n        //            self.coachingView.translatesAutoresizingMaskIntoConstraints = false\n                    self.coachingView.autoresizingMask = [\n                          .flexibleWidth, .flexibleHeight\n                        ]\n                    self.coachingView.session = self.sceneView.session\n                    self.coachingView.activatesAutomatically = true\n                    if configuration.planeDetection == .horizontal {\n                        self.coachingView.goal = .horizontalPlane\n                    }else{\n                        self.coachingView.goal = .verticalPlane\n                    }\n                    // TODO: look into constraints issue. This causes a crash:\n                    /**\n                     Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors <NSLayoutXAxisAnchor:0x28342dec0 \"ARCoachingOverlayView:0x13a470ae0.centerX\"> and <NSLayoutXAxisAnchor:0x28342c680 \"FlutterTouchInterceptingView:0x10bad1c90.centerX\"> because they have no common ancestor.  Does the constraint or its anchors reference items in different view hierarchies?  That's illegal.'\n                     */\n        //            NSLayoutConstraint.activate([\n        //                self.coachingView.centerXAnchor.constraint(equalTo: self.sceneView.superview!.centerXAnchor),\n        //                self.coachingView.centerYAnchor.constraint(equalTo: self.sceneView.superview!.centerYAnchor),\n        //                self.coachingView.widthAnchor.constraint(equalTo: self.sceneView.superview!.widthAnchor),\n        //                self.coachingView.heightAnchor.constraint(equalTo: self.sceneView.superview!.heightAnchor)\n        //                ])\n                }\n            }\n        }\n    \n        // Update session configuration\n        self.sceneView.session.run(configuration)\n    }\n\n    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {\n        \n        if let planeAnchor = anchor as? ARPlaneAnchor{\n            let plane = modelBuilder.makePlane(anchor: planeAnchor, flutterAssetFile: customPlaneTexturePath)\n            trackedPlanes[anchor.identifier] = (node, plane)\n            if (showPlanes) {\n                node.addChildNode(plane)\n            }\n        }\n    }\n\n    func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {\n        \n        if let planeAnchor = anchor as? ARPlaneAnchor, let plane = trackedPlanes[anchor.identifier] {\n            modelBuilder.updatePlaneNode(planeNode: plane.1, anchor: planeAnchor)\n        }\n    }\n\n    func renderer(_ renderer: SCNSceneRenderer, didRemove node: SCNNode, for anchor: ARAnchor) {\n        trackedPlanes.removeValue(forKey: anchor.identifier)\n    }\n    \n    func session(_ session: ARSession, didUpdate frame: ARFrame) {\n        if (arcoreMode) {\n            do {\n                try arcoreSession!.update(frame)\n            } catch {\n                print(error)\n            }\n        }\n    }\n\n    func addNode(dict_node: Dictionary<String, Any>, dict_anchor: Dictionary<String, Any>? = nil) -> Future<Bool, Never> {\n\n        return Future {promise in\n            \n            switch (dict_node[\"type\"] as! Int) {\n                case 0: // GLTF2 Model from Flutter asset folder\n                    // Get path to given Flutter asset\n                    let key = FlutterDartProject.lookupKey(forAsset: dict_node[\"uri\"] as! String)\n                    // Add object to scene\n                    if let node: SCNNode = self.modelBuilder.makeNodeFromGltf(name: dict_node[\"name\"] as! String, modelPath: key, transformation: dict_node[\"transformation\"] as? Array<NSNumber>) {\n                        if let anchorName = dict_anchor?[\"name\"] as? String, let anchorType = dict_anchor?[\"type\"] as? Int {\n                            switch anchorType{\n                                case 0: //PlaneAnchor\n                                    if let anchor = self.anchorCollection[anchorName]{\n                                        // Attach node to the top-level node of the specified anchor\n                                        self.sceneView.node(for: anchor)?.addChildNode(node)\n                                        promise(.success(true))\n                                    } else {\n                                        promise(.success(false))\n                                    }\n                                default:\n                                    promise(.success(false))\n                                }\n                            \n                        } else {\n                            // Attach to top-level node of the scene\n                            self.sceneView.scene.rootNode.addChildNode(node)\n                            promise(.success(true))\n                        }\n                        promise(.success(false))\n                    } else {\n                        self.sessionManagerChannel.invokeMethod(\"onError\", arguments: [\"Unable to load renderable \\(dict_node[\"uri\"] as! String)\"])\n                        promise(.success(false))\n                    }\n                    break\n                case 1: // GLB Model from the web\n                    // Add object to scene\n                    self.modelBuilder.makeNodeFromWebGlb(name: dict_node[\"name\"] as! String, modelURL: dict_node[\"uri\"] as! String, transformation: dict_node[\"transformation\"] as? Array<NSNumber>)\n                    .sink(receiveCompletion: {\n                                    completion in print(\"Async Model Downloading Task completed: \", completion)\n                    }, receiveValue: { val in\n                        if let node: SCNNode = val {\n                            if let anchorName = dict_anchor?[\"name\"] as? String, let anchorType = dict_anchor?[\"type\"] as? Int {\n                                switch anchorType{\n                                    case 0: //PlaneAnchor\n                                        if let anchor = self.anchorCollection[anchorName]{\n                                            // Attach node to the top-level node of the specified anchor\n                                            self.sceneView.node(for: anchor)?.addChildNode(node)\n                                            promise(.success(true))\n                                        } else {\n                                            promise(.success(false))\n                                        }\n                                    default:\n                                        promise(.success(false))\n                                    }\n                                \n                            } else {\n                                // Attach to top-level node of the scene\n                                self.sceneView.scene.rootNode.addChildNode(node)\n                                promise(.success(true))\n                            }\n                            promise(.success(false))\n                        } else {\n                            self.sessionManagerChannel.invokeMethod(\"onError\", arguments: [\"Unable to load renderable \\(dict_node[\"name\"] as! String)\"])\n                            promise(.success(false))\n                        }\n                    }).store(in: &self.cancellableCollection)\n                    break\n                case 2: // GLB Model from the app's documents folder\n                    // Get path to given file system asset\n                    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)\n                    let documentsDirectory = paths[0]\n                    let targetPath = documentsDirectory.appendingPathComponent(dict_node[\"uri\"] as! String).path\n \n                    // Add object to scene\n                    if let node: SCNNode = self.modelBuilder.makeNodeFromFileSystemGLB(name: dict_node[\"name\"] as! String, modelPath: targetPath, transformation: dict_node[\"transformation\"] as? Array<NSNumber>) {\n                        if let anchorName = dict_anchor?[\"name\"] as? String, let anchorType = dict_anchor?[\"type\"] as? Int {\n                            switch anchorType{\n                                case 0: //PlaneAnchor\n                                    if let anchor = self.anchorCollection[anchorName]{\n                                        // Attach node to the top-level node of the specified anchor\n                                        self.sceneView.node(for: anchor)?.addChildNode(node)\n                                        promise(.success(true))\n                                    } else {\n                                        promise(.success(false))\n                                    }\n                                default:\n                                    promise(.success(false))\n                                }\n                            \n                        } else {\n                            // Attach to top-level node of the scene\n                            self.sceneView.scene.rootNode.addChildNode(node)\n                            promise(.success(true))\n                        }\n                        promise(.success(false))\n                    } else {\n                        self.sessionManagerChannel.invokeMethod(\"onError\", arguments: [\"Unable to load renderable \\(dict_node[\"uri\"] as! String)\"])\n                        promise(.success(false))\n                    }\n                    break\n                case 3: //fileSystemAppFolderGLTF2\n                    // Get path to given file system asset\n                    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)\n                    let documentsDirectory = paths[0]\n                    let targetPath = documentsDirectory.appendingPathComponent(dict_node[\"uri\"] as! String).path\n\n                    // Add object to scene\n                    if let node: SCNNode = self.modelBuilder.makeNodeFromFileSystemGltf(name: dict_node[\"name\"] as! String, modelPath: targetPath, transformation: dict_node[\"transformation\"] as? Array<NSNumber>) {\n                        if let anchorName = dict_anchor?[\"name\"] as? String, let anchorType = dict_anchor?[\"type\"] as? Int {\n                            switch anchorType{\n                                case 0: //PlaneAnchor\n                                    if let anchor = self.anchorCollection[anchorName]{\n                                        // Attach node to the top-level node of the specified anchor\n                                        self.sceneView.node(for: anchor)?.addChildNode(node)\n                                        promise(.success(true))\n                                    } else {\n                                        promise(.success(false))\n                                    }\n                                default:\n                                    promise(.success(false))\n                                }\n                            \n                        } else {\n                            // Attach to top-level node of the scene\n                            self.sceneView.scene.rootNode.addChildNode(node)\n                            promise(.success(true))\n                        }\n                        promise(.success(false))\n                    } else {\n                        self.sessionManagerChannel.invokeMethod(\"onError\", arguments: [\"Unable to load renderable \\(dict_node[\"uri\"] as! String)\"])\n                        promise(.success(false))\n                    }\n                    break\n                default:\n                    promise(.success(false))\n            }\n            \n        }\n    }\n    \n    func transformNode(name: String, transform: Array<NSNumber>) {\n        let node = sceneView.scene.rootNode.childNode(withName: name, recursively: true)\n        node?.transform = deserializeMatrix4(transform)\n    }\n    \n    @objc func handleTap(_ recognizer: UITapGestureRecognizer) {\n        guard let sceneView = recognizer.view as? ARSCNView else {\n            return\n        }\n        let touchLocation = recognizer.location(in: sceneView)\n    \n        let allHitResults = sceneView.hitTest(touchLocation, options: [SCNHitTestOption.searchMode : SCNHitTestSearchMode.closest.rawValue])\n        // Because 3D model loading can lead to composed nodes, we have to traverse through a node's parent until the parent node with the name assigned by the Flutter API is found\n        let nodeHitResults: Array<String> = allHitResults.compactMap { nearestParentWithNameStart(node: $0.node, characters: \"[#\")?.name }\n        if (nodeHitResults.count != 0) {\n            self.objectManagerChannel.invokeMethod(\"onNodeTap\", arguments: Array(Set(nodeHitResults))) // Chaining of Array and Set is used to remove duplicates\n            return\n        }\n            \n        let planeTypes: ARHitTestResult.ResultType\n        if #available(iOS 11.3, *){\n            planeTypes = ARHitTestResult.ResultType([.existingPlaneUsingGeometry, .featurePoint])\n        }else {\n            planeTypes = ARHitTestResult.ResultType([.existingPlaneUsingExtent, .featurePoint])\n        }\n        \n        let planeAndPointHitResults = sceneView.hitTest(touchLocation, types: planeTypes)\n        \n        // store the alignment of the tapped plane anchor so we can refer to is later when transforming the node\n        if planeAndPointHitResults.count > 0, let hitAnchor = planeAndPointHitResults.first?.anchor as? ARPlaneAnchor {\n            self.tappedPlaneAnchorAlignment = hitAnchor.alignment\n        }\n            \n        let serializedPlaneAndPointHitResults = planeAndPointHitResults.map{serializeHitResult($0)}\n        if (serializedPlaneAndPointHitResults.count != 0) {\n            self.sessionManagerChannel.invokeMethod(\"onPlaneOrPointTap\", arguments: serializedPlaneAndPointHitResults)\n        }\n    }\n\n    @objc func handlePan(_ recognizer: UIPanGestureRecognizer) {\n        guard let sceneView = recognizer.view as? ARSCNView else {\n            return\n        }\n\n        // State Begins\n        if recognizer.state == UIGestureRecognizer.State.began\n        {\n            panStartLocation = recognizer.location(in: sceneView)\n            if let startLocation = panStartLocation {\n                let allHitResults = sceneView.hitTest(startLocation, options: [SCNHitTestOption.searchMode : SCNHitTestSearchMode.closest.rawValue])\n                // Because 3D model loading can lead to composed nodes, we have to traverse through a node's parent until the parent node with the name assigned by the Flutter API is found\n                let nodeHitResults: Array<String> = allHitResults.compactMap {\n                    if let nearestNode = nearestParentWithNameStart(node: $0.node, characters: \"[#\") {\n                        panningNode = nearestNode\n                        return nearestNode.name\n                    }else{\n                        return nil\n                    }\n                }\n                if (nodeHitResults.count != 0 && panningNode != nil) {\n                    panningNodeCurrentWorldLocation = panningNode!.worldPosition\n                    self.objectManagerChannel.invokeMethod(\"onPanStart\", arguments: panningNode!.name) // Chaining of Array and Set is used to remove duplicates\n                    return\n                }\n            }\n        }\n        // State Changes\n        if(recognizer.state == UIGestureRecognizer.State.changed)\n        {\n            // the velocity of the gesture is how fast it is moving. This can be used to translate the position of the node.\n            panCurrentVelocity = recognizer.velocity(in: sceneView)\n            panCurrentLocation = recognizer.location(in: sceneView)\n            panCurrentTranslation = recognizer.translation(in: sceneView)\n\n            if let panLoc = panCurrentLocation, let panNode = panningNode {\n                if let query = sceneView.raycastQuery(from: panLoc, allowing: .estimatedPlane, alignment: .any) {\n                    guard let result = self.sceneView.session.raycast(query).first else {\n                        return\n                    }\n                    let posX = result.worldTransform.columns.3.x\n                    let posY = result.worldTransform.columns.3.y\n                    let posZ = result.worldTransform.columns.3.z\n                    panNode.worldPosition = SCNVector3(posX, posY, posZ)\n                }\n                self.objectManagerChannel.invokeMethod(\"onPanChange\", arguments: panNode.name)\n            }\n        }\n        // State Ended\n        if(recognizer.state == UIGestureRecognizer.State.ended)\n        {\n            // kill variables\n            panStartLocation = nil\n            panCurrentLocation = nil\n            self.objectManagerChannel.invokeMethod(\"onPanEnd\", arguments: serializeLocalTransformation(node: panningNode))\n            panningNode = nil\n        }\n    }\n    \n    @objc func handleRotation(_ recognizer: UIRotationGestureRecognizer) {\n        guard let sceneView = recognizer.view as? ARSCNView else {\n            return\n        }\n\n        // State Begins\n        if recognizer.state == UIGestureRecognizer.State.began\n        {\n            rotationStartLocation = recognizer.location(in: sceneView)\n            if let startLocation = rotationStartLocation {\n                let allHitResults = sceneView.hitTest(startLocation, options: [SCNHitTestOption.searchMode : SCNHitTestSearchMode.closest.rawValue])\n                // Because 3D model loading can lead to composed nodes, we have to traverse through a node's parent until the parent node with the name assigned by the Flutter API is found\n                let nodeHitResults: Array<String> = allHitResults.compactMap {\n                    if let nearestNode = nearestParentWithNameStart(node: $0.node, characters: \"[#\") {\n                        panningNode = nearestNode\n                        return nearestNode.name\n                    }else{\n                        return nil\n                    }\n                }\n                if (nodeHitResults.count != 0 && panningNode != nil) {\n                    self.objectManagerChannel.invokeMethod(\"onRotationStart\", arguments: panningNode!.name) // Chaining of Array and Set is used to remove duplicates\n                    return\n                }\n            }\n        }\n        // State Changes\n        if(recognizer.state == UIGestureRecognizer.State.changed)\n        {\n            // the velocity of the gesture is how fast it is moving. This can be used to translate the position of the node.\n            rotation = recognizer.rotation\n            rotationVelocity = recognizer.velocity\n\n            if let r = rotationVelocity, let panNode = panningNode {\n                // velocity needs to be reduced substantially otherwise the rotation change seems too fast as radians; also needs inverting to match the movement of the fingers as they rotate on the screen\n                let r2 = (r*0.01) * -1\n                let nodeRotation = panNode.rotation\n                let rotation: SCNQuaternion!\n                let planeAlignment = self.tappedPlaneAnchorAlignment\n                if planeAlignment == .horizontal {\n                    rotation = SCNQuaternion(x: 0, y: 1, z: 0, w: nodeRotation.w+Float(r2)) // quickest way to convert screen into world positions (meters)\n                }else{\n                    rotation = SCNQuaternion(x: 0, y: 0, z: 1, w: nodeRotation.w+Float(r2)) // quickest way to convert screen into world positions (meters)\n                }\n                panNode.rotation = rotation\n                self.objectManagerChannel.invokeMethod(\"onRotationChange\", arguments: panNode.name)\n            }\n\n            // update position of panning node if it has been created\n            // panningNode.position + the gesture delta\n        }\n        // State Ended\n        if(recognizer.state == UIGestureRecognizer.State.ended)\n        {\n            // kill variables\n            rotation = nil\n            rotationVelocity = nil\n            self.objectManagerChannel.invokeMethod(\"onRotationEnd\", arguments: serializeLocalTransformation(node: panningNode))\n            panningNode = nil\n        }\n    \n    }\n\n    // Recursive helper function to traverse a node's parents until a node with a name starting with the specified characters is found\n    func nearestParentWithNameStart(node: SCNNode?, characters: String) -> SCNNode? {\n        if let nodeNamePrefix = node?.name?.prefix(characters.count) {\n            if (nodeNamePrefix == characters) { return node }\n        }\n        if let parent = node?.parent { return nearestParentWithNameStart(node: parent, characters: characters) }\n        return nil\n    }\n    \n    func addPlaneAnchor(transform: Array<NSNumber>, name: String){\n        let arAnchor = ARAnchor(transform: simd_float4x4(deserializeMatrix4(transform)))\n        anchorCollection[name] = arAnchor\n        sceneView.session.add(anchor: arAnchor)\n        // Ensure root node is added to anchor before any other function can run (if this isn't done, addNode could fail because anchor does not have a root node yet).\n        // The root node is added to the anchor as soon as the async rendering loop runs once, more specifically the function \"renderer(_:nodeFor:)\"\n        while (sceneView.node(for: arAnchor) == nil) {\n            usleep(1) // wait 1 millionth of a second\n        }\n    }\n    \n    func deleteAnchor(anchorName: String) {\n        if let anchor = anchorCollection[anchorName]{\n            // Delete all child nodes\n            if var attachedNodes = sceneView.node(for: anchor)?.childNodes {\n                attachedNodes.removeAll()\n            }\n            // Remove anchor\n            sceneView.session.remove(anchor: anchor)\n            // Update bookkeeping\n            anchorCollection.removeValue(forKey: anchorName)\n        }\n    }\n    \n    private class cloudAnchorUploadedListener: CloudAnchorListener {\n        private var parent: IosARView\n        \n        init(parent: IosARView) {\n            self.parent = parent\n        }\n        \n        func onCloudTaskComplete(anchorName: String?, anchor: GARAnchor?) {\n            if let cloudState = anchor?.cloudState {\n                if (cloudState == GARCloudAnchorState.success) {\n                    var args = Dictionary<String, String?>()\n                    args[\"name\"] = anchorName\n                    args[\"cloudanchorid\"] = anchor?.cloudIdentifier\n                    parent.anchorManagerChannel.invokeMethod(\"onCloudAnchorUploaded\", arguments: args)\n                } else {\n                    print(\"Error uploading anchor, state: \\(parent.decodeCloudAnchorState(state: cloudState))\")\n                    parent.sessionManagerChannel.invokeMethod(\"onError\", arguments: [\"Error uploading anchor, state: \\(parent.decodeCloudAnchorState(state: cloudState))\"])\n                    return\n                }\n            }\n        }\n    }\n\n    private class cloudAnchorDownloadedListener: CloudAnchorListener {\n        private var parent: IosARView\n        \n        init(parent: IosARView) {\n            self.parent = parent\n        }\n        \n        func onCloudTaskComplete(anchorName: String?, anchor: GARAnchor?) {\n            if let cloudState = anchor?.cloudState {\n                if (cloudState == GARCloudAnchorState.success) {\n                    let newAnchor = ARAnchor(transform: anchor!.transform)\n                    // Register new anchor on the Flutter side of the plugin\n                    parent.anchorManagerChannel.invokeMethod(\"onAnchorDownloadSuccess\", arguments: serializeAnchor(anchor: newAnchor, anchorNode: nil, ganchor: anchor!, name: anchorName), result: { result in\n                        if let anchorName = result as? String {\n                            self.parent.sceneView.session.add(anchor: newAnchor)\n                            self.parent.anchorCollection[anchorName] = newAnchor\n                        } else {\n                            self.parent.sessionManagerChannel.invokeMethod(\"onError\", arguments: [\"Error while registering downloaded anchor at the AR Flutter plugin\"])\n                        }\n\n                    })\n                } else {\n                    print(\"Error downloading anchor, state \\(cloudState)\")\n                    parent.sessionManagerChannel.invokeMethod(\"onError\", arguments: [\"Error downloading anchor, state \\(cloudState)\"])\n                    return\n                }\n            }\n        }\n    }\n    \n    func decodeCloudAnchorState(state: GARCloudAnchorState) -> String {\n        switch state {\n        case .errorCloudIdNotFound:\n            return \"Cloud anchor id not found\"\n        case .errorHostingDatasetProcessingFailed:\n            return \"Dataset processing failed, feature map insufficient\"\n        case .errorHostingServiceUnavailable:\n            return \"Hosting service unavailable\"\n        case .errorInternal:\n            return \"Internal error\"\n        case .errorNotAuthorized:\n            return \"Authentication failed: Not Authorized\"\n        case .errorResolvingSdkVersionTooNew:\n            return \"Resolving Sdk version too new\"\n        case .errorResolvingSdkVersionTooOld:\n            return \"Resolving Sdk version too old\"\n        case .errorResourceExhausted:\n            return \" Resource exhausted\"\n        case .none:\n            return \"Empty state\"\n        case .taskInProgress:\n            return \"Task in progress\"\n        case .success:\n            return \"Success\"\n        case .errorServiceUnavailable:\n            return \"Cloud Anchor Service unavailable\"\n        case .errorResolvingLocalizationNoMatch:\n            return \"No match\"\n        @unknown default:\n            return \"Unknown\"\n        }\n    }\n}\n\n// ---------------------- ARCoachingOverlayViewDelegate ---------------------------------------\n\nextension IosARView: ARCoachingOverlayViewDelegate {\n    \n    func coachingOverlayViewWillActivate(_ coachingOverlayView: ARCoachingOverlayView){\n        // use this delegate method to hide anything in the UI that could cover the coaching overlay view\n    }\n    \n    func coachingOverlayViewDidRequestSessionReset(_ coachingOverlayView: ARCoachingOverlayView) {\n        // Reset the session.\n        self.sceneView.session.run(configuration, options: [.resetTracking])\n    }\n}\n"
  },
  {
    "path": "ios/Classes/IosARViewFactory.swift",
    "content": "import Flutter\nimport UIKit\n\nclass IosARViewFactory: NSObject, FlutterPlatformViewFactory {\n    private var messenger: FlutterBinaryMessenger\n\n    init(messenger: FlutterBinaryMessenger) {\n        self.messenger = messenger\n        super.init()\n    }\n\n    func create(\n        withFrame frame: CGRect,\n        viewIdentifier viewId: Int64,\n        arguments args: Any?\n    ) -> FlutterPlatformView {\n        return IosARView(\n            frame: frame,\n            viewIdentifier: viewId,\n            arguments: args,\n            binaryMessenger: messenger)\n    }\n}\n"
  },
  {
    "path": "ios/Classes/JWTGenerator.swift",
    "content": "//\n//  JWTGenerator.swift\n//  ar_flutter_plugin\n//\n//  Created by Lars Carius on 08.04.21.\n//\n\nimport Foundation\nimport SwiftJWT\n\nclass JWTGenerator {\n    \n    func generateWebToken() -> String? {\n        if let path = Bundle.main.path(forResource: \"cloudAnchorKey\", ofType: \"json\") {\n            do {\n              let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)\n              let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)\n              if let jsonResult = jsonResult as? Dictionary<String, AnyObject>,\n                 let type = jsonResult[\"type\"] as? String,\n                 let projectId = jsonResult[\"project_id\"] as? String,\n                 let privateKeyId = jsonResult[\"private_key_id\"] as? String,\n                 let privateKey = jsonResult[\"private_key\"] as? String,\n                 let clientEmail = jsonResult[\"client_email\"] as? String,\n                 let clientId = jsonResult[\"client_id\"] as? String,\n                 let authUri = jsonResult[\"auth_uri\"] as? String,\n                 let tokenUri = jsonResult[\"token_uri\"] as? String,\n                 let authProviderX509CertUrl = jsonResult[\"auth_provider_x509_cert_url\"] as? String,\n                 let clientX509CertUrl = jsonResult[\"client_x509_cert_url\"] as? String{\n                \n                    let jwtTokenHeader = Header(typ: type, jku: tokenUri, kid: privateKeyId, x5u: authProviderX509CertUrl, x5c: [clientX509CertUrl])\n                    \n                    struct JWTTokenClaims: Claims {\n                        let iss: String\n                        let sub: String\n                        let iat: Date\n                        let exp: Date\n                        let aud: String\n                    }\n                    let jwtTokenClaims = JWTTokenClaims(iss: clientEmail, sub: clientEmail, iat: Date(), exp: Date(timeIntervalSinceNow: 3600), aud: \"https://arcorecloudanchor.googleapis.com/\")\n                  \n                    var jwtToken = JWT(header: jwtTokenHeader, claims: jwtTokenClaims)\n                    \n                    // Sign Token\n                    let jwtSigner = JWTSigner.rs256(privateKey: privateKey.data(using: String.Encoding.ascii)!)\n                    \n                    let signedJwtToken = try jwtToken.sign(using: jwtSigner)\n                    \n                    return signedJwtToken\n                  \n                  }\n              } catch {\n                   print(\"Error generating JWT\")\n              }\n        }\n        return nil\n    }\n    \n}\n"
  },
  {
    "path": "ios/Classes/Serialization/Deserializers.swift",
    "content": "// The code in this file is adapted from Oleksandr Leuschenko' ARKit Flutter Plugin (https://github.com/olexale/arkit_flutter_plugin)\n\nimport ARKit\n\nfunc deserializeVector3(_ coords: Array<Double>) -> SCNVector3 {\n    let point = SCNVector3(coords[0], coords[1], coords[2])\n    return point\n}\n\nfunc deserializeVector4(_ coords: Array<Double>) -> SCNVector4 {\n    let point = SCNVector4(coords[0], coords[1], coords[2], coords[3])\n    return point\n}\n\nfunc deserializeMatrix4(_ c: Array<NSNumber>) -> SCNMatrix4 {\n    let coords = c.map({ Float(truncating: $0 )})\n    let matrix = SCNMatrix4(m11: coords[0], m12: coords[1],m13: coords[2], m14: coords[3], m21: coords[4], m22: coords[5], m23: coords[6], m24: coords[7], m31: coords[8], m32: coords[9], m33: coords[10], m34: coords[11], m41: coords[12], m42: coords[13], m43: coords[14], m44: coords[15])\n    return matrix\n}\n"
  },
  {
    "path": "ios/Classes/Serialization/Serializers.swift",
    "content": "import Foundation\nimport ARKit\nimport ARCoreCloudAnchors\n\nfunc serializeHitResult(_ result: ARHitTestResult) -> Dictionary<String, Any> {\n    \n    var hitResult = Dictionary<String, Any>(minimumCapacity: 3)\n    if (result.type == .existingPlaneUsingExtent || result.type == .existingPlaneUsingGeometry || result.type == .existingPlane) {\n        hitResult[\"type\"] = 1 // Type plane\n    } else if (result.type == .featurePoint) {\n        hitResult[\"type\"] = 2 // Type point\n    } else {\n        hitResult[\"type\"] = 0 // Type undefined\n    }\n    hitResult[\"distance\"] = result.distance\n    hitResult[\"worldTransform\"] = serializeMatrix(result.worldTransform)\n    return hitResult\n}\n\n// The following code is adapted from Oleksandr Leuschenko' ARKit Flutter Plugin (https://github.com/olexale/arkit_flutter_plugin)\n\nfunc serializeMatrix(_ matrix: simd_float4x4) -> Array<Float> {\n    return [matrix.columns.0, matrix.columns.1, matrix.columns.2, matrix.columns.3].flatMap { serializeArray($0) }\n}\n\nfunc serializeArray(_ array: simd_float4) -> Array<Float> {\n    return [array[0], array[1], array[2], array[3]]\n}\n\nfunc serializeAnchor(anchor: ARAnchor, anchorNode: SCNNode?, ganchor: GARAnchor, name: String?) -> Dictionary<String, Any?> {\n    var serializedAnchor = Dictionary<String, Any?>()\n    \n    serializedAnchor[\"type\"] = 0 // index for plane anchors\n    serializedAnchor[\"name\"] = name\n    serializedAnchor[\"cloudanchorid\"] = ganchor.cloudIdentifier\n    serializedAnchor[\"transformation\"] = serializeMatrix(anchor.transform)\n    serializedAnchor[\"childNodes\"] = anchorNode?.childNodes.map{$0.name}\n\n    return serializedAnchor\n}\n\nfunc serializeLocalTransformation(node: SCNNode?) -> Dictionary<String, Any?> {\n    var serializedLocalTransformation = Dictionary<String, Any?>()\n\n    let transform: [Float?] = [node?.transform.m11, node?.transform.m12, node?.transform.m13, node?.transform.m14, node?.transform.m21, node?.transform.m22, node?.transform.m23, node?.transform.m24, node?.transform.m31, node?.transform.m32, node?.transform.m33, node?.transform.m34, node?.transform.m41, node?.transform.m42, node?.transform.m43, node?.transform.m44]\n    \n    serializedLocalTransformation[\"name\"] = node?.name\n    serializedLocalTransformation[\"transform\"] = transform\n\n    return serializedLocalTransformation\n}\n"
  },
  {
    "path": "ios/Classes/SwiftArFlutterPlugin.swift",
    "content": "import Flutter\nimport UIKit\n\npublic class SwiftArFlutterPlugin: NSObject, FlutterPlugin {\n  public static func register(with registrar: FlutterPluginRegistrar) {\n    let channel = FlutterMethodChannel(name: \"ar_flutter_plugin\", binaryMessenger: registrar.messenger())\n    let instance = SwiftArFlutterPlugin()\n    registrar.addMethodCallDelegate(instance, channel: channel)\n    \n    let factory = IosARViewFactory(messenger: registrar.messenger())\n    registrar.register(factory, withId: \"ar_flutter_plugin\")\n  }\n\n  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {\n    result(\"iOS \" + UIDevice.current.systemVersion)\n  }\n\n}\n"
  },
  {
    "path": "ios/ar_flutter_plugin.podspec",
    "content": "#\n# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.\n# Run `pod lib lint ar_flutter_plugin.podspec` to validate before publishing.\n#\nPod::Spec.new do |s|\n  s.name             = 'ar_flutter_plugin'\n  s.version          = '0.6.2'\n  s.summary          = 'A Flutter plugin for shared AR experiences.'\n  s.description      = <<-DESC\nA Flutter plugin for shared AR experiences supporting Android and iOS.\n                       DESC\n  s.homepage         = 'https://lars.carius.io'\n  s.license          = { :file => '../LICENSE' }\n  s.author           = { 'Lars Carius' => 'carius.lars@gmail.com' }\n  s.source           = { :path => '.' }\n  s.source_files = 'Classes/**/*'\n  s.dependency 'Flutter'\n  s.dependency 'GLTFSceneKit'\n  s.dependency 'SwiftJWT'\n  s.static_framework = true\n  #s.dependency 'ARCore/CloudAnchors', '~> 1.12.0'\n  #s.dependency 'ARCore', '~> 1.2.0'\n  s.dependency 'ARCore/CloudAnchors', '~> 1.33.0' # Updated from 1.32 to 1.33 to support Apple Silicon, info here: https://github.com/google-ar/arcore-ios-sdk/issues/59#issuecomment-1219756010\n  s.platform = :ios, '13.0'\n\n\n  # Flutter.framework does not contain a i386 slice.\n  s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }\n  s.swift_version = '5.0'\nend\n"
  },
  {
    "path": "lib/ar_flutter_plugin.dart",
    "content": "export 'package:ar_flutter_plugin/widgets/ar_view.dart';\n\nimport 'dart:async';\n\nimport 'package:flutter/services.dart';\n\nclass ArFlutterPlugin {\n  static const MethodChannel _channel =\n      const MethodChannel('ar_flutter_plugin');\n\n  /// Private constructor to prevent accidental instantiation of the Plugin using the implicit default constructor\n  ArFlutterPlugin._();\n\n  static Future<String> get platformVersion async {\n    final String version = await _channel.invokeMethod('getPlatformVersion');\n    return version;\n  }\n}\n"
  },
  {
    "path": "lib/datatypes/anchor_types.dart",
    "content": "/// Determines which types of anchors the plugin supports\nenum AnchorType {\n  plane,\n  //Additional anchor Types can be added here (e.g. imageAnchor, faceAnchor, bodyAnchor,etc.)\n}\n"
  },
  {
    "path": "lib/datatypes/config_planedetection.dart",
    "content": "/// Determines which types of planes ARCore and ARKit should track\nenum PlaneDetectionConfig {\n  none,\n  horizontal,\n  vertical,\n  horizontalAndVertical,\n}\n"
  },
  {
    "path": "lib/datatypes/hittest_result_types.dart",
    "content": "/// Determines which types of hit results the plugin supports\nenum ARHitTestResultType {\n  undefined,\n  plane,\n  point,\n}\n"
  },
  {
    "path": "lib/datatypes/node_types.dart",
    "content": "/// Determines which types of nodes the plugin supports\nenum NodeType {\n  localGLTF2, // Node with renderable with fileending .gltf in the Flutter asset folder\n  webGLB, // Node with renderable with fileending .glb loaded from the internet during runtime\n  fileSystemAppFolderGLB, // Node with renderable with fileending .glb in the documents folder of the current app\n  fileSystemAppFolderGLTF2, // Node with renderable with fileending .gltf in the documents folder of the current app\n}\n"
  },
  {
    "path": "lib/managers/ar_anchor_manager.dart",
    "content": "import 'package:ar_flutter_plugin/models/ar_anchor.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter/material.dart';\n\n// Type definitions to enforce a consistent use of the API\ntypedef AnchorUploadedHandler = void Function(ARAnchor arAnchor);\ntypedef AnchorDownloadedHandler = ARAnchor Function(\n    Map<String, dynamic> serializedAnchor);\n\n/// Handles all anchor-related functionality of an [ARView], including configuration and usage of collaborative sessions\nclass ARAnchorManager {\n  /// Platform channel used for communication from and to [ARAnchorManager]\n  late MethodChannel _channel;\n\n  /// Debugging status flag. If true, all platform calls are printed. Defaults to false.\n  final bool debug;\n\n  /// Reference to all anchors that are being uploaded to the google cloud anchor API\n  List<ARAnchor> pendingAnchors = [];\n\n  /// Callback that is triggered once an anchor has successfully been uploaded to the google cloud anchor API\n  AnchorUploadedHandler? onAnchorUploaded;\n\n  /// Callback that is triggered once an anchor has successfully been downloaded from the google cloud anchor API and resolved within the current scene\n  AnchorDownloadedHandler? onAnchorDownloaded;\n\n  ARAnchorManager(int id, {this.debug = false}) {\n    _channel = MethodChannel('aranchors_$id');\n    _channel.setMethodCallHandler(_platformCallHandler);\n    if (debug) {\n      print(\"ARAnchorManager initialized\");\n    }\n  }\n\n  /// Activates collaborative AR mode (using Google Cloud Anchors)\n  initGoogleCloudAnchorMode() async {\n    _channel.invokeMethod<bool>('initGoogleCloudAnchorMode', {});\n  }\n\n  Future<dynamic> _platformCallHandler(MethodCall call) async {\n    if (debug) {\n      print('_platformCallHandler call ${call.method} ${call.arguments}');\n    }\n    try {\n      switch (call.method) {\n        case 'onError':\n          print(call.arguments);\n          break;\n        case 'onCloudAnchorUploaded':\n          final name = call.arguments[\"name\"];\n          final cloudanchorid = call.arguments[\"cloudanchorid\"];\n          print(\n              \"UPLOADED ANCHOR WITH ID: \" + cloudanchorid + \", NAME: \" + name);\n          final currentAnchor =\n              pendingAnchors.where((element) => element.name == name).first;\n          // Update anchor with cloud anchor ID\n          (currentAnchor as ARPlaneAnchor).cloudanchorid = cloudanchorid;\n          // Remove anchor from list of pending anchors\n          pendingAnchors.remove(currentAnchor);\n          // Notify callback\n          if (onAnchorUploaded != null) {\n            onAnchorUploaded!(currentAnchor);\n          }\n          break;\n        case \"onAnchorDownloadSuccess\":\n          final serializedAnchor = call.arguments;\n          if (onAnchorDownloaded != null) {\n            ARAnchor anchor = onAnchorDownloaded!(\n                Map<String, dynamic>.from(serializedAnchor));\n            return anchor.name;\n          } else {\n            return serializedAnchor[\"name\"];\n          }\n        default:\n          if (debug) {\n            print('Unimplemented method ${call.method} ');\n          }\n      }\n    } catch (e) {\n      print('Error caught: ' + e.toString());\n    }\n    return Future.value();\n  }\n\n  /// Add given anchor to the underlying AR scene\n  Future<bool?> addAnchor(ARAnchor anchor) async {\n    try {\n      return await _channel.invokeMethod<bool>('addAnchor', anchor.toJson());\n    } on PlatformException catch (e) {\n      return false;\n    }\n  }\n\n  /// Remove given anchor and all its children from the AR Scene\n  removeAnchor(ARAnchor anchor) {\n    _channel.invokeMethod<String>('removeAnchor', {'name': anchor.name});\n  }\n\n  /// Upload given anchor from the underlying AR scene to the Google Cloud Anchor API\n  Future<bool?> uploadAnchor(ARAnchor anchor) async {\n    try {\n      final response =\n          await _channel.invokeMethod<bool>('uploadAnchor', anchor.toJson());\n      pendingAnchors.add(anchor);\n      return response;\n    } on PlatformException catch (e) {\n      return false;\n    }\n  }\n\n  /// Try to download anchor with the given ID from the Google Cloud Anchor API and add it to the scene\n  Future<bool?> downloadAnchor(String cloudanchorid) async {\n    print(\"TRYING TO DOWNLOAD ANCHOR WITH ID \" + cloudanchorid);\n    _channel\n        .invokeMethod<bool>('downloadAnchor', {\"cloudanchorid\": cloudanchorid});\n  }\n}\n"
  },
  {
    "path": "lib/managers/ar_location_manager.dart",
    "content": "import 'dart:async';\nimport 'package:geolocator/geolocator.dart';\n\n/// Can be used to get the current location of the device, update it and handle location permissions\nclass ARLocationManager {\n  late Position currentLocation;\n  late StreamSubscription<Position> locationStream;\n\n  /// Returns the last known location of the device or an error, if permissions don't suffice. Automatically queries user permission if possible\n  Future<Position?> getLastKnownPosition() async {\n    bool serviceEnabled;\n    LocationPermission permission;\n\n    // Test if location services are enabled.\n    serviceEnabled = await Geolocator.isLocationServiceEnabled();\n    if (!serviceEnabled) {\n      // Location services are not enabled don't continue\n      // accessing the position and request users of the\n      // App to enable the location services.\n      return Future.error('Location services disabled');\n    }\n\n    permission = await Geolocator.checkPermission();\n    if (permission == LocationPermission.denied) {\n      permission = await Geolocator.requestPermission();\n      if (permission == LocationPermission.denied) {\n        // Permissions are denied, next time you could try\n        // requesting permissions again (this is also where\n        // Android's shouldShowRequestPermissionRationale\n        // returned true. According to Android guidelines\n        // your App should show an explanatory UI now.\n        return Future.error('Location permissions denied');\n      }\n    }\n\n    if (permission == LocationPermission.deniedForever) {\n      // Permissions are denied forever, handle appropriately.\n      return Future.error('Location permissions permanently denied');\n    }\n\n    // When we reach here, permissions are granted and we can\n    // continue accessing the last known position of the device.\n    return await Geolocator.getLastKnownPosition();\n  }\n\n  /// Starts high precision location updates to keep track of the device's position. Returns true or an error, if permissions don't suffice. Automatically queries user permission if possible\n  Future<bool> startLocationUpdates() async {\n    bool serviceEnabled;\n    LocationPermission permission;\n\n    // Test if location services are enabled.\n    serviceEnabled = await Geolocator.isLocationServiceEnabled();\n    if (!serviceEnabled) {\n      // Location services are not enabled don't continue\n      // accessing the position and request users of the\n      // App to enable the location services.\n      return Future.error('Location services disabled');\n    }\n\n    permission = await Geolocator.checkPermission();\n    if (permission == LocationPermission.denied) {\n      permission = await Geolocator.requestPermission();\n      if (permission == LocationPermission.denied) {\n        // Permissions are denied, next time you could try\n        // requesting permissions again (this is also where\n        // Android's shouldShowRequestPermissionRationale\n        // returned true. According to Android guidelines\n        // your App should show an explanatory UI now.\n        return Future.error('Location permissions denied');\n      }\n    }\n\n    if (permission == LocationPermission.deniedForever) {\n      // Permissions are denied forever, handle appropriately.\n      return Future.error('Location permissions permanently denied');\n    }\n\n    // When we reach here, permissions are granted and we can\n    // continue accessing the position of the device.\n    locationStream =\n        Geolocator.getPositionStream(locationSettings: LocationSettings(accuracy: LocationAccuracy.high))\n            .listen((Position position) {\n      //print(position.latitude.toString() + ', ' + position.longitude.toString());\n      currentLocation = position;\n    });\n\n    return true;\n  }\n\n  /// Stops the high-precision location updates\n  void stopLocationUpdates() {\n    locationStream.cancel();\n  }\n\n  /// Opens the settings of the current application\n  void openAppPermissionSettings() async {\n    await Geolocator.openAppSettings();\n  }\n\n  /// Opens the device settings where location services can be enabled\n  void openLocationServicesSettings() async {\n    await Geolocator.openLocationSettings();\n  }\n\n  /// Determines the current position of the device.\n  ///\n  /// When the location services are not enabled or permissions\n  /// are denied the `Future` will return an error.\n  Future<Position> _determinePosition() async {\n    bool serviceEnabled;\n    LocationPermission permission;\n\n    // Test if location services are enabled.\n    serviceEnabled = await Geolocator.isLocationServiceEnabled();\n    if (!serviceEnabled) {\n      // Location services are not enabled don't continue\n      // accessing the position and request users of the\n      // App to enable the location services.\n      return Future.error('Location services disabled');\n    }\n\n    permission = await Geolocator.checkPermission();\n    if (permission == LocationPermission.denied) {\n      permission = await Geolocator.requestPermission();\n      if (permission == LocationPermission.denied) {\n        // Permissions are denied, next time you could try\n        // requesting permissions again (this is also where\n        // Android's shouldShowRequestPermissionRationale\n        // returned true. According to Android guidelines\n        // your App should show an explanatory UI now.\n        return Future.error('Location permissions denied');\n      }\n    }\n\n    if (permission == LocationPermission.deniedForever) {\n      // Permissions are denied forever, handle appropriately.\n      return Future.error('Location permissions permanently denied');\n    }\n\n    // When we reach here, permissions are granted and we can\n    // continue accessing the position of the device.\n    return await Geolocator.getCurrentPosition();\n  }\n}\n"
  },
  {
    "path": "lib/managers/ar_object_manager.dart",
    "content": "import 'dart:typed_data';\n\nimport 'package:ar_flutter_plugin/models/ar_anchor.dart';\nimport 'package:ar_flutter_plugin/models/ar_node.dart';\nimport 'package:ar_flutter_plugin/utils/json_converters.dart';\nimport 'package:flutter/services.dart';\nimport 'package:vector_math/vector_math_64.dart';\n\n// Type definitions to enforce a consistent use of the API\ntypedef NodeTapResultHandler = void Function(List<String> nodes);\ntypedef NodePanStartHandler = void Function(String node);\ntypedef NodePanChangeHandler = void Function(String node);\ntypedef NodePanEndHandler = void Function(String node, Matrix4 transform);\ntypedef NodeRotationStartHandler = void Function(String node);\ntypedef NodeRotationChangeHandler = void Function(String node);\ntypedef NodeRotationEndHandler = void Function(String node, Matrix4 transform);\n\n/// Manages the all node-related actions of an [ARView]\nclass ARObjectManager {\n  /// Platform channel used for communication from and to [ARObjectManager]\n  late MethodChannel _channel;\n\n  /// Debugging status flag. If true, all platform calls are printed. Defaults to false.\n  final bool debug;\n\n  /// Callback function that is invoked when the platform detects a tap on a node\n  NodeTapResultHandler? onNodeTap;\n  NodePanStartHandler? onPanStart;\n  NodePanChangeHandler? onPanChange;\n  NodePanEndHandler? onPanEnd;\n  NodeRotationStartHandler? onRotationStart;\n  NodeRotationChangeHandler? onRotationChange;\n  NodeRotationEndHandler? onRotationEnd;\n\n  ARObjectManager(int id, {this.debug = false}) {\n    _channel = MethodChannel('arobjects_$id');\n    _channel.setMethodCallHandler(_platformCallHandler);\n    if (debug) {\n      print(\"ARObjectManager initialized\");\n    }\n  }\n\n  Future<void> _platformCallHandler(MethodCall call) {\n    if (debug) {\n      print('_platformCallHandler call ${call.method} ${call.arguments}');\n    }\n    try {\n      switch (call.method) {\n        case 'onError':\n          print(call.arguments);\n          break;\n        case 'onNodeTap':\n          if (onNodeTap != null) {\n            final tappedNodes = call.arguments as List<dynamic>;\n            onNodeTap!(tappedNodes\n                .map((tappedNode) => tappedNode.toString())\n                .toList());\n          }\n          break;\n        case 'onPanStart':\n          if (onPanStart != null) {\n            final tappedNode = call.arguments as String;\n            // Notify callback\n            onPanStart!(tappedNode);\n          }\n          break;\n        case 'onPanChange':\n          if (onPanChange != null) {\n            final tappedNode = call.arguments as String;\n            // Notify callback\n            onPanChange!(tappedNode);\n          }\n          break;\n        case 'onPanEnd':\n          if (onPanEnd != null) {\n            final tappedNodeName = call.arguments[\"name\"] as String;\n            final transform =\n                MatrixConverter().fromJson(call.arguments['transform'] as List);\n\n            // Notify callback\n            onPanEnd!(tappedNodeName, transform);\n          }\n          break;\n        case 'onRotationStart':\n          if (onRotationStart != null) {\n            final tappedNode = call.arguments as String;\n            onRotationStart!(tappedNode);\n          }\n          break;\n        case 'onRotationChange':\n          if (onRotationChange != null) {\n            final tappedNode = call.arguments as String;\n            onRotationChange!(tappedNode);\n          }\n          break;\n        case 'onRotationEnd':\n          if (onRotationEnd != null) {\n            final tappedNodeName = call.arguments[\"name\"] as String;\n            final transform =\n                MatrixConverter().fromJson(call.arguments['transform'] as List);\n\n            // Notify callback\n            onRotationEnd!(tappedNodeName, transform);\n          }\n          break;\n        default:\n          if (debug) {\n            print('Unimplemented method ${call.method} ');\n          }\n      }\n    } catch (e) {\n      print('Error caught: ' + e.toString());\n    }\n    return Future.value();\n  }\n\n  /// Sets up the AR Object Manager\n  onInitialize() {\n    _channel.invokeMethod<void>('init', {});\n  }\n\n  /// Add given node to the given anchor of the underlying AR scene (or to its top-level if no anchor is given) and listen to any changes made to its transformation\n  Future<bool?> addNode(ARNode node, {ARPlaneAnchor? planeAnchor}) async {\n    try {\n      node.transformNotifier.addListener(() {\n        _channel.invokeMethod<void>('transformationChanged', {\n          'name': node.name,\n          'transformation':\n              MatrixValueNotifierConverter().toJson(node.transformNotifier)\n        });\n      });\n      if (planeAnchor != null) {\n        planeAnchor.childNodes.add(node.name);\n        return await _channel.invokeMethod<bool>('addNodeToPlaneAnchor',\n            {'node': node.toMap(), 'anchor': planeAnchor.toJson()});\n      } else {\n        return await _channel.invokeMethod<bool>('addNode', node.toMap());\n      }\n    } on PlatformException catch (e) {\n      return false;\n    }\n  }\n\n  /// Remove given node from the AR Scene\n  removeNode(ARNode node) {\n    _channel.invokeMethod<String>('removeNode', {'name': node.name});\n  }\n}\n"
  },
  {
    "path": "lib/managers/ar_session_manager.dart",
    "content": "import 'dart:math' show sqrt;\nimport 'dart:typed_data';\n\nimport 'package:ar_flutter_plugin/datatypes/config_planedetection.dart';\nimport 'package:ar_flutter_plugin/models/ar_anchor.dart';\nimport 'package:ar_flutter_plugin/models/ar_hittest_result.dart';\nimport 'package:ar_flutter_plugin/utils/json_converters.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:vector_math/vector_math_64.dart';\n\n// Type definitions to enforce a consistent use of the API\ntypedef ARHitResultHandler = void Function(List<ARHitTestResult> hits);\n\n/// Manages the session configuration, parameters and events of an [ARView]\nclass ARSessionManager {\n  /// Platform channel used for communication from and to [ARSessionManager]\n  late MethodChannel _channel;\n\n  /// Debugging status flag. If true, all platform calls are printed. Defaults to false.\n  final bool debug;\n\n  /// Context of the [ARView] widget that this manager is attributed to\n  final BuildContext buildContext;\n\n  /// Determines the types of planes ARCore and ARKit should show\n  final PlaneDetectionConfig planeDetectionConfig;\n\n  /// Receives hit results from user taps with tracked planes or feature points\n  late ARHitResultHandler onPlaneOrPointTap;\n\n  ARSessionManager(int id, this.buildContext, this.planeDetectionConfig,\n      {this.debug = false}) {\n    _channel = MethodChannel('arsession_$id');\n    _channel.setMethodCallHandler(_platformCallHandler);\n    if (debug) {\n      print(\"ARSessionManager initialized\");\n    }\n  }\n\n  /// Returns the camera pose in Matrix4 format with respect to the world coordinate system of the [ARView]\n  Future<Matrix4?> getCameraPose() async {\n    try {\n      final serializedCameraPose =\n          await _channel.invokeMethod<List<dynamic>>('getCameraPose', {});\n      return MatrixConverter().fromJson(serializedCameraPose!);\n    } catch (e) {\n      print('Error caught: ' + e.toString());\n      return null;\n    }\n  }\n\n  /// Returns the given anchor pose in Matrix4 format with respect to the world coordinate system of the [ARView]\n  Future<Matrix4?> getPose(ARAnchor anchor) async {\n    try {\n      if (anchor.name.isEmpty) {\n        throw Exception(\"Anchor can not be resolved. Anchor name is empty.\");\n      }\n      final serializedCameraPose =\n          await _channel.invokeMethod<List<dynamic>>('getAnchorPose', {\n        \"anchorId\": anchor.name,\n      });\n      return MatrixConverter().fromJson(serializedCameraPose!);\n    } catch (e) {\n      print('Error caught: ' + e.toString());\n      return null;\n    }\n  }\n\n  /// Returns the distance in meters between @anchor1 and @anchor2.\n  Future<double?> getDistanceBetweenAnchors(\n      ARAnchor anchor1, ARAnchor anchor2) async {\n    var anchor1Pose = await getPose(anchor1);\n    var anchor2Pose = await getPose(anchor2);\n    var anchor1Translation = anchor1Pose?.getTranslation();\n    var anchor2Translation = anchor2Pose?.getTranslation();\n    if (anchor1Translation != null && anchor2Translation != null) {\n      return getDistanceBetweenVectors(anchor1Translation, anchor2Translation);\n    } else {\n      return null;\n    }\n  }\n\n  /// Returns the distance in meters between @anchor and device's camera.\n  Future<double?> getDistanceFromAnchor(ARAnchor anchor) async {\n    Matrix4? cameraPose = await getCameraPose();\n    Matrix4? anchorPose = await getPose(anchor);\n    Vector3? cameraTranslation = cameraPose?.getTranslation();\n    Vector3? anchorTranslation = anchorPose?.getTranslation();\n    if (anchorTranslation != null && cameraTranslation != null) {\n      return getDistanceBetweenVectors(anchorTranslation, cameraTranslation);\n    } else {\n      return null;\n    }\n  }\n\n  /// Returns the distance in meters between @vector1 and @vector2.\n  double getDistanceBetweenVectors(Vector3 vector1, Vector3 vector2) {\n    num dx = vector1.x - vector2.x;\n    num dy = vector1.y - vector2.y;\n    num dz = vector1.z - vector2.z;\n    double distance = sqrt(dx * dx + dy * dy + dz * dz);\n    return distance;\n  }\n\n  Future<void> _platformCallHandler(MethodCall call) {\n    if (debug) {\n      print('_platformCallHandler call ${call.method} ${call.arguments}');\n    }\n    try {\n      switch (call.method) {\n        case 'onError':\n          if (onError != null) {\n            onError(call.arguments[0]);\n            print(call.arguments);\n          }\n          break;\n        case 'onPlaneOrPointTap':\n          if (onPlaneOrPointTap != null) {\n            final rawHitTestResults = call.arguments as List<dynamic>;\n            final serializedHitTestResults = rawHitTestResults\n                .map(\n                    (hitTestResult) => Map<String, dynamic>.from(hitTestResult))\n                .toList();\n            final hitTestResults = serializedHitTestResults.map((e) {\n              return ARHitTestResult.fromJson(e);\n            }).toList();\n            onPlaneOrPointTap(hitTestResults);\n          }\n          break;\n        case 'dispose':\n          _channel.invokeMethod<void>(\"dispose\");\n          break;\n        default:\n          if (debug) {\n            print('Unimplemented method ${call.method} ');\n          }\n      }\n    } catch (e) {\n      print('Error caught: ' + e.toString());\n    }\n    return Future.value();\n  }\n\n  /// Function to initialize the platform-specific AR view. Can be used to initially set or update session settings.\n  /// [customPlaneTexturePath] refers to flutter assets from the app that is calling this function, NOT to assets within this plugin. Make sure\n  /// the assets are correctly registered in the pubspec.yaml of the parent app (e.g. the ./example app in this plugin's repo)\n  onInitialize({\n    bool showAnimatedGuide = true,\n    bool showFeaturePoints = false,\n    bool showPlanes = true,\n    String? customPlaneTexturePath,\n    bool showWorldOrigin = false,\n    bool handleTaps = true,\n    bool handlePans = false, // nodes are not draggable by default\n    bool handleRotation = false, // nodes can not be rotated by default\n  }) {\n    _channel.invokeMethod<void>('init', {\n      'showAnimatedGuide': showAnimatedGuide,\n      'showFeaturePoints': showFeaturePoints,\n      'planeDetectionConfig': planeDetectionConfig.index,\n      'showPlanes': showPlanes,\n      'customPlaneTexturePath': customPlaneTexturePath,\n      'showWorldOrigin': showWorldOrigin,\n      'handleTaps': handleTaps,\n      'handlePans': handlePans,\n      'handleRotation': handleRotation,\n    });\n  }\n\n  /// Displays the [errorMessage] in a snackbar of the parent widget\n  onError(String errorMessage) {\n    ScaffoldMessenger.of(buildContext).showSnackBar(SnackBar(\n        content: Text(errorMessage),\n        action: SnackBarAction(\n            label: 'HIDE',\n            onPressed:\n                ScaffoldMessenger.of(buildContext).hideCurrentSnackBar)));\n  }\n\n  /// Dispose the AR view on the platforms to pause the scenes and disconnect the platform handlers.\n  /// You should call this before removing the AR view to prevent out of memory erros\n  dispose() async {\n    try {\n      await _channel.invokeMethod<void>(\"dispose\");\n    } catch (e) {\n      print(e);\n    }\n  }\n\n  /// Returns a future ImageProvider that contains a screenshot of the current AR Scene\n  Future<ImageProvider> snapshot() async {\n    final result = await _channel.invokeMethod<Uint8List>('snapshot');\n    return MemoryImage(result!);\n  }\n}\n"
  },
  {
    "path": "lib/models/ar_anchor.dart",
    "content": "import 'package:ar_flutter_plugin/datatypes/anchor_types.dart';\nimport 'package:ar_flutter_plugin/models/ar_node.dart';\nimport 'package:ar_flutter_plugin/utils/json_converters.dart';\nimport 'package:vector_math/vector_math_64.dart';\nimport 'package:flutter/widgets.dart';\n\n/// Object attached to a tracked physical entity of the AR environment (can be initialized with a world transformation)\nabstract class ARAnchor {\n  ARAnchor({\n    required this.type,\n    required this.transformation,\n    String? name,\n  }) : name = name ?? UniqueKey().toString();\n\n  /// Specifies the [AnchorType] of this [ARAnchor]\n  final AnchorType type;\n\n  /// Determines the name of the [ARAnchor]\n  /// Will be autogenerated if not defined.\n  final String name;\n\n  /// Constructs an [ARAnchor] from a serialized anchor object\n  factory ARAnchor.fromJson(Map<String, dynamic> arguments) {\n    final type = arguments['type'];\n    switch (type) {\n      case 0: //(= AnchorType.plane)\n        return ARPlaneAnchor.fromJson(arguments);\n    }\n    return ARUnkownAnchor.fromJson(arguments);\n  }\n\n  /// Defines the anchor’s rotation, translation and scale in world coordinates.\n  final Matrix4 transformation;\n\n  /// Serializes an [ARAnchor]\n  Map<String, dynamic> toJson();\n}\n\n/// An [ARAnchor] fixed to a tracked plane\nclass ARPlaneAnchor extends ARAnchor {\n  ARPlaneAnchor({\n    required Matrix4 transformation,\n    String? name,\n    List<String>? childNodes,\n    String? cloudanchorid,\n    int? ttl,\n  })  : childNodes = childNodes ?? [],\n        cloudanchorid = cloudanchorid ?? null,\n        ttl = ttl ?? 1,\n        super(\n            type: AnchorType.plane, transformation: transformation, name: name);\n\n  /// Names of ARNodes attached to this [APlaneRAnchor]\n  List<String> childNodes;\n\n  /// ID associated with the anchor after uploading it to the google cloud anchor API\n  String? cloudanchorid;\n\n  /// Time to live of the anchor: Determines how long the anchor is stored once it is uploaded to the google cloud anchor API (optional, defaults to 1 day (24hours))\n  int? ttl;\n\n  static ARPlaneAnchor fromJson(Map<String, dynamic> json) =>\n      aRPlaneAnchorFromJson(json);\n\n  @override\n  Map<String, dynamic> toJson() => aRPlaneAnchorToJson(this);\n}\n\n/// Constructs an [ARPlaneAnchor] from a serialized PlaneAnchor object\nARPlaneAnchor aRPlaneAnchorFromJson(Map<String, dynamic> json) {\n  return ARPlaneAnchor(\n    transformation:\n        const MatrixConverter().fromJson(json['transformation'] as List),\n    name: json['name'] as String,\n    childNodes: json['childNodes']\n        ?.map((child) => child.toString())\n        ?.toList()\n        ?.cast<String>(),\n    cloudanchorid: json['cloudanchorid'] as String?,\n    ttl: json['ttl'] as int?,\n  );\n}\n\n/// Serializes an [ARPlaneAnchor]\nMap<String, dynamic> aRPlaneAnchorToJson(ARPlaneAnchor instance) {\n  return <String, dynamic>{\n    'type': instance.type.index,\n    'transformation': MatrixConverter().toJson(instance.transformation),\n    'name': instance.name,\n    'childNodes': instance.childNodes,\n    'cloudanchorid': instance.cloudanchorid,\n    'ttl': instance.ttl,\n  };\n}\n\n/// An [ARAnchor] type that is not supported yet\nclass ARUnkownAnchor extends ARAnchor {\n  ARUnkownAnchor(\n      {required AnchorType type, required Matrix4 transformation, String? name})\n      : super(type: type, transformation: transformation, name: name);\n\n  static ARUnkownAnchor fromJson(Map<String, dynamic> json) =>\n      aRUnkownAnchorFromJson(json);\n\n  @override\n  Map<String, dynamic> toJson() => aRUnkownAnchorToJson(this);\n}\n\nARUnkownAnchor aRUnkownAnchorFromJson(Map<String, dynamic> json) {\n  return ARUnkownAnchor(\n    type: json['type'],\n    transformation:\n        const MatrixConverter().fromJson(json['transformation'] as List),\n    name: json['name'] as String,\n  );\n}\n\nMap<String, dynamic> aRUnkownAnchorToJson(ARUnkownAnchor instance) {\n  return <String, dynamic>{\n    'type': instance.type.index,\n    'transformation': MatrixConverter().toJson(instance.transformation),\n    'name': instance.name,\n  };\n}\n"
  },
  {
    "path": "lib/models/ar_hittest_result.dart",
    "content": "// The code in this file is adapted from Oleksandr Leuschenko' ARKit Flutter Plugin (https://github.com/olexale/arkit_flutter_plugin)\n\nimport 'package:ar_flutter_plugin/datatypes/hittest_result_types.dart';\nimport 'package:ar_flutter_plugin/utils/json_converters.dart';\nimport 'package:json_annotation/json_annotation.dart';\nimport 'package:vector_math/vector_math_64.dart';\n\n/// A result (type, distance from the camera, world transformation) of an intersection found during a hit-test.\nclass ARHitTestResult {\n  ARHitTestResult(\n    this.type,\n    this.distance,\n    this.worldTransform,\n  );\n\n  /// The type of the hit-test result.\n  final ARHitTestResultType type;\n\n  /// The distance from the camera to the intersection in meters.\n  final double distance;\n\n  /// The transformation matrix that defines the intersection’s rotation, translation and scale\n  /// relative to the world.\n  final Matrix4 worldTransform;\n\n  /// Instantiates am [ARHitTestResult] from a serialized ARHitTestResult\n  static ARHitTestResult fromJson(Map<String, dynamic> json) =>\n      _$ARHitTestResultFromJson(json);\n\n  /// Serializes the [ARHitTestResult]\n  Map<String, dynamic> toJson() => _$ARHitTestResultToJson(this);\n}\n\n/// Instantiates am [ARHitTestResult] from a serialized ARHitTestResult\nARHitTestResult _$ARHitTestResultFromJson(Map<String, dynamic> json) {\n  return ARHitTestResult(\n    const ARHitTestResultTypeConverter().fromJson(json['type'] as int),\n    (json['distance'] as num).toDouble(),\n    const MatrixConverter().fromJson(json['worldTransform'] as List),\n  );\n}\n\n/// Serializes the [ARHitTestResult]\nMap<String, dynamic> _$ARHitTestResultToJson(ARHitTestResult instance) {\n  final val = <String, dynamic>{};\n\n  void writeNotNull(String key, dynamic value) {\n    if (value != null) {\n      val[key] = value;\n    }\n  }\n\n  writeNotNull(\n      'type', const ARHitTestResultTypeConverter().toJson(instance.type));\n  val['distance'] = instance.distance;\n  writeNotNull('worldTransform',\n      const MatrixConverter().toJson(instance.worldTransform));\n  return val;\n}\n\n/// Helper class to convert the type of an [ARHitTestResult] from its integer representation to the [ARHitTestResultType] and vice versa\nclass ARHitTestResultTypeConverter\n    implements JsonConverter<ARHitTestResultType, int> {\n  const ARHitTestResultTypeConverter();\n\n  /// Converts the type of an [ARHitTestResult] from its integer representation to the [ARHitTestResultType]\n  @override\n  ARHitTestResultType fromJson(int json) {\n    switch (json) {\n      case 1:\n        return ARHitTestResultType.plane;\n      case 2:\n        return ARHitTestResultType.point;\n      default:\n        return ARHitTestResultType.undefined;\n    }\n  }\n\n  /// Converts the type of an [ARHitTestResult] from its [ARHitTestResultType] to an integer representation\n  @override\n  int toJson(ARHitTestResultType object) {\n    switch (object) {\n      case ARHitTestResultType.plane:\n        return 1;\n      case ARHitTestResultType.point:\n        return 2;\n      default:\n        return 0;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/models/ar_node.dart",
    "content": "// The code in this file is adapted from Oleksandr Leuschenko' ARKit Flutter Plugin (https://github.com/olexale/arkit_flutter_plugin)\n\nimport 'package:ar_flutter_plugin/utils/json_converters.dart';\nimport 'package:flutter/widgets.dart';\nimport 'package:vector_math/vector_math_64.dart';\nimport 'package:json_annotation/json_annotation.dart';\nimport 'dart:math' as math;\nimport 'package:ar_flutter_plugin/datatypes/node_types.dart';\n\n/// ARNode is the model class for node-tree objects.\n/// It encapsulates the position, rotations, and other transforms of a node, which define a coordinate system.\n/// The coordinate systems of all the sub-nodes are relative to the one of their parent node.\nclass ARNode {\n  ARNode({\n    required this.type,\n    required this.uri,\n    String? name,\n    Vector3? position,\n    Vector3? scale,\n    Vector4? rotation,\n    Vector3? eulerAngles,\n    Matrix4? transformation,\n    Map<String, dynamic>? data,\n  })  : name = name ?? UniqueKey().toString(),\n        transformNotifier = ValueNotifier(createTransformMatrix(\n            transformation, position, scale, rotation, eulerAngles)),\n        data = data ?? null;\n\n  /// Specifies the receiver's [NodeType]\n  NodeType type;\n\n  /// Specifies the path to the 3D model used for the [ARNode]. Depending on the [type], this is either a relative path or an URL to an online asset\n  String uri;\n\n  /// Determines the receiver's transform.\n  /// The transform is the combination of the position, rotation and scale defined below.\n  /// So when the transform is set, the receiver's position, rotation and scale are changed to match the new transform.\n  Matrix4 get transform => transformNotifier.value;\n\n  set transform(Matrix4 matrix) {\n    transformNotifier.value = matrix;\n  }\n\n  /// Determines the receiver's position.\n  Vector3 get position => transform.getTranslation();\n\n  set position(Vector3 value) {\n    final old = Matrix4.fromFloat64List(transform.storage);\n    final newT = old.clone();\n    newT.setTranslation(value);\n    transform = newT;\n  }\n\n  /// Determines the receiver's scale.\n  Vector3 get scale => transform.matrixScale;\n\n  set scale(Vector3 value) {\n    transform =\n        Matrix4.compose(position, Quaternion.fromRotation(rotation), value);\n  }\n\n  /// Determines the receiver's rotation.\n  Matrix3 get rotation => transform.getRotation();\n\n  set rotation(Matrix3 value) {\n    transform =\n        Matrix4.compose(position, Quaternion.fromRotation(value), scale);\n  }\n\n  set rotationFromQuaternion(Quaternion value) {\n    transform = Matrix4.compose(position, value, scale);\n  }\n\n  /// Determines the receiver's euler angles.\n  /// The order of components in this vector matches the axes of rotation:\n  /// 1. Pitch (the x component) is the rotation about the node's x-axis (in radians)\n  /// 2. Yaw   (the y component) is the rotation about the node's y-axis (in radians)\n  /// 3. Roll  (the z component) is the rotation about the node's z-axis (in radians)\n  Vector3 get eulerAngles => transform.matrixEulerAngles;\n\n  set eulerAngles(Vector3 value) {\n    final old = Matrix4.fromFloat64List(transform.storage);\n    final newT = old.clone();\n    newT.matrixEulerAngles = value;\n    transform = newT;\n  }\n\n  final ValueNotifier<Matrix4> transformNotifier;\n\n  /// Determines the name of the receiver.\n  /// Will be autogenerated if not defined.\n  final String name;\n\n  /// Holds any data attached to the node, especially useful when uploading serialized nodes to the cloud. This data is not shared with the underlying platform\n  Map<String, dynamic>? data;\n\n  static const _matrixValueNotifierConverter = MatrixValueNotifierConverter();\n\n  Map<String, dynamic> toMap() => <String, dynamic>{\n        'type': type.index,\n        'uri': uri,\n        'transformation':\n            _matrixValueNotifierConverter.toJson(transformNotifier),\n        'name': name,\n        'data': data,\n      }..removeWhere((String k, dynamic v) => v == null);\n\n  static ARNode fromMap(Map<String, dynamic> map) {\n    return ARNode(\n        type: NodeType.values[map[\"type\"]],\n        uri: map[\"uri\"] as String,\n        name: map[\"name\"] as String,\n        transformation: MatrixConverter().fromJson(map[\"transformation\"]),\n        data: Map<String, dynamic>.from(map[\"data\"]));\n  }\n}\n\n/// Helper function to create a Matrix4 from either a given matrix or from position, scale and rotation relative to the origin\nMatrix4 createTransformMatrix(Matrix4? origin, Vector3? position,\n    Vector3? scale, Vector4? rotation, Vector3? eulerAngles) {\n  final transform = origin ?? Matrix4.identity();\n\n  if (position != null) {\n    transform.setTranslation(position);\n  }\n  if (rotation != null) {\n    transform.rotate(\n        Vector3(rotation[0], rotation[1], rotation[2]), rotation[3]);\n  }\n  if (eulerAngles != null) {\n    transform.matrixEulerAngles = eulerAngles;\n  }\n  if (scale != null) {\n    transform.scale(scale);\n  } else {\n    transform.scale(1.0);\n  }\n  return transform;\n}\n\nextension Matrix4Extenstion on Matrix4 {\n  Vector3 get matrixScale {\n    final scale = Vector3.zero();\n    decompose(Vector3.zero(), Quaternion(0, 0, 0, 0), scale);\n    return scale;\n  }\n\n  Vector3 get matrixEulerAngles {\n    final q = Quaternion(0, 0, 0, 0);\n    decompose(Vector3.zero(), q, Vector3.zero());\n\n    final t = q.x;\n    q.x = q.y;\n    q.y = t;\n\n    final angles = Vector3.zero();\n\n    // roll (x-axis rotation)\n    final sinrCosp = 2 * (q.w * q.x + q.y * q.z);\n    final cosrCosp = 1 - 2 * (q.x * q.x + q.y * q.y);\n    angles[0] = math.atan2(sinrCosp, cosrCosp);\n\n    // pitch (y-axis rotation)\n    final sinp = 2 * (q.w * q.y - q.z * q.x);\n    if (sinp.abs() >= 1) {\n      angles[1] =\n          _copySign(math.pi / 2, sinp); // use 90 degrees if out of range\n    } else {\n      angles[1] = math.asin(sinp);\n    }\n    // yaw (z-axis rotation)\n    final sinyCosp = 2 * (q.w * q.z + q.x * q.y);\n    final cosyCosp = 1 - 2 * (q.y * q.y + q.z * q.z);\n    angles[2] = math.atan2(sinyCosp, cosyCosp);\n\n    return angles;\n  }\n\n  set matrixEulerAngles(Vector3 angles) {\n    final translation = Vector3.zero();\n    final scale = Vector3.zero();\n    decompose(translation, Quaternion(0, 0, 0, 0), scale);\n    final r = Quaternion.euler(angles[0], angles[1], angles[2]);\n    setFromTranslationRotationScale(translation, r, scale);\n  }\n}\n\n// https://scidart.org/docs/scidart/numdart/copySign.html\ndouble _copySign(double magnitude, double sign) {\n  // The highest order bit is going to be zero if the\n  // highest order bit of m and s is the same and one otherwise.\n  // So (m^s) will be positive if both m and s have the same sign\n  // and negative otherwise.\n  /*final long m = Double.doubleToRawLongBits(magnitude); // don't care about NaN\n  final long s = Double.doubleToRawLongBits(sign);\n  if ((m^s) >= 0) {\n      return magnitude;\n  }\n  return -magnitude; // flip sign*/\n  if (sign == 0.0 || sign.isNaN || magnitude.sign == sign.sign) {\n    return magnitude;\n  }\n  return -magnitude; // flip sign\n}\n\nclass MatrixValueNotifierConverter\n    implements JsonConverter<ValueNotifier<Matrix4>, List<dynamic>> {\n  const MatrixValueNotifierConverter();\n\n  @override\n  ValueNotifier<Matrix4> fromJson(List<dynamic> json) {\n    return ValueNotifier(Matrix4.fromList(json.cast<double>()));\n  }\n\n  @override\n  List<dynamic> toJson(ValueNotifier<Matrix4> matrix) {\n    final list = List<double>.filled(16, 0.0);\n    matrix.value.copyIntoArray(list);\n    return list;\n  }\n}\n"
  },
  {
    "path": "lib/utils/json_converters.dart",
    "content": "// The code in this file is adapted from Oleksandr Leuschenko' ARKit Flutter Plugin (https://github.com/olexale/arkit_flutter_plugin)\nimport 'package:json_annotation/json_annotation.dart';\nimport 'package:vector_math/vector_math_64.dart';\n\nclass MatrixConverter implements JsonConverter<Matrix4, List<dynamic>> {\n  const MatrixConverter();\n\n  @override\n  Matrix4 fromJson(List<dynamic> json) {\n    return Matrix4.fromList(json.cast<double>());\n  }\n\n  @override\n  List<dynamic> toJson(Matrix4 matrix) {\n    final list = List<double>.filled(16, 0.0);\n    matrix.copyIntoArray(list);\n    return list;\n  }\n}\n"
  },
  {
    "path": "lib/widgets/ar_view.dart",
    "content": "import 'package:ar_flutter_plugin/managers/ar_anchor_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:permission_handler/permission_handler.dart';\nimport 'package:ar_flutter_plugin/managers/ar_session_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_object_manager.dart';\nimport 'package:ar_flutter_plugin/datatypes/config_planedetection.dart';\n\n// Type definitions to enforce a consistent use of the API\ntypedef ARViewCreatedCallback = void Function(\n    ARSessionManager arSessionManager,\n    ARObjectManager arObjectManager,\n    ARAnchorManager arAnchorManager,\n    ARLocationManager arLocationManager);\n\n/// Factory method for creating a platform-dependent AR view\nabstract class PlatformARView {\n  factory PlatformARView(TargetPlatform platform) {\n    switch (platform) {\n      case TargetPlatform.android:\n        return AndroidARView();\n      case TargetPlatform.iOS:\n        return IosARView();\n      default:\n        throw FlutterError;\n    }\n  }\n\n  Widget build(\n      {@required BuildContext context,\n      @required ARViewCreatedCallback arViewCreatedCallback,\n      @required PlaneDetectionConfig planeDetectionConfig});\n\n  /// Callback function that is executed once the view is established\n  void onPlatformViewCreated(int id);\n}\n\n/// Instantiates [ARSessionManager], [ARObjectManager] and returns them to the widget instantiating the [ARView] using the [arViewCreatedCallback]\ncreateManagers(\n    int id,\n    BuildContext? context,\n    ARViewCreatedCallback? arViewCreatedCallback,\n    PlaneDetectionConfig? planeDetectionConfig) {\n  if (context == null ||\n      arViewCreatedCallback == null ||\n      planeDetectionConfig == null) {\n    return;\n  }\n  arViewCreatedCallback(ARSessionManager(id, context, planeDetectionConfig),\n      ARObjectManager(id), ARAnchorManager(id), ARLocationManager());\n}\n\n/// Android-specific implementation of [PlatformARView]\n/// Uses Hybrid Composition to increase peformance on Android 9 and below (https://flutter.dev/docs/development/platform-integration/platform-views)\nclass AndroidARView implements PlatformARView {\n  late BuildContext? _context;\n  late ARViewCreatedCallback? _arViewCreatedCallback;\n  late PlaneDetectionConfig? _planeDetectionConfig;\n\n  @override\n  void onPlatformViewCreated(int id) {\n    print(\"Android platform view created!\");\n    createManagers(id, _context, _arViewCreatedCallback, _planeDetectionConfig);\n  }\n\n  @override\n  Widget build(\n      {BuildContext? context,\n      ARViewCreatedCallback? arViewCreatedCallback,\n      PlaneDetectionConfig? planeDetectionConfig}) {\n    _context = context;\n    _arViewCreatedCallback = arViewCreatedCallback;\n    _planeDetectionConfig = planeDetectionConfig;\n    // This is used in the platform side to register the view.\n    final String viewType = 'ar_flutter_plugin';\n    // Pass parameters to the platform side.\n    final Map<String, dynamic> creationParams = <String, dynamic>{};\n\n    return AndroidView(\n      viewType: viewType,\n      layoutDirection: TextDirection.ltr,\n      creationParams: creationParams,\n      creationParamsCodec: const StandardMessageCodec(),\n      onPlatformViewCreated: onPlatformViewCreated,\n    );\n  }\n}\n\n/// iOS-specific implementation of [PlatformARView]\nclass IosARView implements PlatformARView {\n  BuildContext? _context;\n  ARViewCreatedCallback? _arViewCreatedCallback;\n  PlaneDetectionConfig? _planeDetectionConfig;\n\n  @override\n  void onPlatformViewCreated(int id) {\n    print(\"iOS platform view created!\");\n    createManagers(id, _context, _arViewCreatedCallback, _planeDetectionConfig);\n  }\n\n  @override\n  Widget build(\n      {BuildContext? context,\n      ARViewCreatedCallback? arViewCreatedCallback,\n      PlaneDetectionConfig? planeDetectionConfig}) {\n    _context = context;\n    _arViewCreatedCallback = arViewCreatedCallback;\n    _planeDetectionConfig = planeDetectionConfig;\n    // This is used in the platform side to register the view.\n    final String viewType = 'ar_flutter_plugin';\n    // Pass parameters to the platform side.\n    final Map<String, dynamic> creationParams = <String, dynamic>{};\n\n    return UiKitView(\n      viewType: viewType,\n      layoutDirection: TextDirection.ltr,\n      creationParams: creationParams,\n      creationParamsCodec: const StandardMessageCodec(),\n      onPlatformViewCreated: onPlatformViewCreated,\n    );\n  }\n}\n\n/// If camera permission is granted, [ARView] creates a platform-dependent view from the factory method [PlatformARView]. To instantiate an [ARView],\n/// the calling widget needs to pass the callback function [onARViewCreated] to which the function [createManagers] returns managers such as the\n/// [ARSessionManager] and the [ARObjectManager]. [planeDetectionConfig] is passed to the constructor to determine which types of planes the underlying\n/// AR frameworks should track (defaults to none).\n/// If camera permission is not given, the user is prompted to grant it. To modify the UI of the prompts, the following named parameters can be used:\n/// [permissionPromptDescription], [permissionPromptButtonText] and [permissionPromptParentalRestriction].\nclass ARView extends StatefulWidget {\n  final String permissionPromptDescription;\n  final String permissionPromptButtonText;\n  final String permissionPromptParentalRestriction;\n\n  /// Function to be called when the AR View is created\n  final ARViewCreatedCallback onARViewCreated;\n\n  /// Configures the type of planes ARCore and ARKit should track. defaults to none\n  final PlaneDetectionConfig planeDetectionConfig;\n\n  /// Configures whether or not to display the device's platform type above the AR view. Defaults to false\n  final bool showPlatformType;\n\n  ARView(\n      {Key? key,\n      required this.onARViewCreated,\n      this.planeDetectionConfig = PlaneDetectionConfig.none,\n      this.showPlatformType = false,\n      this.permissionPromptDescription =\n          \"Camera permission must be given to the app for AR functions to work\",\n      this.permissionPromptButtonText = \"Grant Permission\",\n      this.permissionPromptParentalRestriction =\n          \"Camera permission is restriced by the OS, please check parental control settings\"})\n      : super(key: key);\n  @override\n  _ARViewState createState() => _ARViewState(\n      showPlatformType: this.showPlatformType,\n      permissionPromptDescription: this.permissionPromptDescription,\n      permissionPromptButtonText: this.permissionPromptButtonText,\n      permissionPromptParentalRestriction:\n          this.permissionPromptParentalRestriction);\n}\n\nclass _ARViewState extends State<ARView> {\n  PermissionStatus _cameraPermission = PermissionStatus.denied;\n  bool showPlatformType;\n  String permissionPromptDescription;\n  String permissionPromptButtonText;\n  String permissionPromptParentalRestriction;\n\n  _ARViewState(\n      {required this.showPlatformType,\n      required this.permissionPromptDescription,\n      required this.permissionPromptButtonText,\n      required this.permissionPromptParentalRestriction});\n\n  @override\n  void initState() {\n    super.initState();\n    initCameraPermission();\n  }\n\n  initCameraPermission() async {\n    requestCameraPermission();\n  }\n\n  requestCameraPermission() async {\n    final cameraPermission = await Permission.camera.request();\n    setState(() {\n      _cameraPermission = cameraPermission;\n    });\n  }\n\n  requestCameraPermissionFromSettings() async {\n    final cameraPermission = await Permission.camera.request();\n    if (cameraPermission == PermissionStatus.permanentlyDenied) {\n      openAppSettings();\n    }\n    setState(() {\n      _cameraPermission = cameraPermission;\n    });\n  }\n\n  @override\n  build(BuildContext context) {\n    switch (_cameraPermission) {\n      case (PermissionStatus\n          .limited): //iOS-specific: permissions granted for this specific application\n      case (PermissionStatus.granted):\n        {\n          return Column(children: [\n            if (showPlatformType) Text(Theme.of(context).platform.toString()),\n            Expanded(\n                child: PlatformARView(Theme.of(context).platform).build(\n                    context: context,\n                    arViewCreatedCallback: widget.onARViewCreated,\n                    planeDetectionConfig: widget.planeDetectionConfig)),\n          ]);\n        }\n      case (PermissionStatus.denied):\n        {\n          return Center(\n              child: Column(\n            children: [\n              Text(permissionPromptDescription),\n              ElevatedButton(\n                  child: Text(permissionPromptButtonText),\n                  onPressed: () async => {await requestCameraPermission()})\n            ],\n          ));\n        }\n      case (PermissionStatus\n          .permanentlyDenied): //Android-specific: User needs to open Settings to give permissions\n        {\n          return Center(\n              child: Column(\n            children: [\n              Text(permissionPromptDescription),\n              ElevatedButton(\n                  child: Text(permissionPromptButtonText),\n                  onPressed: () async =>\n                      {await requestCameraPermissionFromSettings()})\n            ],\n          ));\n        }\n      case (PermissionStatus.restricted):\n        {\n          //iOS only\n          return Center(child: Text(permissionPromptParentalRestriction));\n        }\n      default:\n        return Text('something went wrong');\n    }\n  }\n}\n"
  },
  {
    "path": "pubspec.yaml",
    "content": "name: ar_flutter_plugin\ndescription: Flutter Plugin for creating (collaborative) Augmented Reality experiences - Supports ARKit for iOS and ARCore for Android devices.\nversion: 0.7.3\nhomepage: https://lars.carius.io\nrepository: https://github.com/CariusLars/ar_flutter_plugin\n\nenvironment:\n  sdk: \">=2.16.1 <3.0.0\"\n  flutter: \">=1.20.0\"\n\ndependencies:\n  flutter:\n    sdk: flutter\n  permission_handler: ^10.1.0\n  vector_math: ^2.1.1\n  json_annotation: ^4.5.0\n  geolocator: ^9.0.0\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter.\nflutter:\n  # This section identifies this Flutter project as a plugin project.\n  # The 'pluginClass' and Android 'package' identifiers should not ordinarily\n  # be modified. They are used by the tooling to maintain consistency when\n  # adding or updating assets for this project.\n  plugin:\n    platforms:\n      android:\n        package: io.carius.lars.ar_flutter_plugin\n        pluginClass: ArFlutterPlugin\n      ios:\n        pluginClass: ArFlutterPlugin\n"
  },
  {
    "path": "test/ar_flutter_plugin_test.dart",
    "content": "import 'package:flutter/services.dart';\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:ar_flutter_plugin/ar_flutter_plugin.dart';\n\nvoid main() {\n  const MethodChannel channel = MethodChannel('ar_flutter_plugin');\n\n  TestWidgetsFlutterBinding.ensureInitialized();\n\n  setUp(() {\n    channel.setMockMethodCallHandler((MethodCall methodCall) async {\n      return '42';\n    });\n  });\n\n  tearDown(() {\n    channel.setMockMethodCallHandler(null);\n  });\n\n  test('getPlatformVersion', () async {\n    expect(await ArFlutterPlugin.platformVersion, '42');\n  });\n}\n"
  }
]