Showing preview only (379K chars total). Download the full file or copy to clipboard to get everything.
Repository: CariusLars/ar_flutter_plugin
Branch: main
Commit: 16fa29a8d30a
Files: 97
Total size: 349.7 KB
Directory structure:
gitextract_cy7w_x8f/
├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── android/
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ ├── settings.gradle
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── kotlin/
│ └── io/
│ └── carius/
│ └── lars/
│ └── ar_flutter_plugin/
│ ├── AndroidARView.kt
│ ├── AndroidARViewFactory.kt
│ ├── ArFlutterPlugin.kt
│ ├── ArModelBuilder.kt
│ ├── CloudAnchorHandler.kt
│ └── Serialization/
│ ├── Deserializers.kt
│ └── Serializers.kt
├── cloudAnchorSetup.md
├── example/
│ ├── .gitignore
│ ├── .metadata
│ ├── Models/
│ │ └── Chicken_01/
│ │ ├── Chicken_01.gltf
│ │ └── license.txt
│ ├── README.md
│ ├── android/
│ │ ├── .gitignore
│ │ ├── app/
│ │ │ ├── build.gradle
│ │ │ └── src/
│ │ │ ├── debug/
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── main/
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── kotlin/
│ │ │ │ │ └── io/
│ │ │ │ │ └── carius/
│ │ │ │ │ └── lars/
│ │ │ │ │ └── ar_flutter_plugin_example/
│ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── res/
│ │ │ │ ├── drawable/
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable-v21/
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── values/
│ │ │ │ │ └── styles.xml
│ │ │ │ └── values-night/
│ │ │ │ └── styles.xml
│ │ │ └── profile/
│ │ │ └── AndroidManifest.xml
│ │ ├── build.gradle
│ │ ├── gradle/
│ │ │ └── wrapper/
│ │ │ └── gradle-wrapper.properties
│ │ ├── gradle.properties
│ │ └── settings.gradle
│ ├── ios/
│ │ ├── .gitignore
│ │ ├── Flutter/
│ │ │ ├── AppFrameworkInfo.plist
│ │ │ ├── Debug.xcconfig
│ │ │ └── Release.xcconfig
│ │ ├── Podfile
│ │ ├── Runner/
│ │ │ ├── AppDelegate.swift
│ │ │ ├── Assets.xcassets/
│ │ │ │ ├── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Contents.json
│ │ │ │ └── LaunchImage.imageset/
│ │ │ │ ├── Contents.json
│ │ │ │ └── README.md
│ │ │ ├── Base.lproj/
│ │ │ │ ├── LaunchScreen.storyboard
│ │ │ │ └── Main.storyboard
│ │ │ ├── Info.plist
│ │ │ └── Runner-Bridging-Header.h
│ │ ├── Runner.xcodeproj/
│ │ │ ├── project.pbxproj
│ │ │ ├── project.xcworkspace/
│ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ └── xcshareddata/
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ │ └── xcshareddata/
│ │ │ └── xcschemes/
│ │ │ └── Runner.xcscheme
│ │ └── Runner.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ └── IDEWorkspaceChecks.plist
│ ├── lib/
│ │ ├── examples/
│ │ │ ├── cloudanchorexample.dart
│ │ │ ├── debugoptionsexample.dart
│ │ │ ├── externalmodelmanagementexample.dart
│ │ │ ├── localandwebobjectsexample.dart
│ │ │ ├── objectgesturesexample.dart
│ │ │ ├── objectsonplanesexample.dart
│ │ │ └── screenshotexample.dart
│ │ └── main.dart
│ ├── pubspec.yaml
│ └── test/
│ └── widget_test.dart
├── ios/
│ ├── .gitignore
│ ├── Classes/
│ │ ├── ArFlutterPlugin.h
│ │ ├── ArFlutterPlugin.m
│ │ ├── ArModelBuilder.swift
│ │ ├── CloudAnchorHandler.swift
│ │ ├── IosARView.swift
│ │ ├── IosARViewFactory.swift
│ │ ├── JWTGenerator.swift
│ │ ├── Serialization/
│ │ │ ├── Deserializers.swift
│ │ │ └── Serializers.swift
│ │ └── SwiftArFlutterPlugin.swift
│ └── ar_flutter_plugin.podspec
├── lib/
│ ├── ar_flutter_plugin.dart
│ ├── datatypes/
│ │ ├── anchor_types.dart
│ │ ├── config_planedetection.dart
│ │ ├── hittest_result_types.dart
│ │ └── node_types.dart
│ ├── managers/
│ │ ├── ar_anchor_manager.dart
│ │ ├── ar_location_manager.dart
│ │ ├── ar_object_manager.dart
│ │ └── ar_session_manager.dart
│ ├── models/
│ │ ├── ar_anchor.dart
│ │ ├── ar_hittest_result.dart
│ │ └── ar_node.dart
│ ├── utils/
│ │ └── json_converters.dart
│ └── widgets/
│ └── ar_view.dart
├── pubspec.yaml
└── test/
└── ar_flutter_plugin_test.dart
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Credential files
/example/ios/Runner/GoogleService-Info.plist
/example/ios/Runner/cloudAnchorKey.json
/example/android/app/google-services.json
# Gitignore entries taken from https://github.com/flutter/flutter/blob/master/.gitignore :
# Miscellaneous
*.class
*.lock
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# Visual Studio Code related
.classpath
.project
.settings/
.vscode/
# Flutter repo-specific
/bin/cache/
/bin/internal/bootstrap.bat
/bin/internal/bootstrap.sh
/bin/mingit/
/dev/benchmarks/mega_gallery/
/dev/bots/.recipe_deps
/dev/bots/android_tools/
/dev/devicelab/ABresults*.json
/dev/docs/doc/
/dev/docs/flutter.docs.zip
/dev/docs/lib/
/dev/docs/pubspec.yaml
/dev/integration_tests/**/xcuserdata
/dev/integration_tests/**/Pods
/packages/flutter/coverage/
version
analysis_benchmark.json
# packages file containing multi-root paths
.packages.generated
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
**/generated_plugin_registrant.dart
.packages
.pub-cache/
.pub/
build/
flutter_*.png
linked_*.ds
unlinked.ds
unlinked_spec.ds
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
**/android/key.properties
*.jks
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/.last_build_id
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# macOS
**/macos/Flutter/GeneratedPluginRegistrant.swift
# Coverage
coverage/
# Symbols
app.*.symbols
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
!/dev/ci/**/Gemfile.lock
================================================
FILE: .metadata
================================================
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 4b50ca7f7fbf56be72e54cd200825b760416a356
channel: beta
project_type: plugin
================================================
FILE: CHANGELOG.md
================================================
# Changelog
##0.7.3
* Update the examples with null-safety
## 0.7.2
* Fixes missing texturing on iOS
## 0.7.1
* Adds config to fix iOS cloud anchors not being able to upload
## 0.7.0
* Adds support to calculate distance between device and anchor and distance between two anchors
## 0.6.5
* Fixes the 'addNode' function to return true when a node is added to an anchor.
## 0.6.4
* Flutter 3 compatibility
## 0.6.3
* The function 'addNode' returned only true. You have now modified it to return false as well.
* Prevent apps from turning off when errors other than those on your camera occur.
## 0.6.2
* Slight changes in ```AndroidARView``` dispose methods to prevent memory overflow issues when AR view is closed and reopened multiple times
## 0.6.1
* Adds ```dispose``` method to ```ARSessionManager``` to prevent memory overflow issues when AR view is closed and reopened multiple times
## 0.6.0
* Adds handling of two gestures: panning and rotating
* Adds example showcasing handling and panning of nodes
* 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```
* Updates ```ARCore/CloudAnchors``` to 1.26.0 on iOS
* Removes overly restrictive permission for background location access on Android
## 0.5.1
* Removes overly restrictive background location permission on Android
## 0.5.0
* Adds new nodetypes ```fileSystemAppFolderGLB``` and ```fileSystemAppFolderGLTF2``` to load renderables from the device's local storage assigned to the current app
* Extends the ```localandwebobjectsexample``` of the example app to showcase the new form of model loading
* Adds snapshot functionality to the session manager to take screenshots of the ARView
* Adds ```screenshotexample``` to the example app to showcase the snapshot functionality
* Updates package versions of Flutter packages ```geolocator```, ```permission_handler```, ```vector_math```, and iOS package ```ARCore/CloudAnchors``` in the plugin
* Updates package versions of Flutter packages ```firebase_core```, ```cloud_firestore```, ```geoflutterfire```, and ```FirebaseSDKVersion ``` on iOS in the example application
## 0.4.3
* Updates documentation after publishing to [pub.dev](https://pub.dev)
## 0.4.2
* Updates documentation
* Deletes unnecessary files
## 0.4.1
* 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.
## 0.4.0
* Adds location manager which can be used to query the device's location (including permission and update handling)
* Adds geoflutterfire to support uploading GPS coordinates alongside anchors and downloading anchors and objects by their location
* 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
* Bugfix: fixes bug on Android causing some examples to crash because the cloud anchor manager wasn't initialized
## 0.3.0
* BREAKING CHANGE: Converts plugin to adhere to Flutter null safety
* Adds Cloud Anchor functionality (uploading and downloading anchors to/from the Google Cloud Anchor API), including keyless authentication
* Adds Cloud Anchor example demonstrating how to use Firebase to manage cloud anchor IDs and corresponding data (e.g. on-tap texts)
* Adds ```data``` member variable to ```ARNode``` as a flexible variable to hold any information associated with the node
## 0.2.1
* Bugfix: Handles singularities in affine transformation matrix deserialization on Android
## 0.2.0
* Adds AR Anchor as a common representation of anchors on all platforms
* 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
* 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)
* Adds option to trigger callbacks when nodes are tapped
* Adds example to showcase hittests, creating and placing anchors and attaching nodes to anchors
## 0.1.0
* Adds AR Node as a common representation of nodes on all platforms
* 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
* 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
* Updates debug option functionality: options can be changed at runtime (see Debug Option example)
* Updates examples to showcase current state of the plugin
## 0.0.1
* Coarse Plugin Architecture layed out
* ARView supports iOS (ARKit) and Android (ARCore) devices
* Camera Permission checks added
* Debug options added on both platforms: Feature Points visualization, detected planes visualization and world origin visualization
* Adds possibility to use own texture for plane visualization on both platforms
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 Lars Carius
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# ar_flutter_plugin
[](https://pub.dev/packages/ar_flutter_plugin)
Flutter Plugin for (collaborative) Augmented Reality - Supports ARKit for iOS and ARCore for Android devices.
Many 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.
## Getting Started
### Installing
Add the Flutter package to your project by running:
```bash
flutter pub add ar_flutter_plugin
```
Or manually add this to your `pubspec.yaml` file (and run `flutter pub get`):
# ar_flutter_plugin package extension
```yaml
dependencies:
ar_flutter_plugin: ^0.7.3
```
### Importing
Add this to your code:
```dart
import 'package:ar_flutter_plugin/ar_flutter_plugin.dart';
```
If 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:
```pod
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
# Additional configuration options could already be set here
# BEGINNING OF WHAT YOU SHOULD ADD
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: PermissionGroup.camera
'PERMISSION_CAMERA=1',
## dart: PermissionGroup.photos
'PERMISSION_PHOTOS=1',
## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
'PERMISSION_LOCATION=1',
## dart: PermissionGroup.sensors
'PERMISSION_SENSORS=1',
## dart: PermissionGroup.bluetooth
'PERMISSION_BLUETOOTH=1',
# add additional permission groups if required
]
# END OF WHAT YOU SHOULD ADD
end
end
end
```
### Example Applications
To try out the plugin, it is best to have a look at one of the following examples implemented in the `Example` app:
| Example Name | Description | Link to Code |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| 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) |
| 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) |
| 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) |
| 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) |
| |
| 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) |
| 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) |
| 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) |
## Contributing
Contributions 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).
## Plugin Architecture
This is a rough sketch of the architecture the plugin implements:

================================================
FILE: android/.gitignore
================================================
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
================================================
FILE: android/build.gradle
================================================
group 'io.carius.lars.ar_flutter_plugin'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
rootProject.allprojects {
repositories {
google()
jcenter()
}
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 30
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
minSdkVersion 24
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.google.ar:core:1.22.0"
implementation 'com.google.ar.sceneform:core:1.15.0'
implementation 'com.google.ar.sceneform:assets:1.15.0'
implementation 'com.google.android.gms:play-services-auth:16+'
implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.17.1'
implementation 'androidx.appcompat:appcompat:1.3.0'
}
afterEvaluate {
def containsEmbeddingDependencies = false
for (def configuration : configurations.all) {
for (def dependency : configuration.dependencies) {
if (dependency.group == 'io.flutter' &&
dependency.name.startsWith('flutter_embedding') &&
dependency.isTransitive())
{
containsEmbeddingDependencies = true
break
}
}
}
if (!containsEmbeddingDependencies) {
android {
dependencies {
def lifecycle_version = "1.1.1"
compileOnly "android.arch.lifecycle:runtime:$lifecycle_version"
compileOnly "android.arch.lifecycle:common:$lifecycle_version"
compileOnly "android.arch.lifecycle:common-java8:$lifecycle_version"
}
}
}
}
================================================
FILE: android/gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
================================================
FILE: android/gradle.properties
================================================
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
================================================
FILE: android/settings.gradle
================================================
rootProject.name = 'ar_flutter_plugin'
================================================
FILE: android/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.carius.lars.ar_flutter_plugin">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Sceneform requires OpenGL ES 3.0 or later. -->
<uses-feature android:glEsVersion="0x00030000" android:required="true" />
<application>
<!-- "AR Optional" app, contains non-AR features that can be used when
"Google Play Services for AR" (ARCore) is not available. -->
<meta-data android:name="com.google.ar.core" android:value="optional" />
</application>
</manifest>
================================================
FILE: android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/AndroidARView.kt
================================================
package io.carius.lars.ar_flutter_plugin
import android.app.Activity
import android.app.Application
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.HandlerThread
import android.util.Log
import android.view.MotionEvent
import android.view.PixelCopy
import android.view.View
import android.widget.Toast
import com.google.ar.core.*
import com.google.ar.core.exceptions.*
import com.google.ar.sceneform.*
import com.google.ar.sceneform.math.Vector3
import com.google.ar.sceneform.ux.*
import io.carius.lars.ar_flutter_plugin.Serialization.deserializeMatrix4
import io.carius.lars.ar_flutter_plugin.Serialization.serializeAnchor
import io.carius.lars.ar_flutter_plugin.Serialization.serializeHitResult
import io.carius.lars.ar_flutter_plugin.Serialization.serializePose
import io.flutter.FlutterInjector
import io.flutter.embedding.engine.loader.FlutterLoader
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.platform.PlatformView
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.nio.FloatBuffer
import java.util.concurrent.CompletableFuture
import android.R
import com.google.ar.sceneform.rendering.*
import android.view.ViewGroup
import com.google.ar.core.TrackingState
internal class AndroidARView(
val activity: Activity,
context: Context,
messenger: BinaryMessenger,
id: Int,
creationParams: Map<String?, Any?>?
) : PlatformView {
// constants
private val TAG: String = AndroidARView::class.java.name
// Lifecycle variables
private var mUserRequestedInstall = true
lateinit var activityLifecycleCallbacks: Application.ActivityLifecycleCallbacks
private val viewContext: Context
// Platform channels
private val sessionManagerChannel: MethodChannel = MethodChannel(messenger, "arsession_$id")
private val objectManagerChannel: MethodChannel = MethodChannel(messenger, "arobjects_$id")
private val anchorManagerChannel: MethodChannel = MethodChannel(messenger, "aranchors_$id")
// UI variables
private lateinit var arSceneView: ArSceneView
private lateinit var transformationSystem: TransformationSystem
private var showFeaturePoints = false
private var showAnimatedGuide = false
private lateinit var animatedGuide: View
private var pointCloudNode = Node()
private var worldOriginNode = Node()
// Setting defaults
private var enableRotation = false
private var enablePans = false
private var keepNodeSelected = true;
private var footprintSelectionVisualizer = FootprintSelectionVisualizer()
// Model builder
private var modelBuilder = ArModelBuilder()
// Cloud anchor handler
private lateinit var cloudAnchorHandler: CloudAnchorHandler
private lateinit var sceneUpdateListener: com.google.ar.sceneform.Scene.OnUpdateListener
private lateinit var onNodeTapListener: com.google.ar.sceneform.Scene.OnPeekTouchListener
// Method channel handlers
private val onSessionMethodCall =
object : MethodChannel.MethodCallHandler {
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
Log.d(TAG, "AndroidARView onsessionmethodcall reveived a call!")
when (call.method) {
"init" -> {
initializeARView(call, result)
}
"getAnchorPose" -> {
val anchorNode = arSceneView.scene.findByName(call.argument("anchorId")) as AnchorNode?
if (anchorNode != null) {
result.success(serializePose(anchorNode.anchor!!.pose))
} else {
result.error("Error", "could not get anchor pose", null)
}
}
"getCameraPose" -> {
val cameraPose = arSceneView.arFrame?.camera?.displayOrientedPose
if (cameraPose != null) {
result.success(serializePose(cameraPose!!))
} else {
result.error("Error", "could not get camera pose", null)
}
}
"snapshot" -> {
var bitmap = Bitmap.createBitmap(arSceneView.width, arSceneView.height,
Bitmap.Config.ARGB_8888);
// Create a handler thread to offload the processing of the image.
var handlerThread = HandlerThread("PixelCopier");
handlerThread.start();
// Make the request to copy.
PixelCopy.request(arSceneView, bitmap, { copyResult:Int ->
Log.d(TAG, "PIXELCOPY DONE")
if (copyResult == PixelCopy.SUCCESS) {
try {
val mainHandler = Handler(context.mainLooper)
val runnable = Runnable {
val stream = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream)
val data = stream.toByteArray()
result.success(data)
}
mainHandler.post(runnable)
} catch (e: IOException) {
result.error("e", e.message, e.stackTrace);
}
} else {
result.error("e", "failed to take screenshot", null);
}
handlerThread.quitSafely();
}, Handler(handlerThread.looper));
}
"dispose" -> {
dispose()
}
else -> {}
}
}
}
private val onObjectMethodCall =
object : MethodChannel.MethodCallHandler {
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
Log.d(TAG, "AndroidARView onobjectmethodcall reveived a call!")
when (call.method) {
"init" -> {
// objectManagerChannel.invokeMethod("onError", listOf("ObjectTEST from
// Android"))
}
"addNode" -> {
val dict_node: HashMap<String, Any>? = call.arguments as? HashMap<String, Any>
dict_node?.let{
addNode(it).thenAccept{status: Boolean ->
result.success(status)
}.exceptionally { throwable ->
result.error("e", throwable.message, throwable.stackTrace)
null
}
}
}
"addNodeToPlaneAnchor" -> {
val dict_node: HashMap<String, Any>? = call.argument<HashMap<String, Any>>("node")
val dict_anchor: HashMap<String, Any>? = call.argument<HashMap<String, Any>>("anchor")
if (dict_node != null && dict_anchor != null) {
addNode(dict_node, dict_anchor).thenAccept{status: Boolean ->
result.success(status)
}.exceptionally { throwable ->
result.error("e", throwable.message, throwable.stackTrace)
null
}
} else {
result.success(false)
}
}
"removeNode" -> {
val nodeName: String? = call.argument<String>("name")
nodeName?.let{
if (transformationSystem.selectedNode?.name == nodeName){
transformationSystem.selectNode(null)
keepNodeSelected = true
}
val node = arSceneView.scene.findByName(nodeName)
node?.let{
arSceneView.scene.removeChild(node)
result.success(null)
}
}
}
"transformationChanged" -> {
val nodeName: String? = call.argument<String>("name")
val newTransformation: ArrayList<Double>? = call.argument<ArrayList<Double>>("transformation")
nodeName?.let{ name ->
newTransformation?.let{ transform ->
transformNode(name, transform)
result.success(null)
}
}
}
else -> {}
}
}
}
private val onAnchorMethodCall =
object : MethodChannel.MethodCallHandler {
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"addAnchor" -> {
val anchorType: Int? = call.argument<Int>("type")
if (anchorType != null){
when(anchorType) {
0 -> { // Plane Anchor
val transform: ArrayList<Double>? = call.argument<ArrayList<Double>>("transformation")
val name: String? = call.argument<String>("name")
if ( name != null && transform != null){
result.success(addPlaneAnchor(transform, name))
} else {
result.success(false)
}
}
else -> result.success(false)
}
} else {
result.success(false)
}
}
"removeAnchor" -> {
val anchorName: String? = call.argument<String>("name")
anchorName?.let{ name ->
removeAnchor(name)
}
}
"initGoogleCloudAnchorMode" -> {
if (arSceneView.session != null) {
val config = Config(arSceneView.session)
config.cloudAnchorMode = Config.CloudAnchorMode.ENABLED
config.updateMode = Config.UpdateMode.LATEST_CAMERA_IMAGE
config.focusMode = Config.FocusMode.AUTO
arSceneView.session?.configure(config)
cloudAnchorHandler = CloudAnchorHandler(arSceneView.session!!)
} else {
sessionManagerChannel.invokeMethod("onError", listOf("Error initializing cloud anchor mode: Session is null"))
}
}
"uploadAnchor" -> {
val anchorName: String? = call.argument<String>("name")
val ttl: Int? = call.argument<Int>("ttl")
anchorName?.let {
val anchorNode = arSceneView.scene.findByName(anchorName) as AnchorNode?
if (ttl != null) {
cloudAnchorHandler.hostCloudAnchorWithTtl(anchorName, anchorNode!!.anchor, cloudAnchorUploadedListener(), ttl!!)
} else {
cloudAnchorHandler.hostCloudAnchor(anchorName, anchorNode!!.anchor, cloudAnchorUploadedListener())
}
//Log.d(TAG, "---------------- HOSTING INITIATED ------------------")
result.success(true)
}
}
"downloadAnchor" -> {
val anchorId: String? = call.argument<String>("cloudanchorid")
//Log.d(TAG, "---------------- RESOLVING INITIATED ------------------")
anchorId?.let {
cloudAnchorHandler.resolveCloudAnchor(anchorId, cloudAnchorDownloadedListener())
}
}
else -> {}
}
}
}
override fun getView(): View {
return arSceneView
}
override fun dispose() {
// Destroy AR session
Log.d(TAG, "dispose called")
try {
onPause()
onDestroy()
ArSceneView.destroyAllResources()
} catch (e: Exception) {
e.printStackTrace()
}
}
init {
Log.d(TAG, "Initializing AndroidARView")
viewContext = context
arSceneView = ArSceneView(context)
setupLifeCycle(context)
sessionManagerChannel.setMethodCallHandler(onSessionMethodCall)
objectManagerChannel.setMethodCallHandler(onObjectMethodCall)
anchorManagerChannel.setMethodCallHandler(onAnchorMethodCall)
//Original visualizer: com.google.ar.sceneform.ux.R.raw.sceneform_footprint
MaterialFactory.makeTransparentWithColor(context, Color(255f, 255f, 255f, 0.3f))
.thenAccept { mat ->
footprintSelectionVisualizer.footprintRenderable = ShapeFactory.makeCylinder(0.7f,0.05f, Vector3(0f,0f,0f), mat)
}
transformationSystem =
TransformationSystem(
activity.resources.displayMetrics,
footprintSelectionVisualizer)
onResume() // call onResume once to setup initial session
// TODO: find out why this does not happen automatically
}
private fun setupLifeCycle(context: Context) {
activityLifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
Log.d(TAG, "onActivityCreated")
}
override fun onActivityStarted(activity: Activity) {
Log.d(TAG, "onActivityStarted")
}
override fun onActivityResumed(activity: Activity) {
Log.d(TAG, "onActivityResumed")
onResume()
}
override fun onActivityPaused(activity: Activity) {
Log.d(TAG, "onActivityPaused")
onPause()
}
override fun onActivityStopped(activity: Activity) {
Log.d(TAG, "onActivityStopped")
// onStopped()
onPause()
}
override fun onActivitySaveInstanceState(
activity: Activity,
outState: Bundle
) {}
override fun onActivityDestroyed(activity: Activity) {
Log.d(TAG, "onActivityDestroyed")
// onPause()
// onDestroy()
}
}
activity.application.registerActivityLifecycleCallbacks(this.activityLifecycleCallbacks)
}
fun onResume() {
// Create session if there is none
if (arSceneView.session == null) {
Log.d(TAG, "ARSceneView session is null. Trying to initialize")
try {
var session: Session?
if (ArCoreApk.getInstance().requestInstall(activity, mUserRequestedInstall) ==
ArCoreApk.InstallStatus.INSTALL_REQUESTED) {
Log.d(TAG, "Install of ArCore APK requested")
session = null
} else {
session = Session(activity)
}
if (session == null) {
// Ensures next invocation of requestInstall() will either return
// INSTALLED or throw an exception.
mUserRequestedInstall = false
return
} else {
val config = Config(session)
config.updateMode = Config.UpdateMode.LATEST_CAMERA_IMAGE
config.focusMode = Config.FocusMode.AUTO
session.configure(config)
arSceneView.setupSession(session)
}
} catch (ex: UnavailableUserDeclinedInstallationException) {
// Display an appropriate message to the user zand return gracefully.
Toast.makeText(
activity,
"TODO: handle exception " + ex.localizedMessage,
Toast.LENGTH_LONG)
.show()
return
} catch (ex: UnavailableArcoreNotInstalledException) {
Toast.makeText(activity, "Please install ARCore", Toast.LENGTH_LONG).show()
return
} catch (ex: UnavailableApkTooOldException) {
Toast.makeText(activity, "Please update ARCore", Toast.LENGTH_LONG).show()
return
} catch (ex: UnavailableSdkTooOldException) {
Toast.makeText(activity, "Please update this app", Toast.LENGTH_LONG).show()
return
} catch (ex: UnavailableDeviceNotCompatibleException) {
Toast.makeText(activity, "This device does not support AR", Toast.LENGTH_LONG)
.show()
return
} catch (e: Exception) {
Toast.makeText(activity, "Failed to create AR session", Toast.LENGTH_LONG).show()
return
}
}
try {
arSceneView.resume()
} catch (ex: CameraNotAvailableException) {
Log.d(TAG, "Unable to get camera" + ex)
activity.finish()
return
} catch (e : Exception){
return
}
}
fun onPause() {
// hide instructions view if no longer required
if (showAnimatedGuide){
val view = activity.findViewById(R.id.content) as ViewGroup
view.removeView(animatedGuide)
showAnimatedGuide = false
}
arSceneView.pause()
}
fun onDestroy() {
try {
arSceneView.session?.close()
arSceneView.destroy()
arSceneView.scene?.removeOnUpdateListener(sceneUpdateListener)
arSceneView.scene?.removeOnPeekTouchListener(onNodeTapListener)
}catch (e : Exception){
e.printStackTrace();
}
}
private fun initializeARView(call: MethodCall, result: MethodChannel.Result) {
// Unpack call arguments
val argShowFeaturePoints: Boolean? = call.argument<Boolean>("showFeaturePoints")
val argPlaneDetectionConfig: Int? = call.argument<Int>("planeDetectionConfig")
val argShowPlanes: Boolean? = call.argument<Boolean>("showPlanes")
val argCustomPlaneTexturePath: String? = call.argument<String>("customPlaneTexturePath")
val argShowWorldOrigin: Boolean? = call.argument<Boolean>("showWorldOrigin")
val argHandleTaps: Boolean? = call.argument<Boolean>("handleTaps")
val argHandleRotation: Boolean? = call.argument<Boolean>("handleRotation")
val argHandlePans: Boolean? = call.argument<Boolean>("handlePans")
val argShowAnimatedGuide: Boolean? = call.argument<Boolean>("showAnimatedGuide")
sceneUpdateListener = com.google.ar.sceneform.Scene.OnUpdateListener {
frameTime: FrameTime -> onFrame(frameTime)
}
onNodeTapListener = com.google.ar.sceneform.Scene.OnPeekTouchListener { hitTestResult, motionEvent ->
//if (hitTestResult.node != null){
//transformationSystem.selectionVisualizer.applySelectionVisual(hitTestResult.node as TransformableNode)
//transformationSystem.selectNode(hitTestResult.node as TransformableNode)
//}
if (hitTestResult.node != null && motionEvent?.action == MotionEvent.ACTION_DOWN) {
objectManagerChannel.invokeMethod("onNodeTap", listOf(hitTestResult.node?.name))
}
transformationSystem.onTouch(
hitTestResult,
motionEvent
)
}
arSceneView.scene?.addOnUpdateListener(sceneUpdateListener)
arSceneView.scene?.addOnPeekTouchListener(onNodeTapListener)
// Configure Plane scanning guide
if (argShowAnimatedGuide == true) { // explicit comparison necessary because of nullable type
showAnimatedGuide = true
val view = activity.findViewById(R.id.content) as ViewGroup
animatedGuide = activity.layoutInflater.inflate(com.google.ar.sceneform.ux.R.layout.sceneform_plane_discovery_layout, null)
view.addView(animatedGuide)
}
// Configure feature points
if (argShowFeaturePoints ==
true) { // explicit comparison necessary because of nullable type
arSceneView.scene.addChild(pointCloudNode)
showFeaturePoints = true
} else {
showFeaturePoints = false
while (pointCloudNode.children?.size
?: 0 > 0) {
pointCloudNode.children?.first()?.setParent(null)
}
pointCloudNode.setParent(null)
}
// Configure plane detection
val config = arSceneView.session?.config
if (config == null) {
sessionManagerChannel.invokeMethod("onError", listOf("session is null"))
}
when (argPlaneDetectionConfig) {
1 -> {
config?.planeFindingMode = Config.PlaneFindingMode.HORIZONTAL
}
2 -> {
config?.planeFindingMode = Config.PlaneFindingMode.VERTICAL
}
3 -> {
config?.planeFindingMode = Config.PlaneFindingMode.HORIZONTAL_AND_VERTICAL
}
else -> {
config?.planeFindingMode = Config.PlaneFindingMode.DISABLED
}
}
arSceneView.session?.configure(config)
// Configure whether or not detected planes should be shown
arSceneView.planeRenderer.isVisible = if (argShowPlanes == true) true else false
// Create custom plane renderer (use supplied texture & increase radius)
argCustomPlaneTexturePath?.let {
val loader: FlutterLoader = FlutterInjector.instance().flutterLoader()
val key: String = loader.getLookupKeyForAsset(it)
val sampler =
Texture.Sampler.builder()
.setMinFilter(Texture.Sampler.MinFilter.LINEAR)
.setWrapMode(Texture.Sampler.WrapMode.REPEAT)
.build()
Texture.builder()
.setSource(viewContext, Uri.parse(key))
.setSampler(sampler)
.build()
.thenAccept { texture: Texture? ->
arSceneView.planeRenderer.material.thenAccept { material: Material ->
material.setTexture(PlaneRenderer.MATERIAL_TEXTURE, texture)
material.setFloat(PlaneRenderer.MATERIAL_SPOTLIGHT_RADIUS, 10f)
}
}
// Set radius to render planes in
arSceneView.scene.addOnUpdateListener { frameTime: FrameTime? ->
val planeRenderer = arSceneView.planeRenderer
planeRenderer.material.thenAccept { material: Material ->
material.setFloat(
PlaneRenderer.MATERIAL_SPOTLIGHT_RADIUS,
10f) // Sets the radius in which to visualize planes
}
}
}
// Configure world origin
if (argShowWorldOrigin == true) {
worldOriginNode = modelBuilder.makeWorldOriginNode(viewContext)
arSceneView.scene.addChild(worldOriginNode)
} else {
worldOriginNode.setParent(null)
}
// Configure Tap handling
if (argHandleTaps == true) { // explicit comparison necessary because of nullable type
arSceneView.scene.setOnTouchListener{ hitTestResult: HitTestResult, motionEvent: MotionEvent? -> onTap(hitTestResult, motionEvent) }
}
// Configure gestures
if (argHandleRotation ==
true) { // explicit comparison necessary because of nullable type
enableRotation = true
} else {
enableRotation = false
}
if (argHandlePans ==
true) { // explicit comparison necessary because of nullable type
enablePans = true
} else {
enablePans = false
}
result.success(null)
}
private fun onFrame(frameTime: FrameTime) {
// hide instructions view if no longer required
if (showAnimatedGuide && arSceneView.arFrame != null){
for (plane in arSceneView.arFrame!!.getUpdatedTrackables(Plane::class.java)) {
if (plane.trackingState === TrackingState.TRACKING) {
val view = activity.findViewById(R.id.content) as ViewGroup
view.removeView(animatedGuide)
showAnimatedGuide = false
break
}
}
}
if (showFeaturePoints) {
// remove points from last frame
while (pointCloudNode.children?.size
?: 0 > 0) {
pointCloudNode.children?.first()?.setParent(null)
}
var pointCloud = arSceneView.arFrame?.acquirePointCloud()
// Access point cloud data (returns FloatBufferw with x,y,z coordinates and confidence
// value).
val points = pointCloud?.getPoints() ?: FloatBuffer.allocate(0)
// Check if there are any feature points
if (points.limit() / 4 >= 1) {
for (index in 0 until points.limit() / 4) {
// Add feature point to scene
val featurePoint =
modelBuilder.makeFeaturePointNode(
viewContext,
points.get(4 * index),
points.get(4 * index + 1),
points.get(4 * index + 2))
featurePoint.setParent(pointCloudNode)
}
}
// Release resources
pointCloud?.release()
}
val updatedAnchors = arSceneView.arFrame!!.updatedAnchors
// Notify the cloudManager of all the updates.
if (this::cloudAnchorHandler.isInitialized) {cloudAnchorHandler.onUpdate(updatedAnchors)}
if (keepNodeSelected && transformationSystem.selectedNode != null && transformationSystem.selectedNode!!.isTransforming){
// If the selected node is currently transforming, we want to deselect it as soon as the transformation is done
keepNodeSelected = false
}
if (!keepNodeSelected && transformationSystem.selectedNode != null && !transformationSystem.selectedNode!!.isTransforming){
// once the transformation is done, deselect the node and allow selection of another node
transformationSystem.selectNode(null)
keepNodeSelected = true
}
if (!enablePans && !enableRotation){
//unselect all nodes as we do not want the selection visualizer
transformationSystem.selectNode(null)
}
}
private fun addNode(dict_node: HashMap<String, Any>, dict_anchor: HashMap<String, Any>? = null): CompletableFuture<Boolean>{
val completableFutureSuccess: CompletableFuture<Boolean> = CompletableFuture()
try {
when (dict_node["type"] as Int) {
0 -> { // GLTF2 Model from Flutter asset folder
// Get path to given Flutter asset
val loader: FlutterLoader = FlutterInjector.instance().flutterLoader()
val key: String = loader.getLookupKeyForAsset(dict_node["uri"] as String)
// Add object to scene
modelBuilder.makeNodeFromGltf(viewContext, transformationSystem, objectManagerChannel, enablePans, enableRotation, dict_node["name"] as String, key, dict_node["transformation"] as ArrayList<Double>)
.thenAccept{node ->
val anchorName: String? = dict_anchor?.get("name") as? String
val anchorType: Int? = dict_anchor?.get("type") as? Int
if (anchorName != null && anchorType != null) {
val anchorNode = arSceneView.scene.findByName(anchorName) as AnchorNode?
if (anchorNode != null) {
anchorNode.addChild(node)
completableFutureSuccess.complete(true)
} else {
completableFutureSuccess.complete(false)
}
} else {
arSceneView.scene.addChild(node)
completableFutureSuccess.complete(true)
}
completableFutureSuccess.complete(false)
}
.exceptionally { throwable ->
// Pass error to session manager (this has to be done on the main thread if this activity)
val mainHandler = Handler(viewContext.mainLooper)
val runnable = Runnable {sessionManagerChannel.invokeMethod("onError", listOf("Unable to load renderable" + dict_node["uri"] as String)) }
mainHandler.post(runnable)
completableFutureSuccess.completeExceptionally(throwable)
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)
}
}
1 -> { // GLB Model from the web
modelBuilder.makeNodeFromGlb(viewContext, transformationSystem, objectManagerChannel, enablePans, enableRotation, dict_node["name"] as String, dict_node["uri"] as String, dict_node["transformation"] as ArrayList<Double>)
.thenAccept{node ->
val anchorName: String? = dict_anchor?.get("name") as? String
val anchorType: Int? = dict_anchor?.get("type") as? Int
if (anchorName != null && anchorType != null) {
val anchorNode = arSceneView.scene.findByName(anchorName) as AnchorNode?
if (anchorNode != null) {
anchorNode.addChild(node)
completableFutureSuccess.complete(true)
} else {
completableFutureSuccess.complete(false)
}
} else {
arSceneView.scene.addChild(node)
completableFutureSuccess.complete(true)
}
completableFutureSuccess.complete(false)
}
.exceptionally { throwable ->
// Pass error to session manager (this has to be done on the main thread if this activity)
val mainHandler = Handler(viewContext.mainLooper)
val runnable = Runnable {sessionManagerChannel.invokeMethod("onError", listOf("Unable to load renderable" + dict_node["uri"] as String)) }
mainHandler.post(runnable)
completableFutureSuccess.completeExceptionally(throwable)
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)
}
}
2 -> { // fileSystemAppFolderGLB
val documentsPath = viewContext.getApplicationInfo().dataDir
val assetPath = documentsPath + "/app_flutter/" + dict_node["uri"] as String
modelBuilder.makeNodeFromGlb(viewContext, transformationSystem, objectManagerChannel, enablePans, enableRotation, dict_node["name"] as String, assetPath as String, dict_node["transformation"] as ArrayList<Double>) //
.thenAccept{node ->
val anchorName: String? = dict_anchor?.get("name") as? String
val anchorType: Int? = dict_anchor?.get("type") as? Int
if (anchorName != null && anchorType != null) {
val anchorNode = arSceneView.scene.findByName(anchorName) as AnchorNode?
if (anchorNode != null) {
anchorNode.addChild(node)
completableFutureSuccess.complete(true)
} else {
completableFutureSuccess.complete(false)
}
} else {
arSceneView.scene.addChild(node)
completableFutureSuccess.complete(true)
}
completableFutureSuccess.complete(false)
}
.exceptionally { throwable ->
// Pass error to session manager (this has to be done on the main thread if this activity)
val mainHandler = Handler(viewContext.mainLooper)
val runnable = Runnable {sessionManagerChannel.invokeMethod("onError", listOf("Unable to load renderable " + dict_node["uri"] as String)) }
mainHandler.post(runnable)
completableFutureSuccess.completeExceptionally(throwable)
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)
}
}
3 -> { //fileSystemAppFolderGLTF2
// Get path to given Flutter asset
val documentsPath = viewContext.getApplicationInfo().dataDir
val assetPath = documentsPath + "/app_flutter/" + dict_node["uri"] as String
// Add object to scene
modelBuilder.makeNodeFromGltf(viewContext, transformationSystem, objectManagerChannel, enablePans, enableRotation, dict_node["name"] as String, assetPath, dict_node["transformation"] as ArrayList<Double>)
.thenAccept{node ->
val anchorName: String? = dict_anchor?.get("name") as? String
val anchorType: Int? = dict_anchor?.get("type") as? Int
if (anchorName != null && anchorType != null) {
val anchorNode = arSceneView.scene.findByName(anchorName) as AnchorNode?
if (anchorNode != null) {
anchorNode.addChild(node)
completableFutureSuccess.complete(true)
} else {
completableFutureSuccess.complete(false)
}
} else {
arSceneView.scene.addChild(node)
completableFutureSuccess.complete(true)
}
completableFutureSuccess.complete(false)
}
.exceptionally { throwable ->
// Pass error to session manager (this has to be done on the main thread if this activity)
val mainHandler = Handler(viewContext.mainLooper)
val runnable = Runnable {sessionManagerChannel.invokeMethod("onError", listOf("Unable to load renderable" + dict_node["uri"] as String)) }
mainHandler.post(runnable)
completableFutureSuccess.completeExceptionally(throwable)
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)
}
}
else -> {
completableFutureSuccess.complete(false)
}
}
} catch (e: java.lang.Exception) {
completableFutureSuccess.completeExceptionally(e)
}
return completableFutureSuccess
}
private fun transformNode(name: String, transform: ArrayList<Double>) {
val node = arSceneView.scene.findByName(name)
node?.let {
val transformTriple = deserializeMatrix4(transform)
it.localScale = transformTriple.first
it.localPosition = transformTriple.second
it.localRotation = transformTriple.third
//it.worldScale = transformTriple.first
//it.worldPosition = transformTriple.second
//it.worldRotation = transformTriple.third
}
}
private fun onTap(hitTestResult: HitTestResult, motionEvent: MotionEvent?): Boolean {
val frame = arSceneView.arFrame
if (hitTestResult.node != null && motionEvent?.action == MotionEvent.ACTION_DOWN) {
objectManagerChannel.invokeMethod("onNodeTap", listOf(hitTestResult.node?.name))
return true
}
if (motionEvent != null && motionEvent.action == MotionEvent.ACTION_DOWN) {
if (transformationSystem.selectedNode == null || (!enablePans && !enableRotation)){
val allHitResults = frame?.hitTest(motionEvent) ?: listOf<HitResult>()
val planeAndPointHitResults =
allHitResults.filter { ((it.trackable is Plane) || (it.trackable is Point)) }
val serializedPlaneAndPointHitResults: ArrayList<HashMap<String, Any>> =
ArrayList(planeAndPointHitResults.map { serializeHitResult(it) })
sessionManagerChannel.invokeMethod(
"onPlaneOrPointTap",
serializedPlaneAndPointHitResults
)
return true
} else {
return false
}
}
return false
}
private fun addPlaneAnchor(transform: ArrayList<Double>, name: String): Boolean {
return try {
val position = floatArrayOf(deserializeMatrix4(transform).second.x, deserializeMatrix4(transform).second.y, deserializeMatrix4(transform).second.z)
val rotation = floatArrayOf(deserializeMatrix4(transform).third.x, deserializeMatrix4(transform).third.y, deserializeMatrix4(transform).third.z, deserializeMatrix4(transform).third.w)
val anchor: Anchor = arSceneView.session!!.createAnchor(Pose(position, rotation))
val anchorNode = AnchorNode(anchor)
anchorNode.name = name
anchorNode.setParent(arSceneView.scene)
true
} catch (e: Exception) {
false
}
}
private fun removeAnchor(name: String) {
val anchorNode = arSceneView.scene.findByName(name) as AnchorNode?
anchorNode?.let{
// Remove corresponding anchor from tracking
anchorNode.anchor?.detach()
// Remove children
for (node in anchorNode.children) {
if (transformationSystem.selectedNode?.name == node.name){
transformationSystem.selectNode(null)
keepNodeSelected = true
}
node.setParent(null)
}
// Remove anchor node
anchorNode.setParent(null)
}
}
private inner class cloudAnchorUploadedListener: CloudAnchorHandler.CloudAnchorListener {
override fun onCloudTaskComplete(anchorName: String?, anchor: Anchor?) {
val cloudState = anchor!!.cloudAnchorState
if (cloudState.isError) {
Log.e(TAG, "Error uploading anchor, state $cloudState")
sessionManagerChannel.invokeMethod("onError", listOf("Error uploading anchor, state $cloudState"))
return
}
// Swap old an new anchor of the respective AnchorNode
val anchorNode = arSceneView.scene.findByName(anchorName) as AnchorNode?
val oldAnchor = anchorNode?.anchor
anchorNode?.anchor = anchor
oldAnchor?.detach()
val args = HashMap<String, String?>()
args["name"] = anchorName
args["cloudanchorid"] = anchor.cloudAnchorId
anchorManagerChannel.invokeMethod("onCloudAnchorUploaded", args)
}
}
private inner class cloudAnchorDownloadedListener: CloudAnchorHandler.CloudAnchorListener {
override fun onCloudTaskComplete(anchorName: String?, anchor: Anchor?) {
val cloudState = anchor!!.cloudAnchorState
if (cloudState.isError) {
Log.e(TAG, "Error downloading anchor, state $cloudState")
sessionManagerChannel.invokeMethod("onError", listOf("Error downloading anchor, state $cloudState"))
return
}
//Log.d(TAG, "---------------- RESOLVING SUCCESSFUL ------------------")
val newAnchorNode = AnchorNode(anchor)
// Register new anchor on the Flutter side of the plugin
anchorManagerChannel.invokeMethod("onAnchorDownloadSuccess", serializeAnchor(newAnchorNode, anchor), object: MethodChannel.Result {
override fun success(result: Any?) {
newAnchorNode.name = result.toString()
newAnchorNode.setParent(arSceneView.scene)
//Log.d(TAG, "---------------- REGISTERING ANCHOR SUCCESSFUL ------------------")
}
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
sessionManagerChannel.invokeMethod("onError", listOf("Error while registering downloaded anchor at the AR Flutter plugin: $errorMessage"))
}
override fun notImplemented() {
sessionManagerChannel.invokeMethod("onError", listOf("Error while registering downloaded anchor at the AR Flutter plugin"))
}
})
}
}
}
================================================
FILE: android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/AndroidARViewFactory.kt
================================================
package io.carius.lars.ar_flutter_plugin
import android.app.Activity
import android.content.Context
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.StandardMessageCodec
import io.flutter.plugin.platform.PlatformView
import io.flutter.plugin.platform.PlatformViewFactory
class AndroidARViewFactory(val activity: Activity, val messenger: BinaryMessenger) :
PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(context: Context?, viewId: Int, args: Any?): PlatformView {
val creationParams = args as Map<String?, Any?>?
return AndroidARView(activity, context!!, messenger, viewId, creationParams)
}
}
================================================
FILE: android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/ArFlutterPlugin.kt
================================================
package io.carius.lars.ar_flutter_plugin
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
/** ArFlutterPlugin */
class ArFlutterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel: MethodChannel
private lateinit var flutterPluginBinding: FlutterPlugin.FlutterPluginBinding
override fun onAttachedToEngine(
@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding
) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "ar_flutter_plugin")
channel.setMethodCallHandler(this)
this.flutterPluginBinding = flutterPluginBinding
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onDetachedFromActivity() {
channel.setMethodCallHandler(null)
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
onAttachedToActivity(binding)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
this.flutterPluginBinding.platformViewRegistry.registerViewFactory(
"ar_flutter_plugin", AndroidARViewFactory(binding.activity, flutterPluginBinding.binaryMessenger))
}
override fun onDetachedFromActivityForConfigChanges() {
onDetachedFromActivity()
}
}
================================================
FILE: android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/ArModelBuilder.kt
================================================
package io.carius.lars.ar_flutter_plugin
import android.R
import android.app.Activity
import android.content.Context
import com.google.ar.sceneform.Node
import com.google.ar.sceneform.math.Vector3
import com.google.ar.sceneform.math.Quaternion
import com.google.ar.sceneform.assets.RenderableSource
import java.util.concurrent.CompletableFuture
import android.net.Uri
import android.view.Gravity
import android.widget.Toast
import com.google.ar.core.*
import com.google.ar.sceneform.ArSceneView
import com.google.ar.sceneform.FrameTime
import com.google.ar.sceneform.math.MathHelper
import com.google.ar.sceneform.rendering.*
import com.google.ar.sceneform.utilities.Preconditions
import com.google.ar.sceneform.ux.*
import io.carius.lars.ar_flutter_plugin.Serialization.*
import io.flutter.FlutterInjector
import io.flutter.embedding.engine.loader.FlutterLoader
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import java.security.AccessController
// Responsible for creating Renderables and Nodes
class ArModelBuilder {
// Creates feature point node
fun makeFeaturePointNode(context: Context, xPos: Float, yPos: Float, zPos: Float): Node {
val featurePoint = Node()
var cubeRenderable: ModelRenderable? = null
MaterialFactory.makeOpaqueWithColor(context, Color(android.graphics.Color.YELLOW))
.thenAccept { material ->
val vector3 = Vector3(0.01f, 0.01f, 0.01f)
cubeRenderable = ShapeFactory.makeCube(vector3, Vector3(xPos, yPos, zPos), material)
cubeRenderable?.isShadowCaster = false
cubeRenderable?.isShadowReceiver = false
}
featurePoint.renderable = cubeRenderable
return featurePoint
}
// Creates a coordinate system model at the world origin (X-axis: red, Y-axis: green, Z-axis:blue)
// 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)
fun makeWorldOriginNode(context: Context): Node {
val axisSize = 0.1f
val axisRadius = 0.005f
val rootNode = Node()
val xNode = Node()
val yNode = Node()
val zNode = Node()
rootNode.addChild(xNode)
rootNode.addChild(yNode)
rootNode.addChild(zNode)
xNode.worldPosition = Vector3(axisSize / 2, 0f, 0f)
xNode.worldRotation = Quaternion.axisAngle(Vector3(0f, 0f, 1f), 90f)
yNode.worldPosition = Vector3(0f, axisSize / 2, 0f)
zNode.worldPosition = Vector3(0f, 0f, axisSize / 2)
zNode.worldRotation = Quaternion.axisAngle(Vector3(1f, 0f, 0f), 90f)
MaterialFactory.makeOpaqueWithColor(context, Color(255f, 0f, 0f))
.thenAccept { redMat ->
xNode.renderable = ShapeFactory.makeCylinder(axisRadius, axisSize, Vector3.zero(), redMat)
}
MaterialFactory.makeOpaqueWithColor(context, Color(0f, 255f, 0f))
.thenAccept { greenMat ->
yNode.renderable = ShapeFactory.makeCylinder(axisRadius, axisSize, Vector3.zero(), greenMat)
}
MaterialFactory.makeOpaqueWithColor(context, Color(0f, 0f, 255f))
.thenAccept { blueMat ->
zNode.renderable = ShapeFactory.makeCylinder(axisRadius, axisSize, Vector3.zero(), blueMat)
}
return rootNode
}
// 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
fun makeNodeFromGltf(context: Context, transformationSystem: TransformationSystem, objectManagerChannel: MethodChannel, enablePans: Boolean, enableRotation: Boolean, name: String, modelPath: String, transformation: ArrayList<Double>): CompletableFuture<CustomTransformableNode> {
val completableFutureNode: CompletableFuture<CustomTransformableNode> = CompletableFuture()
val gltfNode = CustomTransformableNode(transformationSystem, objectManagerChannel, enablePans, enableRotation)
ModelRenderable.builder()
.setSource(context, RenderableSource.builder().setSource(
context,
Uri.parse(modelPath),
RenderableSource.SourceType.GLTF2)
.build())
.setRegistryId(modelPath)
.build()
.thenAccept{ renderable ->
gltfNode.renderable = renderable
gltfNode.name = name
val transform = deserializeMatrix4(transformation)
gltfNode.worldScale = transform.first
gltfNode.worldPosition = transform.second
gltfNode.worldRotation = transform.third
completableFutureNode.complete(gltfNode)
}
.exceptionally { throwable ->
completableFutureNode.completeExceptionally(throwable)
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)
}
return completableFutureNode
}
// 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
fun makeNodeFromGlb(context: Context, transformationSystem: TransformationSystem, objectManagerChannel: MethodChannel, enablePans: Boolean, enableRotation: Boolean, name: String, modelPath: String, transformation: ArrayList<Double>): CompletableFuture<CustomTransformableNode> {
val completableFutureNode: CompletableFuture<CustomTransformableNode> = CompletableFuture()
val gltfNode = CustomTransformableNode(transformationSystem, objectManagerChannel, enablePans, enableRotation)
//gltfNode.scaleController.isEnabled = false
//gltfNode.translationController.isEnabled = false
/*gltfNode.removeTransformationController(translationController)
gltfNode.addTra
val customTranslationController = DragController(
gltfNode,
transformationSystem.dragRecognizer,
objectManagerChannel,
transformationSystem
)*/
ModelRenderable.builder()
.setSource(context, RenderableSource.builder().setSource(
context,
Uri.parse(modelPath),
RenderableSource.SourceType.GLB)
.build())
.setRegistryId(modelPath)
.build()
.thenAccept{ renderable ->
gltfNode.renderable = renderable
gltfNode.name = name
val transform = deserializeMatrix4(transformation)
gltfNode.worldScale = transform.first
gltfNode.worldPosition = transform.second
gltfNode.worldRotation = transform.third
completableFutureNode.complete(gltfNode)
}
.exceptionally{throwable ->
completableFutureNode.completeExceptionally(throwable)
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)
}
return completableFutureNode
}
}
class CustomTransformableNode(transformationSystem: TransformationSystem, objectManagerChannel: MethodChannel, enablePans: Boolean, enableRotation: Boolean) :
TransformableNode(transformationSystem) { //
private lateinit var customTranslationController: CustomTranslationController
private lateinit var customRotationController: CustomRotationController
init {
// Remove standard controllers
translationController.isEnabled = false
rotationController.isEnabled = false
scaleController.isEnabled = false
removeTransformationController(translationController)
removeTransformationController(rotationController)
removeTransformationController(scaleController)
// Add custom controllers if needed
if (enablePans) {
customTranslationController = CustomTranslationController(
this,
transformationSystem.dragRecognizer,
objectManagerChannel
)
addTransformationController(customTranslationController)
}
if (enableRotation) {
customRotationController = CustomRotationController(
this,
transformationSystem.twistRecognizer,
objectManagerChannel
)
addTransformationController(customRotationController)
}
}
}
class CustomTranslationController(transformableNode: BaseTransformableNode, gestureRecognizer: DragGestureRecognizer, objectManagerChannel: MethodChannel) :
TranslationController(transformableNode, gestureRecognizer) {
val platformChannel: MethodChannel = objectManagerChannel
override fun canStartTransformation(gesture: DragGesture): Boolean {
platformChannel.invokeMethod("onPanStart", transformableNode.name)
super.canStartTransformation(gesture)
return transformableNode.isSelected
}
override fun onContinueTransformation(gesture: DragGesture) {
platformChannel.invokeMethod("onPanChange", transformableNode.name)
super.onContinueTransformation(gesture)
}
override fun onEndTransformation(gesture: DragGesture) {
val serializedLocalTransformation = serializeLocalTransformation(transformableNode)
platformChannel.invokeMethod("onPanEnd", serializedLocalTransformation)
super.onEndTransformation(gesture)
}
}
class CustomRotationController(transformableNode: BaseTransformableNode, gestureRecognizer: TwistGestureRecognizer, objectManagerChannel: MethodChannel) :
RotationController(transformableNode, gestureRecognizer) {
val platformChannel: MethodChannel = objectManagerChannel
override fun canStartTransformation(gesture: TwistGesture): Boolean {
platformChannel.invokeMethod("onRotationStart", transformableNode.name)
super.canStartTransformation(gesture)
return transformableNode.isSelected
}
override fun onContinueTransformation(gesture: TwistGesture) {
platformChannel.invokeMethod("onRotationChange", transformableNode.name)
super.onContinueTransformation(gesture)
}
override fun onEndTransformation(gesture: TwistGesture) {
val serializedLocalTransformation = serializeLocalTransformation(transformableNode)
platformChannel.invokeMethod("onRotationEnd", serializedLocalTransformation)
super.onEndTransformation(gesture)
}
}
================================================
FILE: android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/CloudAnchorHandler.kt
================================================
package io.carius.lars.ar_flutter_plugin
import com.google.ar.core.Anchor
import com.google.ar.core.Anchor.CloudAnchorState
import com.google.ar.core.Session
import java.util.*
// Class for handling logic regarding the Google Cloud Anchor API
internal class CloudAnchorHandler( arSession: Session ) {
// Listener that can be attached to hosing or resolving processes
interface CloudAnchorListener {
// Callback to invoke when cloud anchor task finishes
fun onCloudTaskComplete(anchorName: String?, anchor: Anchor?)
}
private val TAG: String = CloudAnchorHandler::class.java.simpleName
private val pendingAnchors = HashMap<Anchor, Pair<String?, CloudAnchorListener?>>()
private val session: Session = arSession
@Synchronized
fun hostCloudAnchor(anchorName: String, anchor: Anchor?, listener: CloudAnchorListener?) {
val newAnchor = session.hostCloudAnchor(anchor)
// Register listener so it is invoked when the operation finishes
pendingAnchors[newAnchor] = Pair(anchorName, listener)
}
@Synchronized
fun hostCloudAnchorWithTtl(anchorName: String, anchor: Anchor?, listener: CloudAnchorListener?, ttl: Int) {
val newAnchor = session.hostCloudAnchorWithTtl(anchor, ttl)
// Register listener so it is invoked when the operation finishes
pendingAnchors[newAnchor] = Pair(anchorName, listener)
}
@Synchronized
fun resolveCloudAnchor(anchorId: String?, listener: CloudAnchorListener?) {
val newAnchor = session.resolveCloudAnchor(anchorId)
// Register listener so it is invoked when the operation finishes
pendingAnchors[newAnchor] = Pair(null, listener)
}
// Updating function that should be called after each session.update call
@Synchronized
fun onUpdate(updatedAnchors: Collection<Anchor>) {
for (anchor in updatedAnchors) {
if (pendingAnchors.containsKey(anchor)) {
if (anchor.cloudAnchorState != CloudAnchorState.NONE && anchor.cloudAnchorState != CloudAnchorState.TASK_IN_PROGRESS){
val element: Pair<String?, CloudAnchorListener?>? = pendingAnchors.remove(anchor)
element!!.second!!.onCloudTaskComplete(element.first, anchor)
}
}
}
}
// Remove all listeners
@Synchronized
fun clearListeners() {
pendingAnchors.clear()
}
}
================================================
FILE: android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/Serialization/Deserializers.kt
================================================
package io.carius.lars.ar_flutter_plugin.Serialization
import com.google.ar.sceneform.math.Quaternion
import com.google.ar.sceneform.math.Vector3
fun deserializeMatrix4(transform: ArrayList<Double>): Triple<Vector3, Vector3, Quaternion> {
val scale = Vector3()
val position = Vector3()
val rotation: Quaternion
// Get the scale by calculating the length of each 3-dimensional column vector of the
// transformation matrix
// See
// https://math.stackexchange.com/questions/237369/given-this-transformation-matrix-how-do-i-decompose-it-into-translation-rotati for a mathematical explanation
scale.x = Vector3(transform[0].toFloat(), transform[1].toFloat(), transform[2].toFloat()).length()
scale.y = Vector3(transform[4].toFloat(), transform[5].toFloat(), transform[6].toFloat()).length()
scale.z =
Vector3(transform[8].toFloat(), transform[9].toFloat(), transform[10].toFloat()).length()
// Get the translation by taking the last column of the transformation matrix
// See
// https://math.stackexchange.com/questions/237369/given-this-transformation-matrix-how-do-i-decompose-it-into-translation-rotati for a mathematical explanation
position.x = transform[12].toFloat()
position.y = transform[13].toFloat()
position.z = transform[14].toFloat()
// Get the rotation matrix from the transformation matrix by normalizing with the scales
// See
// https://math.stackexchange.com/questions/237369/given-this-transformation-matrix-how-do-i-decompose-it-into-translation-rotati for a mathematical explanation
val rowWiseMatrix =
floatArrayOf(
transform[0].toFloat() / scale.x,
transform[4].toFloat() / scale.y,
transform[8].toFloat() / scale.z,
transform[1].toFloat() / scale.x,
transform[5].toFloat() / scale.y,
transform[9].toFloat() / scale.z,
transform[2].toFloat() / scale.x,
transform[6].toFloat() / scale.y,
transform[10].toFloat() / scale.z)
// Calculate the quaternion from the rotation matrix
// See https://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/ for
// a mathematical explanation
val trace = rowWiseMatrix[0] + rowWiseMatrix[4] + rowWiseMatrix[8]
var w = 0.0
var x = 0.0
var y = 0.0
var z = 0.0
if (trace > 0) {
val scalefactor = Math.sqrt(trace + 1.0) * 2
w = 0.25 * scalefactor
x = (rowWiseMatrix[7] - rowWiseMatrix[5]) / scalefactor
y = (rowWiseMatrix[2] - rowWiseMatrix[6]) / scalefactor
z = (rowWiseMatrix[3] - rowWiseMatrix[1]) / scalefactor
} else if ((rowWiseMatrix[0] > rowWiseMatrix[4]) && (rowWiseMatrix[0] > rowWiseMatrix[8])) {
val scalefactor = Math.sqrt(1.0 + rowWiseMatrix[0] - rowWiseMatrix[4] - rowWiseMatrix[8]) * 2
w = (rowWiseMatrix[7] - rowWiseMatrix[5]) / scalefactor
x = 0.25 * scalefactor
y = (rowWiseMatrix[1] + rowWiseMatrix[3]) / scalefactor
z = (rowWiseMatrix[2] + rowWiseMatrix[6]) / scalefactor
} else if (rowWiseMatrix[4] > rowWiseMatrix[8]) {
val scalefactor = Math.sqrt(1.0 + rowWiseMatrix[4] - rowWiseMatrix[0] - rowWiseMatrix[8]) * 2
w = (rowWiseMatrix[2] - rowWiseMatrix[6]) / scalefactor
x = (rowWiseMatrix[1] + rowWiseMatrix[3]) / scalefactor
y = 0.25 * scalefactor
z = (rowWiseMatrix[5] + rowWiseMatrix[7]) / scalefactor
} else {
val scalefactor = Math.sqrt(1.0 + rowWiseMatrix[8] - rowWiseMatrix[0] - rowWiseMatrix[4]) * 2
w = (rowWiseMatrix[3] - rowWiseMatrix[1]) / scalefactor
x = (rowWiseMatrix[2] + rowWiseMatrix[6]) / scalefactor
y = (rowWiseMatrix[5] + rowWiseMatrix[7]) / scalefactor
z = 0.25 * scalefactor
}
val inputRotation = Quaternion(x.toFloat(), y.toFloat(), z.toFloat(), w.toFloat())
// Rotate by an additional 180 degrees around z and y to compensate for the different model
// coordinate system definition used in Sceneform (in comparison to Scenekit and the definition
// used for the Flutter API of this plugin)
val correction_z = Quaternion(0.0f, 0.0f, 1.0f, 180f)
val correction_y = Quaternion(0.0f, 1.0f, 0.0f, 180f)
// Calculate resulting rotation quaternion by multiplying input and corrections
rotation = Quaternion.multiply(Quaternion.multiply(inputRotation, correction_y), correction_z)
return Triple(scale, position, rotation)
}
================================================
FILE: android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/Serialization/Serializers.kt
================================================
package io.carius.lars.ar_flutter_plugin.Serialization
import com.google.ar.core.*
import com.google.ar.sceneform.AnchorNode
import com.google.ar.sceneform.math.Matrix
import com.google.ar.sceneform.math.Quaternion
import com.google.ar.sceneform.math.Vector3
import com.google.ar.sceneform.ux.BaseTransformableNode
import com.google.ar.sceneform.ux.TransformableNode
fun serializeHitResult(hitResult: HitResult): HashMap<String, Any> {
val serializedHitResult = HashMap<String,Any>()
if (hitResult.trackable is Plane && (hitResult.trackable as Plane).isPoseInPolygon(hitResult.hitPose)) {
serializedHitResult["type"] = 1 // Type plane
}
else if (hitResult.trackable is Point){
serializedHitResult["type"] = 2 // Type point
} else {
serializedHitResult["type"] = 0 // Type undefined
}
serializedHitResult["distance"] = hitResult.distance.toDouble()
serializedHitResult["worldTransform"] = serializePose(hitResult.hitPose)
return serializedHitResult
}
fun serializePose(pose: Pose): DoubleArray {
val serializedPose = FloatArray(16)
pose.toMatrix(serializedPose, 0)
// copy into double Array
val serializedPoseDouble = DoubleArray(serializedPose.size)
for (i in serializedPose.indices) {
serializedPoseDouble[i] = serializedPose[i].toDouble()
}
return serializedPoseDouble
}
fun serializePoseWithScale(pose: Pose, scale: Vector3): DoubleArray {
val serializedPose = FloatArray(16)
pose.toMatrix(serializedPose, 0)
// copy into double Array
val serializedPoseDouble = DoubleArray(serializedPose.size)
for (i in serializedPose.indices) {
serializedPoseDouble[i] = serializedPose[i].toDouble()
if (i == 0 || i == 4 || i == 8){
serializedPoseDouble[i] = serializedPoseDouble[i] * scale.x
}
if (i == 1 || i == 5 || i == 9){
serializedPoseDouble[i] = serializedPoseDouble[i] * scale.y
}
if (i == 2 || i == 7 || i == 10){
serializedPoseDouble[i] = serializedPoseDouble[i] * scale.z
}
}
return serializedPoseDouble
}
fun serializeAnchor(anchorNode: AnchorNode, anchor: Anchor?): HashMap<String, Any?> {
val serializedAnchor = HashMap<String, Any?>()
serializedAnchor["type"] = 0 // index for plane anchors
serializedAnchor["name"] = anchorNode.name
serializedAnchor["cloudanchorid"] = anchor?.cloudAnchorId
serializedAnchor["transformation"] = if (anchor != null) serializePose(anchor.pose) else null
serializedAnchor["childNodes"] = anchorNode.children.map { child -> child.name }
return serializedAnchor
}
fun serializeLocalTransformation(node: BaseTransformableNode): HashMap<String, Any>{
val serializedLocalTransformation = HashMap<String, Any>()
serializedLocalTransformation["name"] = node.name
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))
serializedLocalTransformation["transform"] = serializePoseWithScale(transform, node.localScale)
return serializedLocalTransformation
}
================================================
FILE: cloudAnchorSetup.md
================================================
# Getting Started with Cloud Anchors
The 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.
Follow the steps below to set up your application.
## Set up Google Cloud Anchor Service
The 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:
1. 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
2. Register the Android part of your Flutter Application
* 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=)):
* Go to the [Google Cloud Platform Console](https://console.cloud.google.com).
* If the APIs & services page isn't already open, open the console left side menu and select APIs & services.
* On the left, click Credentials.
* Click New Credentials, then select OAuth client ID.
* Select "Android" as the Application type
* 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
* 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
* Click Create client ID
* 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.
* Go to the Google API Console [OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent) page.
* Add required information like a product name and support email address.
* Click Add Scope.
* 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.)
* Finish the remaining steps of the OAuth consent screen setup.
* 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)):
* Add the dependency ```implementation 'com.google.android.gms:play-services-auth:16+'``` to the ```build.gradle``` file
* If you are using [ProGuard](https://www.guardsquare.com/en/products/proguard), add it to your app’s build.gradle file with
```java
buildTypes {
release {
...
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
```
* And add the following to your app’s proguard-rules.pro file:
```java
-keep class com.google.android.gms.common.** { *; }
-keep class com.google.android.gms.auth.** { *; }
-keep class com.google.android.gms.tasks.** { *; }
```
3. Register the iOS part of your Flutter Application
* 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)):
* In the navigation menu of the Google Cloud Platform console, go to APIs & Services > Credentials.
* Select the desired project, then click Create Credentials > Service account.
* Under Service account details, type a name for the new account, then click Create.
* On the Service account permissions page, go to the Select a role dropdown. Select Service Accounts > Service Account Token Creator, then click Continue.
* On the Grant users access to this service account page, click Done. This takes you back to APIs & Services > Credentials.
* On the Credentials page, scroll down to the Service Accounts section and click the name of the account you just created.
* On the Service account details page, scroll down to the Keys section and select Add Key > Create new key.
* Select JSON as the key type and click Create. This downloads a JSON file containing the private key to your machine.
* Add the contents of the JSON file you just downloaded to the iOS part of your Flutter application:
* Rename the file to ```cloudAnchorKey.json```
* Move or copy ```cloudAnchorKey.json``` into the example/ios/Runner directory.
* Open Xcode, then right-click on Runner directory and select Add Files to "Runner".
* Select ```cloudAnchorKey.json``` from the file manager.
* A dialog will show up and ask you to select the targets, select the Runner target.
## Set up Firebase
Google'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):
1. Create a new project in the [Firebase console](https://console.firebase.google.com/project/_/overview)
2. Register the Android part of your Flutter Application (based on the [FlutterFire Android Installation Guide](https://firebase.flutter.dev/docs/installation/android/)):
* 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```
* 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
* 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
* 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
* Add ```apply plugin: 'com.google.gms.google-services'``` to the app-level ```build.gradle```file of the Android part of your Flutter application
3. Register the iOS part of your Flutter Application (based on the [FlutterFire iOS Installation Guide](https://firebase.flutter.dev/docs/installation/ios/)):
* 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.
* Download the file ```GoogleService-Info.plist```
* Move or copy ``` GoogleService-Info.plist``` into the example/ios/Runner directory.
* Open Xcode, then right-click on Runner directory and select Add Files to "Runner".
* Select ``` GoogleService-Info.plist``` from the file manager.
* A dialog will show up and ask you to select the targets, select the Runner target.
4. 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)
## Set up Location Services
* 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:
```
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to location when open.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs access to location when in the background.</string>
```
================================================
FILE: example/.gitignore
================================================
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
================================================
FILE: example/.metadata
================================================
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 4b50ca7f7fbf56be72e54cd200825b760416a356
channel: beta
project_type: app
================================================
FILE: example/Models/Chicken_01/Chicken_01.gltf
================================================
{
"accessors" : [
{
"bufferView" : 0,
"byteOffset" : 0,
"componentType" : 5123,
"count" : 558,
"max" : [ 557 ],
"min" : [ 0 ],
"name" : "buffer-0-accessor-indices-buffer-0-mesh-0",
"type" : "SCALAR"
},
{
"bufferView" : 2,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 558,
"max" : [ 3.636018991470337, 152.5314636230469, 49.29042816162109 ],
"min" : [ -87.20170593261719, 0, -49.29042816162109 ],
"name" : "buffer-0-accessor-position-buffer-0-mesh-0",
"type" : "VEC3"
},
{
"bufferView" : 2,
"byteOffset" : 6696,
"componentType" : 5126,
"count" : 558,
"max" : [ 0.9146999716758728, 0.9926000237464905, 0.9937000274658203 ],
"min" : [ -0.9735000133514404, -1, -0.9937000274658203 ],
"name" : "buffer-0-accessor-normal-buffer-0-mesh-0",
"type" : "VEC3"
},
{
"bufferView" : 1,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 558,
"max" : [ 1, 0 ],
"min" : [ 0, -1 ],
"name" : "buffer-0-accessor-texcoord-buffer-0-mesh-0",
"type" : "VEC2"
},
{
"bufferView" : 3,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 0,
"max" : [ 0, 0, 0, 0 ],
"min" : [ 0, 0, 0, 0 ],
"name" : "buffer-0-accessor-color-buffer-0-mesh-0",
"type" : "VEC4"
},
{
"bufferView" : 0,
"byteOffset" : 1116,
"componentType" : 5123,
"count" : 414,
"max" : [ 413 ],
"min" : [ 0 ],
"name" : "buffer-0-accessor-indices-buffer-0-mesh-0",
"type" : "SCALAR"
},
{
"bufferView" : 2,
"byteOffset" : 13392,
"componentType" : 5126,
"count" : 414,
"max" : [ 80.11344909667969, 176.5484161376953, 40.12937164306641 ],
"min" : [ -75.36231994628906, 0, -40.12934494018555 ],
"name" : "buffer-0-accessor-position-buffer-0-mesh-0",
"type" : "VEC3"
},
{
"bufferView" : 2,
"byteOffset" : 18360,
"componentType" : 5126,
"count" : 414,
"max" : [ 0.9840999841690063, 0.9950000047683716, 1 ],
"min" : [ -0.942300021648407, -0.9502999782562256, -1 ],
"name" : "buffer-0-accessor-normal-buffer-0-mesh-0",
"type" : "VEC3"
},
{
"bufferView" : 1,
"byteOffset" : 4464,
"componentType" : 5126,
"count" : 414,
"max" : [ 1, 0 ],
"min" : [ 0, -1 ],
"name" : "buffer-0-accessor-texcoord-buffer-0-mesh-0",
"type" : "VEC2"
},
{
"bufferView" : 3,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 0,
"max" : [ 0, 0, 0, 0 ],
"min" : [ 0, 0, 0, 0 ],
"name" : "buffer-0-accessor-color-buffer-0-mesh-0",
"type" : "VEC4"
},
{
"bufferView" : 0,
"byteOffset" : 1944,
"componentType" : 5123,
"count" : 144,
"max" : [ 143 ],
"min" : [ 0 ],
"name" : "buffer-0-accessor-indices-buffer-0-mesh-0",
"type" : "SCALAR"
},
{
"bufferView" : 2,
"byteOffset" : 23328,
"componentType" : 5126,
"count" : 144,
"max" : [ 0, 156.3618316650391, 14.85952568054199 ],
"min" : [ -65.07901763916016, 0, -14.85947513580322 ],
"name" : "buffer-0-accessor-position-buffer-0-mesh-0",
"type" : "VEC3"
},
{
"bufferView" : 2,
"byteOffset" : 25056,
"componentType" : 5126,
"count" : 144,
"max" : [ 0.9239000082015991, 0.9239000082015991, 1 ],
"min" : [ -0.9239000082015991, -0.9239000082015991, -1 ],
"name" : "buffer-0-accessor-normal-buffer-0-mesh-0",
"type" : "VEC3"
},
{
"bufferView" : 1,
"byteOffset" : 7776,
"componentType" : 5126,
"count" : 144,
"max" : [ 1, 0 ],
"min" : [ 0, -1 ],
"name" : "buffer-0-accessor-texcoord-buffer-0-mesh-0",
"type" : "VEC2"
},
{
"bufferView" : 3,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 0,
"max" : [ 0, 0, 0, 0 ],
"min" : [ 0, 0, 0, 0 ],
"name" : "buffer-0-accessor-color-buffer-0-mesh-0",
"type" : "VEC4"
},
{
"bufferView" : 0,
"byteOffset" : 2232,
"componentType" : 5123,
"count" : 504,
"max" : [ 503 ],
"min" : [ 0 ],
"name" : "buffer-0-accessor-indices-buffer-0-mesh-0",
"type" : "SCALAR"
},
{
"bufferView" : 2,
"byteOffset" : 26784,
"componentType" : 5126,
"count" : 504,
"max" : [ 0, 190.3115692138672, 10.57294273376465 ],
"min" : [ -84.04743957519531, 0, -10.57290267944336 ],
"name" : "buffer-0-accessor-position-buffer-0-mesh-0",
"type" : "VEC3"
},
{
"bufferView" : 2,
"byteOffset" : 32832,
"componentType" : 5126,
"count" : 504,
"max" : [ 0.9994000196456909, 0.998199999332428, 0.9961000084877014 ],
"min" : [ -1, -0.9664000272750854, -0.9973000288009644 ],
"name" : "buffer-0-accessor-normal-buffer-0-mesh-0",
"type" : "VEC3"
},
{
"bufferView" : 1,
"byteOffset" : 8928,
"componentType" : 5126,
"count" : 504,
"max" : [ 1, 0 ],
"min" : [ 0, -1 ],
"name" : "buffer-0-accessor-texcoord-buffer-0-mesh-0",
"type" : "VEC2"
},
{
"bufferView" : 3,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 0,
"max" : [ 0, 0, 0, 0 ],
"min" : [ 0, 0, 0, 0 ],
"name" : "buffer-0-accessor-color-buffer-0-mesh-0",
"type" : "VEC4"
},
{
"bufferView" : 0,
"byteOffset" : 3240,
"componentType" : 5123,
"count" : 324,
"max" : [ 323 ],
"min" : [ 0 ],
"name" : "buffer-0-accessor-indices-buffer-0-mesh-0",
"type" : "SCALAR"
},
{
"bufferView" : 2,
"byteOffset" : 38880,
"componentType" : 5126,
"count" : 324,
"max" : [ 6.280783176422119, 4.014040946960449, 51.71989440917969 ],
"min" : [ -26.94760704040527, 0, -51.71989440917969 ],
"name" : "buffer-0-accessor-position-buffer-0-mesh-0",
"type" : "VEC3"
},
{
"bufferView" : 2,
"byteOffset" : 42768,
"componentType" : 5126,
"count" : 324,
"max" : [ 0.9013000130653381, 0.9991000294685364, 0.9283000230789185 ],
"min" : [ -0.9253000020980835, -0.9991000294685364, -0.9283000230789185 ],
"name" : "buffer-0-accessor-normal-buffer-0-mesh-0",
"type" : "VEC3"
},
{
"bufferView" : 1,
"byteOffset" : 12960,
"componentType" : 5126,
"count" : 324,
"max" : [ 1, 0 ],
"min" : [ 0, -1 ],
"name" : "buffer-0-accessor-texcoord-buffer-0-mesh-0",
"type" : "VEC2"
},
{
"bufferView" : 3,
"byteOffset" : 0,
"componentType" : 5126,
"count" : 0,
"max" : [ 0, 0, 0, 0 ],
"min" : [ 0, 0, 0, 0 ],
"name" : "buffer-0-accessor-color-buffer-0-mesh-0",
"type" : "VEC4"
}
],
"asset" : {
"extras" : {
"GOOGLE_processor_version" : 293297437
},
"generator" : "Obj2GltfConverter",
"version" : "2.0"
},
"bufferViews" : [
{
"buffer" : 0,
"byteLength" : 3888,
"byteOffset" : 0,
"byteStride" : 0,
"name" : "buffer-0-bufferview-ushort",
"target" : 34963
},
{
"buffer" : 0,
"byteLength" : 15552,
"byteOffset" : 3888,
"byteStride" : 8,
"name" : "buffer-0-bufferview-vec2",
"target" : 34962
},
{
"buffer" : 0,
"byteLength" : 46656,
"byteOffset" : 19440,
"byteStride" : 12,
"name" : "buffer-0-bufferview-vec3",
"target" : 34962
},
{
"buffer" : 0,
"byteLength" : 1,
"byteOffset" : 0,
"name" : "buffer-0-bufferview-vec4"
}
],
"buffers" : [
{
"byteLength" : 66096,
"name" : "buffer-0",
"uri" : "Chicken_01.bin"
}
],
"cameras" : [
{
"perspective" : {
"yfov" : 0.7853981633974483,
"znear" : 0.1000000014901161
},
"type" : "perspective"
}
],
"extensions" : {
"KHR_lights_punctual" : {
"lights" : [
{
"color" : [ 1, 0.9333333333333333, 0.8666666666666667 ],
"intensity" : 0.25,
"name" : "headLight",
"type" : "directional"
},
{
"color" : [ 1, 0.9333333333333333, 0.8666666666666667 ],
"intensity" : 0.324999988079071,
"name" : "keyLight",
"type" : "directional"
}
]
}
},
"extensionsUsed" : [ "GOOGLE_backgrounds", "GOOGLE_camera_settings", "KHR_lights_punctual" ],
"extras" : {
"GOOGLE_initial_camera_motion" : {
"motionPath" : "FULL_ROTATION"
},
"GOOGLE_lighting_rig" : {
"disableShadows" : false
}
},
"materials" : [
{
"alphaMode" : "OPAQUE",
"doubleSided" : true,
"extras" : {
"__iag_" : true
},
"name" : "FF9800",
"pbrMetallicRoughness" : {
"baseColorFactor" : [ 1, 0.355308982084, 0, 1 ],
"metallicFactor" : 0,
"roughnessFactor" : 0.7493216756721951
}
},
{
"alphaMode" : "OPAQUE",
"doubleSided" : true,
"extras" : {
"__iag_" : true
},
"name" : "FFFFFF",
"pbrMetallicRoughness" : {
"baseColorFactor" : [ 1, 1, 1, 1 ],
"metallicFactor" : 0,
"roughnessFactor" : 0.7493216756721951
}
},
{
"alphaMode" : "OPAQUE",
"doubleSided" : true,
"extras" : {
"__iag_" : true
},
"name" : "1A1A1A",
"pbrMetallicRoughness" : {
"baseColorFactor" : [ 0.010396045521, 0.010396045521, 0.010396045521, 1 ],
"metallicFactor" : 0,
"roughnessFactor" : 0.7493216756721951
}
},
{
"alphaMode" : "OPAQUE",
"doubleSided" : true,
"extras" : {
"__iag_" : true
},
"name" : "F44336",
"pbrMetallicRoughness" : {
"baseColorFactor" : [ 0.915586800769, 0.06903493502500001, 0.044844415225, 1 ],
"metallicFactor" : 0,
"roughnessFactor" : 0.7493216756721951
}
},
{
"alphaMode" : "OPAQUE",
"doubleSided" : true,
"extras" : {
"__iag_" : true
},
"name" : "455A64",
"pbrMetallicRoughness" : {
"baseColorFactor" : [ 0.07321786574399999, 0.124567349481, 0.153787112649, 1 ],
"metallicFactor" : 0,
"roughnessFactor" : 0.7493216756721951
}
}
],
"meshes" : [
{
"name" : "buffer-0-mesh-0",
"primitives" : [
{
"attributes" : {
"NORMAL" : 2,
"POSITION" : 1,
"TEXCOORD_0" : 3
},
"indices" : 0,
"material" : 0,
"mode" : 4
},
{
"attributes" : {
"NORMAL" : 7,
"POSITION" : 6,
"TEXCOORD_0" : 8
},
"indices" : 5,
"material" : 1,
"mode" : 4
},
{
"attributes" : {
"NORMAL" : 12,
"POSITION" : 11,
"TEXCOORD_0" : 13
},
"indices" : 10,
"material" : 2,
"mode" : 4
},
{
"attributes" : {
"NORMAL" : 17,
"POSITION" : 16,
"TEXCOORD_0" : 18
},
"indices" : 15,
"material" : 3,
"mode" : 4
},
{
"attributes" : {
"NORMAL" : 22,
"POSITION" : 21,
"TEXCOORD_0" : 23
},
"indices" : 20,
"material" : 4,
"mode" : 4
}
]
}
],
"nodes" : [
{
"mesh" : 0,
"name" : "node-0"
},
{
"children" : [ 2 ]
},
{
"children" : [ 3 ],
"extras" : {
"isTransformNode" : 1
},
"translation" : [ -10.33925953243352, 101.4766894350761, 0.4725487306714168 ]
},
{
"children" : [ 0 ],
"translation" : [ 10.33925953243352, -101.4766894350761, -0.4725487306714168 ]
},
{
"camera" : 0,
"children" : [ 5 ],
"rotation" : [
0.03161089902743668,
0.9405290359497446,
0.091286676120003,
-0.3256879278712723
],
"translation" : [ -209.7092493748044, 164.720177932136, -253.0129743638226 ]
},
{
"extensions" : {
"KHR_lights_punctual" : {
"light" : 0
}
},
"extras" : {
"isHeadLight" : 1
},
"name" : "headLightNode",
"translation" : [ -137.6435827985261, 68.82179139926303, 68.82179139926303 ]
},
{
"extensions" : {
"KHR_lights_punctual" : {
"light" : 1
}
},
"extras" : {
"isKeyLight" : 1
},
"name" : "keyLightNode",
"rotation" : [ -0.7505326882858236, -0.3752663441429118, 0, 0.5439447166468927 ],
"scale" : [ 0, 0, 139.0200186265113 ],
"translation" : [ -169.8998424664395, 356.8894572597737, -139.0250173948788 ]
}
],
"scene" : 0,
"scenes" : [
{
"extensions" : {
"GOOGLE_backgrounds" : {
"color" : [ 0.803921568627451, 0.8627450980392157, 0.2235294117647059 ]
},
"GOOGLE_camera_settings" : {}
},
"extras" : {
"GOOGLE_camera_index" : {
"cameraIndex" : 0,
"nodeIndex" : 4
},
"GOOGLE_geometry_data" : {
"stats" : {
"centroid" : [ -30.87982383992815, 78.84942000675105, -0.004998768367502245 ],
"radius" : 137.6435827985261,
"stdev" : 86.13574977146493
},
"visualCenterPoint" : [ -10.33925953243352, 101.4766894350761, 0.4725487306714168 ]
},
"GOOGLE_hemi_light" : {
"groundColor" : [ 0.803921568627451, 0.8627450980392157, 0.2235294117647059 ]
}
},
"name" : "scene-0",
"nodes" : [ 1, 4, 6 ]
}
]
}
================================================
FILE: example/Models/Chicken_01/license.txt
================================================
Licensed under CC-BY 3.0 by "Poly by Google" (https://poly.google.com/user/4aEd8rQgKu2)
================================================
FILE: example/README.md
================================================
# ar_flutter_plugin_example
Demonstrates how to use the ar_flutter_plugin plugin to create collaborative AR experiences.
## Contributing
Contributions 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).
================================================
FILE: example/android/.gitignore
================================================
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
================================================
FILE: example/android/app/build.gradle
================================================
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "io.carius.lars.ar_flutter_plugin_example"
minSdkVersion 24
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
apply plugin: 'com.google.gms.google-services'
================================================
FILE: example/android/app/src/debug/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.carius.lars.ar_flutter_plugin_example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
================================================
FILE: example/android/app/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.carius.lars.ar_flutter_plugin_example">
<application
android:label="ar_flutter_plugin_example"
android:icon="@mipmap/ic_launcher">
<activity
android:exported="true"
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
================================================
FILE: example/android/app/src/main/kotlin/io/carius/lars/ar_flutter_plugin_example/MainActivity.kt
================================================
package io.carius.lars.ar_flutter_plugin_example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
================================================
FILE: example/android/app/src/main/res/drawable/launch_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
================================================
FILE: example/android/app/src/main/res/drawable-v21/launch_background.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
================================================
FILE: example/android/app/src/main/res/values/styles.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
================================================
FILE: example/android/app/src/main/res/values-night/styles.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
================================================
FILE: example/android/app/src/profile/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.carius.lars.ar_flutter_plugin_example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
================================================
FILE: example/android/build.gradle
================================================
buildscript {
ext.kotlin_version = '1.6.10'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.8'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: example/android/gradle/wrapper/gradle-wrapper.properties
================================================
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
================================================
FILE: example/android/gradle.properties
================================================
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
================================================
FILE: example/android/settings.gradle
================================================
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
================================================
FILE: example/ios/.gitignore
================================================
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
================================================
FILE: example/ios/Flutter/AppFrameworkInfo.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>13.0</string>
</dict>
</plist>
================================================
FILE: example/ios/Flutter/Debug.xcconfig
================================================
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
================================================
FILE: example/ios/Flutter/Release.xcconfig
================================================
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
================================================
FILE: example/ios/Podfile
================================================
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# Override firebase SDK version for compatibility reasons
$FirebaseSDKVersion = '8.7.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
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/)
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: PermissionGroup.camera
'PERMISSION_CAMERA=1',
## dart: PermissionGroup.photos
'PERMISSION_PHOTOS=1',
## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
'PERMISSION_LOCATION=1',
## dart: PermissionGroup.sensors
'PERMISSION_SENSORS=1',
## dart: PermissionGroup.bluetooth
'PERMISSION_BLUETOOTH=1',
]
end
end
end
================================================
FILE: example/ios/Runner/AppDelegate.swift
================================================
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
================================================
FILE: example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: example/ios/Runner/Assets.xcassets/Contents.json
================================================
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
================================================
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
================================================
FILE: example/ios/Runner/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
================================================
FILE: example/ios/Runner/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
================================================
FILE: example/ios/Runner/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>ar_flutter_plugin_example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
<string></string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to location when open.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This app needs access to location when in the background.</string>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
</dict>
</plist>
================================================
FILE: example/ios/Runner/Runner-Bridging-Header.h
================================================
#import "GeneratedPluginRegistrant.h"
================================================
FILE: example/ios/Runner.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
EF5AB5D5261C9BD900A60388 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = EF5AB5D4261C9BD900A60388 /* GoogleService-Info.plist */; };
EF5AB736261F5EA600A60388 /* cloudAnchorKey.json in Resources */ = {isa = PBXBuildFile; fileRef = EF5AB735261F5EA600A60388 /* cloudAnchorKey.json */; };
FC3B1579A1F9BBE343269DEF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A1F47AAC378FB2E799B13D8 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0181363DB89EBAFA9858B1A3 /* 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>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3A1F47AAC378FB2E799B13D8 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D424E5FE727DFB8DCB6BF1D8 /* 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>"; };
EF5AB5D4261C9BD900A60388 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
EF5AB5D6261DA4D800A60388 /* ARCoreCloudAnchors.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ARCoreCloudAnchors.framework; path = Pods/ARCore/CloudAnchors/Frameworks/ARCoreCloudAnchors.framework; sourceTree = "<group>"; };
EF5AB5D8261DA54800A60388 /* ARCore */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ARCore; path = Pods/ARCore; sourceTree = "<group>"; };
EF5AB735261F5EA600A60388 /* cloudAnchorKey.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = cloudAnchorKey.json; sourceTree = "<group>"; };
F6A0B71292B6059CA94A3F18 /* 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>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
FC3B1579A1F9BBE343269DEF /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
4E95B0BED358A7C62F0A1158 /* Frameworks */ = {
isa = PBXGroup;
children = (
EF5AB5D8261DA54800A60388 /* ARCore */,
EF5AB5D6261DA4D800A60388 /* ARCoreCloudAnchors.framework */,
3A1F47AAC378FB2E799B13D8 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
9B7184BFA5C5D8AB5FFB1988 /* Pods */,
4E95B0BED358A7C62F0A1158 /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
EF5AB735261F5EA600A60388 /* cloudAnchorKey.json */,
EF5AB5D4261C9BD900A60388 /* GoogleService-Info.plist */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
9B7184BFA5C5D8AB5FFB1988 /* Pods */ = {
isa = PBXGroup;
children = (
D424E5FE727DFB8DCB6BF1D8 /* Pods-Runner.debug.xcconfig */,
F6A0B71292B6059CA94A3F18 /* Pods-Runner.release.xcconfig */,
0181363DB89EBAFA9858B1A3 /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
B6180CDA50A54DB55045479E /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
94B0826727C2A055D78A9B66 /* [CP] Embed Pods Frameworks */,
C226EEF590F236CBFE1746B9 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
EF5AB736261F5EA600A60388 /* cloudAnchorKey.json in Resources */,
EF5AB5D5261C9BD900A60388 /* GoogleService-Info.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
94B0826727C2A055D78A9B66 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
B6180CDA50A54DB55045479E /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
C226EEF590F236CBFE1746B9 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 5TD7B79GK6;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Pods/ARCore/CloudAnchors/Frameworks",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.0.1;
PRODUCT_BUNDLE_IDENTIFIER = io.carius.lars.arFlutterPluginExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 5TD7B79GK6;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Pods/ARCore/CloudAnchors/Frameworks",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.0.1;
PRODUCT_BUNDLE_IDENTIFIER = io.carius.lars.arFlutterPluginExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 5TD7B79GK6;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Pods/ARCore/CloudAnchors/Frameworks",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 0.0.1;
PRODUCT_BUNDLE_IDENTIFIER = io.carius.lars.arFlutterPluginExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
================================================
FILE: example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
================================================
FILE: example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
================================================
FILE: example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
================================================
FILE: example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: example/ios/Runner.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
================================================
FILE: example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
================================================
FILE: example/lib/examples/cloudanchorexample.dart
================================================
import 'dart:convert';
import 'package:ar_flutter_plugin/managers/ar_location_manager.dart';
import 'package:ar_flutter_plugin/managers/ar_session_manager.dart';
import 'package:ar_flutter_plugin/managers/ar_object_manager.dart';
import 'package:ar_flutter_plugin/managers/ar_anchor_manager.dart';
import 'package:ar_flutter_plugin/models/ar_anchor.dart';
import 'package:flutter/material.dart';
import 'package:ar_flutter_plugin/ar_flutter_plugin.dart';
import 'package:ar_flutter_plugin/datatypes/config_planedetection.dart';
import 'package:ar_flutter_plugin/datatypes/node_types.dart';
import 'package:ar_flutter_plugin/datatypes/hittest_result_types.dart';
import 'package:ar_flutter_plugin/models/ar_node.dart';
import 'package:ar_flutter_plugin/models/ar_hittest_result.dart';
import 'package:vector_math/vector_math_64.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:geoflutterfire/geoflutterfire.dart';
import 'package:geolocator/geolocator.dart';
class CloudAnchorWidget extends StatefulWidget {
CloudAnchorWidget({Key? key}) : super(key: key);
@override
_CloudAnchorWidgetState createState() => _CloudAnchorWidgetState();
}
class _CloudAnchorWidgetState extends State<CloudAnchorWidget> {
// Firebase stuff
bool _initialized = false;
bool _error = false;
FirebaseManager firebaseManager = FirebaseManager();
Map<String, Map> anchorsInDownloadProgress = Map<String, Map>();
ARSessionManager? arSessionManager;
ARObjectManager? arObjectManager;
ARAnchorManager? arAnchorManager;
ARLocationManager? arLocationManager;
List<ARNode> nodes = [];
List<ARAnchor> anchors = [];
String lastUploadedAnchor = "";
bool readyToUpload = false;
bool readyToDownload = true;
@override
void initState() {
firebaseManager.initializeFlutterFire().then((value) => setState(() {
_initialized = value;
_error = !value;
}));
super.initState();
}
@override
void dispose() {
super.dispose();
arSessionManager!.dispose();
}
@override
Widget build(BuildContext context) {
// Show error message if initialization failed
if (_error) {
return Scaffold(
appBar: AppBar(
title: const Text('Cloud Anchors'),
),
body: Container(
child: Center(
child: Column(
children: [
Text("Firebase initialization failed"),
ElevatedButton(
child: Text("Retry"), onPressed: () => {initState()})
],
))));
}
// Show a loader until FlutterFire is initialized
if (!_initialized) {
return Scaffold(
appBar: AppBar(
title: const Text('Cloud Anchors'),
),
body: Container(
child: Center(
child: Column(children: [
CircularProgressIndicator(),
Text("Initializing Firebase")
]))));
}
return Scaffold(
appBar: AppBar(
title: const Text('Cloud Anchors'),
),
body: Container(
child: Stack(children: [
ARView(
onARViewCreated: onARViewCreated,
planeDetectionConfig: PlaneDetectionConfig.horizontalAndVertical,
),
Align(
alignment: FractionalOffset.bottomCenter,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: onRemoveEverything,
child: Text("Remove Everything")),
]),
),
Align(
alignment: FractionalOffset.topCenter,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Visibility(
visible: readyToUpload,
child: ElevatedButton(
onPressed: onUploadButtonPressed,
child: Text("Upload"))),
Visibility(
visible: readyToDownload,
child: ElevatedButton(
onPressed: onDownloadButtonPressed,
child: Text("Download"))),
]),
)
])));
}
void onARViewCreated(
ARSessionManager arSessionManager,
ARObjectManager arObjectManager,
ARAnchorManager arAnchorManager,
ARLocationManager arLocationManager) {
this.arSessionManager = arSessionManager;
this.arObjectManager = arObjectManager;
this.arAnchorManager = arAnchorManager;
this.arLocationManager = arLocationManager;
this.arSessionManager!.onInitialize(
showFeaturePoints: false,
showPlanes: true,
customPlaneTexturePath: "Images/triangle.png",
showWorldOrigin: true,
);
this.arObjectManager!.onInitialize();
this.arAnchorManager!.initGoogleCloudAnchorMode();
this.arSessionManager!.onPlaneOrPointTap = onPlaneOrPointTapped;
this.arObjectManager!.onNodeTap = onNodeTapped;
this.arAnchorManager!.onAnchorUploaded = onAnchorUploaded;
this.arAnchorManager!.onAnchorDownloaded = onAnchorDownloaded;
this
.arLocationManager
!.startLocationUpdates()
.then((value) => null)
.onError((error, stackTrace) {
switch (error.toString()) {
case 'Location services disabled':
{
showAlertDialog(
context,
"Action Required",
"To use cloud anchor functionality, please enable your location services",
"Settings",
this.arLocationManager!.openLocationServicesSettings,
"Cancel");
break;
}
case 'Location permissions denied':
{
showAlertDialog(
context,
"Action Required",
"To use cloud anchor functionality, please allow the app to access your device's location",
"Retry",
this.arLocationManager!.startLocationUpdates,
"Cancel");
break;
}
case 'Location permissions permanently denied':
{
showAlertDialog(
context,
"Action Required",
"To use cloud anchor functionality, please allow the app to access your device's location",
"Settings",
this.arLocationManager!.openAppPermissionSettings,
"Cancel");
break;
}
default:
{
this.arSessionManager!.onError(error.toString());
break;
}
}
this.arSessionManager!.onError(error.toString());
});
}
Future<void> onRemoveEverything() async {
anchors.forEach((anchor) {
this.arAnchorManager!.removeAnchor(anchor);
});
anchors = [];
if (lastUploadedAnchor != "") {
setState(() {
readyToDownload = true;
readyToUpload = false;
});
} else {
setState(() {
readyToDownload = true;
readyToUpload = false;
});
}
}
Future<void> onNodeTapped(List<String> nodeNames) async {
var foregroundNode =
nodes.firstWhere((element) => element.name == nodeNames.first);
this.arSessionManager!.onError(foregroundNode.data!["onTapText"]);
}
Future<void> onPlaneOrPointTapped(
List<ARHitTestResult> hitTestResults) async {
var singleHitTestResult = hitTestResults.firstWhere(
(hitTestResult) => hitTestResult.type == ARHitTestResultType.plane);
if (singleHitTestResult != null) {
var newAnchor = ARPlaneAnchor(
transformation: singleHitTestResult.worldTransform, ttl: 2);
bool? didAddAnchor = await this.arAnchorManager!.addAnchor(newAnchor);
if (didAddAnchor ?? false) {
this.anchors.add(newAnchor);
// Add note to anchor
var newNode = ARNode(
type: NodeType.webGLB,
uri: "https://github.com/KhronosGroup/glTF-Sample-Models/raw/master/2.0/Duck/glTF-Binary/Duck.glb",
scale: Vector3(0.2, 0.2, 0.2),
position: Vector3(0.0, 0.0, 0.0),
rotation: Vector4(1.0, 0.0, 0.0, 0.0),
data: {"onTapText": "Ouch, that hurt!"});
bool? didAddNodeToAnchor =
await this.arObjectManager!.addNode(newNode, planeAnchor: newAnchor);
if (didAddNodeToAnchor ?? false) {
this.nodes.add(newNode);
setState(() {
readyToUpload = true;
});
} else {
this.arSessionManager!.onError("Adding Node to Anchor failed");
}
} else {
this.arSessionManager!.onError("Adding Anchor failed");
}
}
}
Future<void> onUploadButtonPressed() async {
this.arAnchorManager!.uploadAnchor(this.anchors.last);
setState(() {
readyToUpload = false;
});
}
onAnchorUploaded(ARAnchor anchor) {
// Upload anchor information to firebase
firebaseManager.uploadAnchor(anchor,
currentLocation: this.arLocationManager!.currentLocation);
// Upload child nodes to firebase
if (anchor is ARPlaneAnchor) {
anchor.childNodes.forEach((nodeName) => firebaseManager.uploadObject(
nodes.firstWhere((element) => element.name == nodeName)));
}
setState(() {
readyToDownload = true;
readyToUpload = false;
});
this.arSessionManager!.onError("Upload successful");
}
ARAnchor onAnchorDownloaded(Map<String, dynamic>serializedAnchor) {
final anchor = ARPlaneAnchor.fromJson(anchorsInDownloadProgress[serializedAnchor["cloudanchorid"]] as Map<String, dynamic>);
anchorsInDownloadProgress.remove(anchor.cloudanchorid);
this.anchors.add(anchor);
// Download nodes attached to this anchor
firebaseManager.getObjectsFromAnchor(anchor, (snapshot) {
snapshot.docs.forEach((objectDoc) {
ARNode object = ARNode.fromMap(objectDoc.data() as Map<String, dynamic>);
arObjectManager!.addNode(object, planeAnchor: anchor);
this.nodes.add(object);
});
});
return anchor;
}
Future<void> onDownloadButtonPressed() async {
//this.arAnchorManager.downloadAnchor(lastUploadedAnchor);
//firebaseManager.downloadLatestAnchor((snapshot) {
// final cloudAnchorId = snapshot.docs.first.get("cloudanchorid");
// anchorsInDownloadProgress[cloudAnchorId] = snapshot.docs.first.data();
// arAnchorManager.downloadAnchor(cloudAnchorId);
//});
// Get anchors within a radius of 100m of the current device's location
if (this.arLocationManager!.currentLocation != null) {
firebaseManager.downloadAnchorsByLocation((snapshot) {
final cloudAnchorId = snapshot.get("cloudanchorid");
anchorsInDownloadProgress[cloudAnchorId] = snapshot.data() as Map<String, dynamic>;
arAnchorManager!.downloadAnchor(cloudAnchorId);
}, this.arLocationManager!.currentLocation, 0.1);
setState(() {
readyToDownload = false;
});
} else {
this
.arSessionManager!
.onError("Location updates not running, can't download anchors");
}
}
void showAlertDialog(BuildContext context, String title, String content,
String buttonText, Function buttonFunction, String cancelButtonText) {
// set up the buttons
Widget cancelButton = ElevatedButton(
child: Text(cancelButtonText),
onPressed: () {
Navigator.of(context).pop();
},
);
Widget actionButton = ElevatedButton(
child: Text(buttonText),
onPressed: () {
buttonFunction();
Navigator.of(context).pop();
},
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text(title),
content: Text(content),
actions: [
cancelButton,
actionButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
}
// Class for managing interaction with Firebase (in your own app, this can be put in a separate file to keep everything clean and tidy)
typedef FirebaseListener = void Function(QuerySnapshot snapshot);
typedef FirebaseDocumentStreamListener = void Function(
DocumentSnapshot snapshot);
class FirebaseManager {
FirebaseFirestore? firestore;
Geoflutterfire? geo;
CollectionReference? anchorCollection;
CollectionReference? objectCollection;
// Firebase initialization function
Future<bool> initializeFlutterFire() async {
try {
// Wait for Firebase to initialize
await Firebase.initializeApp();
geo = Geoflutterfire();
firestore = FirebaseFirestore.instance;
anchorCollection = FirebaseFirestore.instance.collection('anchors');
objectCollection = FirebaseFirestore.instance.collection('objects');
return true;
} catch (e) {
return false;
}
}
void uploadAnchor(ARAnchor anchor, {Position? currentLocation}) {
if (firestore == null) return;
var serializedAnchor = anchor.toJson();
var expirationTime = DateTime.now().millisecondsSinceEpoch / 1000 +
serializedAnchor["ttl"] * 24 * 60 * 60;
serializedAnchor["expirationTime"] = expirationTime;
// Add location
if (currentLocation != null) {
GeoFirePoint myLocation = geo!.point(
latitude: currentLocation.latitude,
longitude: currentLocation.longitude);
serializedAnchor["position"] = myLocation.data;
}
anchorCollection!
.add(serializedAnchor)
.then((value) =>
print("Successfully added anchor: " + serializedAnchor["name"]))
.catchError((error) => print("Failed to add anchor: $error"));
}
void uploadObject(ARNode node) {
if (firestore == null) return;
var serializedNode = node.toMap();
objectCollection!
.add(serializedNode)
.then((value) =>
print("Successfully added object: " + serializedNode["name"]))
.catchError((error) => print("Failed to add object: $error"));
}
void downloadLatestAnchor(FirebaseListener listener) {
anchorCollection!
.orderBy("expirationTime", descending: false)
.limitToLast(1)
.get()
.then((value) => listener(value))
.catchError(
(error) => (error) => print("Failed to download anchor: $error"));
}
void downloadAnchorsByLocation(FirebaseDocumentStreamListener listener,
Position location, double radius) {
GeoFirePoint center =
geo!.point(latitude: location.latitude, longitude: location.longitude);
Stream<List<DocumentSnapshot>> stream = geo!
.collection(collectionRef: anchorCollection!)
.within(center: center, radius: radius, field: 'position');
stream.listen((List<DocumentSnapshot> documentList) {
documentList.forEach((element) {
listener(element);
});
});
}
void downloadAnchorsByChannel() {}
void getObjectsFromAnchor(ARPlaneAnchor anchor, FirebaseListener listener) {
objectCollection!
.where("name", whereIn: anchor.childNodes)
.get()
.then((value) => listener(value))
.catchError((error) => print("Failed to download objects: $error"));
}
void deleteExpiredDatabaseEntries() {
WriteBatch batch = FirebaseFirestore.instance.batch();
anchorCollection!
.where("expirationTime",
isLessThan: DateTime.now().millisecondsSinceEpoch / 1000)
.get()
.then((anchorSnapshot) => anchorSnapshot.docs.forEach((anchorDoc) {
// Delete all objects attached to the expired anchor
objectCollection!
.where("name", arrayContainsAny: anchorDoc.get("childNodes"))
.get()
.then((objectSnapshot) => objectSnapshot.docs.forEach(
(objectDoc) => batch.delete(objectDoc.reference)));
// Delete the expired anchor
batch.delete(anchorDoc.reference);
}));
batch.commit();
}
}
================================================
FILE: example/lib/examples/debugoptionsexample.dart
================================================
import 'package:ar_flutter_plugin/managers/ar_location_manager.dart';
import 'package:ar_flutter_plugin/managers/ar_session_manager.dart';
import 'package:ar_flutter_plugin/managers/ar_object_manager.dart';
import 'package:ar_flutter_plugin/managers/ar_anchor_manager.dart';
import 'package:flutter/material.dart';
import 'package:ar_flutter_plugin/ar_flutter_plugin.dart';
import 'package:ar_flutter_plugin/datatypes/config_planedetection.dart';
class DebugOptionsWidget extends StatefulWidget {
DebugOptionsWidget({Key? key}) : super(key: key);
@override
_DebugOptionsWidgetState createState() => _DebugOptionsWidgetState();
}
class _DebugOptionsWidgetState extends State<DebugOptionsWidget> {
ARSessionManager? arSessionManager;
ARObjectManager? arObjectManager;
bool _showFeaturePoints = false;
bool _showPlanes = false;
bool _showWorldOrigin = false;
bool _showAnimatedGuide = true;
String _planeTexturePath = "Images/triangle.png";
bool _handleTaps = false;
@override
void dispose() {
super.dispose();
arSessionManager!.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Debug Options'),
),
body: Container(
child: Stack(children: [
ARView(
onARViewCreated: onARViewCreated,
planeDetectionConfig: PlaneDetectionConfig.horizontalAndVertical,
showPlatformType: true,
),
Align(
alignment: FractionalOffset.bottomRight,
child: Container(
width: MediaQuery.of(context).size.width * 0.5,
color: Color(0xFFFFFFF).withOpacity(0.5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: [
SwitchListTile(
title: const Text('Feature Points'),
value: _showFeaturePoints,
onChanged: (bool value) {
setState(() {
_showFeaturePoints = value;
updateSessionSettings();
});
},
),
SwitchListTile(
title: const Text('Planes'),
value: _showPlanes,
onChanged: (bool value) {
setState(() {
_showPlanes = value;
updateSessionSettings();
});
},
),
SwitchListTile(
title: const Text('World Origin'),
value: _showWorldOrigin,
onChanged: (bool value) {
setState(() {
_showWorldOrigin = value;
updateSessionSettings();
});
},
),
],
),
),
),
])));
}
void onARViewCreated(
ARSessionManager arSessionManager,
ARObjectManager arObjectManager,
ARAnchorManager arAnchorManager,
ARLocationManager arLocationManager) {
this.arSessionManager = arSessionManager;
this.arObjectManager = arObjectManager;
this.arSessionManager!.onInitialize(
showFeaturePoints: _showFeaturePoints,
showPlanes: _showPlanes,
customPlaneTexturePath: _planeTexturePath,
showWorldOrigin: _showWorldOrigin,
showAnimatedGuide: _showAnimatedGuide,
handleTaps: _handleTaps,
);
this.arObjectManager!.onInitialize();
}
void updateSessionSettings() {
this.arSessionManager!.onInitialize(
showFeaturePoints: _showFeaturePoints,
showPlanes: _showPlanes,
customPlaneTexturePath: _planeTexturePath,
showWorldOrigin: _showWorldOrigin,
);
}
}
================================================
FILE: example/lib/examples/externalmodelmanagemen
gitextract_cy7w_x8f/
├── .gitignore
├── .metadata
├── CHANGELOG.md
├── LICENSE
├── README.md
├── android/
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ ├── settings.gradle
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── kotlin/
│ └── io/
│ └── carius/
│ └── lars/
│ └── ar_flutter_plugin/
│ ├── AndroidARView.kt
│ ├── AndroidARViewFactory.kt
│ ├── ArFlutterPlugin.kt
│ ├── ArModelBuilder.kt
│ ├── CloudAnchorHandler.kt
│ └── Serialization/
│ ├── Deserializers.kt
│ └── Serializers.kt
├── cloudAnchorSetup.md
├── example/
│ ├── .gitignore
│ ├── .metadata
│ ├── Models/
│ │ └── Chicken_01/
│ │ ├── Chicken_01.gltf
│ │ └── license.txt
│ ├── README.md
│ ├── android/
│ │ ├── .gitignore
│ │ ├── app/
│ │ │ ├── build.gradle
│ │ │ └── src/
│ │ │ ├── debug/
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── main/
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── kotlin/
│ │ │ │ │ └── io/
│ │ │ │ │ └── carius/
│ │ │ │ │ └── lars/
│ │ │ │ │ └── ar_flutter_plugin_example/
│ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── res/
│ │ │ │ ├── drawable/
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable-v21/
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── values/
│ │ │ │ │ └── styles.xml
│ │ │ │ └── values-night/
│ │ │ │ └── styles.xml
│ │ │ └── profile/
│ │ │ └── AndroidManifest.xml
│ │ ├── build.gradle
│ │ ├── gradle/
│ │ │ └── wrapper/
│ │ │ └── gradle-wrapper.properties
│ │ ├── gradle.properties
│ │ └── settings.gradle
│ ├── ios/
│ │ ├── .gitignore
│ │ ├── Flutter/
│ │ │ ├── AppFrameworkInfo.plist
│ │ │ ├── Debug.xcconfig
│ │ │ └── Release.xcconfig
│ │ ├── Podfile
│ │ ├── Runner/
│ │ │ ├── AppDelegate.swift
│ │ │ ├── Assets.xcassets/
│ │ │ │ ├── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── Contents.json
│ │ │ │ └── LaunchImage.imageset/
│ │ │ │ ├── Contents.json
│ │ │ │ └── README.md
│ │ │ ├── Base.lproj/
│ │ │ │ ├── LaunchScreen.storyboard
│ │ │ │ └── Main.storyboard
│ │ │ ├── Info.plist
│ │ │ └── Runner-Bridging-Header.h
│ │ ├── Runner.xcodeproj/
│ │ │ ├── project.pbxproj
│ │ │ ├── project.xcworkspace/
│ │ │ │ ├── contents.xcworkspacedata
│ │ │ │ └── xcshareddata/
│ │ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ │ └── WorkspaceSettings.xcsettings
│ │ │ └── xcshareddata/
│ │ │ └── xcschemes/
│ │ │ └── Runner.xcscheme
│ │ └── Runner.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ └── IDEWorkspaceChecks.plist
│ ├── lib/
│ │ ├── examples/
│ │ │ ├── cloudanchorexample.dart
│ │ │ ├── debugoptionsexample.dart
│ │ │ ├── externalmodelmanagementexample.dart
│ │ │ ├── localandwebobjectsexample.dart
│ │ │ ├── objectgesturesexample.dart
│ │ │ ├── objectsonplanesexample.dart
│ │ │ └── screenshotexample.dart
│ │ └── main.dart
│ ├── pubspec.yaml
│ └── test/
│ └── widget_test.dart
├── ios/
│ ├── .gitignore
│ ├── Classes/
│ │ ├── ArFlutterPlugin.h
│ │ ├── ArFlutterPlugin.m
│ │ ├── ArModelBuilder.swift
│ │ ├── CloudAnchorHandler.swift
│ │ ├── IosARView.swift
│ │ ├── IosARViewFactory.swift
│ │ ├── JWTGenerator.swift
│ │ ├── Serialization/
│ │ │ ├── Deserializers.swift
│ │ │ └── Serializers.swift
│ │ └── SwiftArFlutterPlugin.swift
│ └── ar_flutter_plugin.podspec
├── lib/
│ ├── ar_flutter_plugin.dart
│ ├── datatypes/
│ │ ├── anchor_types.dart
│ │ ├── config_planedetection.dart
│ │ ├── hittest_result_types.dart
│ │ └── node_types.dart
│ ├── managers/
│ │ ├── ar_anchor_manager.dart
│ │ ├── ar_location_manager.dart
│ │ ├── ar_object_manager.dart
│ │ └── ar_session_manager.dart
│ ├── models/
│ │ ├── ar_anchor.dart
│ │ ├── ar_hittest_result.dart
│ │ └── ar_node.dart
│ ├── utils/
│ │ └── json_converters.dart
│ └── widgets/
│ └── ar_view.dart
├── pubspec.yaml
└── test/
└── ar_flutter_plugin_test.dart
SYMBOL INDEX (203 symbols across 24 files)
FILE: example/lib/examples/cloudanchorexample.dart
class CloudAnchorWidget (line 21) | class CloudAnchorWidget extends StatefulWidget {
method createState (line 24) | _CloudAnchorWidgetState createState()
class _CloudAnchorWidgetState (line 27) | class _CloudAnchorWidgetState extends State<CloudAnchorWidget> {
method initState (line 47) | void initState()
method dispose (line 57) | void dispose()
method build (line 63) | Widget build(BuildContext context)
method onARViewCreated (line 135) | void onARViewCreated(
method onRemoveEverything (line 211) | Future<void> onRemoveEverything()
method onNodeTapped (line 229) | Future<void> onNodeTapped(List<String> nodeNames)
method onPlaneOrPointTapped (line 235) | Future<void> onPlaneOrPointTapped(
method onUploadButtonPressed (line 269) | Future<void> onUploadButtonPressed()
method onAnchorDownloaded (line 292) | ARAnchor onAnchorDownloaded(Map<String, dynamic>serializedAnchor)
method onDownloadButtonPressed (line 309) | Future<void> onDownloadButtonPressed()
method showAlertDialog (line 334) | void showAlertDialog(BuildContext context, String title, String content,
type FirebaseListener (line 372) | typedef FirebaseListener = void Function(QuerySnapshot snapshot);
type FirebaseDocumentStreamListener (line 373) | typedef FirebaseDocumentStreamListener = void Function(
class FirebaseManager (line 376) | class FirebaseManager {
method initializeFlutterFire (line 383) | Future<bool> initializeFlutterFire()
method uploadAnchor (line 397) | void uploadAnchor(ARAnchor anchor, {Position? currentLocation})
method uploadObject (line 419) | void uploadObject(ARNode node)
method downloadLatestAnchor (line 431) | void downloadLatestAnchor(FirebaseListener listener)
method downloadAnchorsByLocation (line 441) | void downloadAnchorsByLocation(FirebaseDocumentStreamListener listener,
method downloadAnchorsByChannel (line 457) | void downloadAnchorsByChannel()
method getObjectsFromAnchor (line 459) | void getObjectsFromAnchor(ARPlaneAnchor anchor, FirebaseListener liste...
method deleteExpiredDatabaseEntries (line 467) | void deleteExpiredDatabaseEntries()
FILE: example/lib/examples/debugoptionsexample.dart
class DebugOptionsWidget (line 9) | class DebugOptionsWidget extends StatefulWidget {
method createState (line 12) | _DebugOptionsWidgetState createState()
class _DebugOptionsWidgetState (line 15) | class _DebugOptionsWidgetState extends State<DebugOptionsWidget> {
method dispose (line 26) | void dispose()
method build (line 32) | Widget build(BuildContext context)
method onARViewCreated (line 90) | void onARViewCreated(
method updateSessionSettings (line 109) | void updateSessionSettings()
FILE: example/lib/examples/externalmodelmanagementexample.dart
class ExternalModelManagementWidget (line 21) | class ExternalModelManagementWidget extends StatefulWidget {
method createState (line 24) | _ExternalModelManagementWidgetState createState()
class _ExternalModelManagementWidgetState (line 28) | class _ExternalModelManagementWidgetState
method initState (line 54) | void initState()
method dispose (line 64) | void dispose()
method build (line 70) | Widget build(BuildContext context)
method onARViewCreated (line 158) | void onARViewCreated(
method onModelSelected (line 234) | void onModelSelected(AvailableModel model)
method onRemoveEverything (line 242) | Future<void> onRemoveEverything()
method onNodeTapped (line 260) | Future<void> onNodeTapped(List<String> nodeNames)
method onPlaneOrPointTapped (line 265) | Future<void> onPlaneOrPointTapped(
method onUploadButtonPressed (line 299) | Future<void> onUploadButtonPressed()
method onAnchorDownloaded (line 322) | ARAnchor onAnchorDownloaded(Map<String,dynamic> serializedAnchor)
method onDownloadButtonPressed (line 339) | Future<void> onDownloadButtonPressed()
method showAlertDialog (line 364) | void showAlertDialog(BuildContext context, String title, String content,
type FirebaseListener (line 402) | typedef FirebaseListener = void Function(QuerySnapshot snapshot);
type FirebaseDocumentStreamListener (line 403) | typedef FirebaseDocumentStreamListener = void Function(
class FirebaseManager (line 406) | class FirebaseManager {
method initializeFlutterFire (line 414) | Future<bool> initializeFlutterFire()
method uploadAnchor (line 429) | void uploadAnchor(ARAnchor anchor, {Position? currentLocation})
method uploadObject (line 451) | void uploadObject(ARNode node)
method downloadLatestAnchor (line 463) | void downloadLatestAnchor(FirebaseListener listener)
method downloadAnchorsByLocation (line 473) | void downloadAnchorsByLocation(FirebaseDocumentStreamListener listener,
method downloadAnchorsByChannel (line 489) | void downloadAnchorsByChannel()
method getObjectsFromAnchor (line 491) | void getObjectsFromAnchor(ARPlaneAnchor anchor, FirebaseListener liste...
method deleteExpiredDatabaseEntries (line 499) | void deleteExpiredDatabaseEntries()
method downloadAvailableModels (line 518) | void downloadAvailableModels(FirebaseListener listener)
class AvailableModel (line 526) | class AvailableModel {
class ModelSelectionWidget (line 533) | class ModelSelectionWidget extends StatefulWidget {
method createState (line 540) | _ModelSelectionWidgetState createState()
class _ModelSelectionWidgetState (line 543) | class _ModelSelectionWidgetState extends State<ModelSelectionWidget> {
method initState (line 549) | void initState()
method build (line 562) | Widget build(BuildContext context)
FILE: example/lib/examples/localandwebobjectsexample.dart
class LocalAndWebObjectsWidget (line 19) | class LocalAndWebObjectsWidget extends StatefulWidget {
method createState (line 22) | _LocalAndWebObjectsWidgetState createState()
class _LocalAndWebObjectsWidgetState (line 26) | class _LocalAndWebObjectsWidgetState extends State<LocalAndWebObjectsWid...
method dispose (line 37) | void dispose()
method build (line 43) | Widget build(BuildContext context)
method onARViewCreated (line 92) | void onARViewCreated(
method _downloadFile (line 120) | Future<File> _downloadFile(String url, String filename)
method _downloadAndUnpack (line 131) | Future<void> _downloadAndUnpack(String url, String filename)
method onLocalObjectAtOriginButtonPressed (line 150) | Future<void> onLocalObjectAtOriginButtonPressed()
method onWebObjectAtOriginButtonPressed (line 166) | Future<void> onWebObjectAtOriginButtonPressed()
method onFileSystemObjectAtOriginButtonPressed (line 181) | Future<void> onFileSystemObjectAtOriginButtonPressed()
method onLocalObjectShuffleButtonPressed (line 200) | Future<void> onLocalObjectShuffleButtonPressed()
method onWebObjectShuffleButtonPressed (line 222) | Future<void> onWebObjectShuffleButtonPressed()
FILE: example/lib/examples/objectgesturesexample.dart
class ObjectGesturesWidget (line 17) | class ObjectGesturesWidget extends StatefulWidget {
method createState (line 20) | _ObjectGesturesWidgetState createState()
class _ObjectGesturesWidgetState (line 23) | class _ObjectGesturesWidgetState extends State<ObjectGesturesWidget> {
method dispose (line 32) | void dispose()
method build (line 38) | Widget build(BuildContext context)
method onARViewCreated (line 62) | void onARViewCreated(
method onRemoveEverything (line 90) | Future<void> onRemoveEverything()
method onPlaneOrPointTapped (line 100) | Future<void> onPlaneOrPointTapped(
FILE: example/lib/examples/objectsonplanesexample.dart
class ObjectsOnPlanesWidget (line 17) | class ObjectsOnPlanesWidget extends StatefulWidget {
method createState (line 20) | _ObjectsOnPlanesWidgetState createState()
class _ObjectsOnPlanesWidgetState (line 23) | class _ObjectsOnPlanesWidgetState extends State<ObjectsOnPlanesWidget> {
method dispose (line 32) | void dispose()
method build (line 38) | Widget build(BuildContext context)
method onARViewCreated (line 62) | void onARViewCreated(
method onRemoveEverything (line 83) | Future<void> onRemoveEverything()
method onNodeTapped (line 93) | Future<void> onNodeTapped(List<String> nodes)
method onPlaneOrPointTapped (line 98) | Future<void> onPlaneOrPointTapped(
FILE: example/lib/examples/screenshotexample.dart
class ScreenshotWidget (line 15) | class ScreenshotWidget extends StatefulWidget {
method createState (line 18) | _ScreenshotWidgetState createState()
class _ScreenshotWidgetState (line 21) | class _ScreenshotWidgetState extends State<ScreenshotWidget> {
method dispose (line 30) | void dispose()
method build (line 36) | Widget build(BuildContext context)
method onARViewCreated (line 65) | void onARViewCreated(
method onRemoveEverything (line 86) | Future<void> onRemoveEverything()
method onTakeScreenshot (line 98) | Future<void> onTakeScreenshot()
method onNodeTapped (line 110) | Future<void> onNodeTapped(List<String> nodes)
method onPlaneOrPointTapped (line 115) | Future<void> onPlaneOrPointTapped(
FILE: example/lib/main.dart
function main (line 16) | void main()
class MyApp (line 21) | class MyApp extends StatefulWidget {
method createState (line 23) | _MyAppState createState()
class _MyAppState (line 26) | class _MyAppState extends State<MyApp> {
method initState (line 31) | void initState()
method initPlatformState (line 37) | Future<void> initPlatformState()
method build (line 57) | Widget build(BuildContext context)
class ExampleList (line 74) | class ExampleList extends StatelessWidget {
method build (line 78) | Widget build(BuildContext context)
class ExampleCard (line 129) | class ExampleCard extends StatelessWidget {
class Example (line 150) | class Example {
FILE: example/test/widget_test.dart
function main (line 13) | void main()
FILE: lib/ar_flutter_plugin.dart
class ArFlutterPlugin (line 7) | class ArFlutterPlugin {
FILE: lib/datatypes/anchor_types.dart
type AnchorType (line 2) | enum AnchorType {
FILE: lib/datatypes/config_planedetection.dart
type PlaneDetectionConfig (line 2) | enum PlaneDetectionConfig {
FILE: lib/datatypes/hittest_result_types.dart
type ARHitTestResultType (line 2) | enum ARHitTestResultType {
FILE: lib/datatypes/node_types.dart
type NodeType (line 2) | enum NodeType {
FILE: lib/managers/ar_anchor_manager.dart
type AnchorUploadedHandler (line 6) | typedef AnchorUploadedHandler = void Function(ARAnchor arAnchor);
type AnchorDownloadedHandler (line 7) | typedef AnchorDownloadedHandler = ARAnchor Function(
class ARAnchorManager (line 11) | class ARAnchorManager {
method _platformCallHandler (line 40) | Future<dynamic> _platformCallHandler(MethodCall call)
method addAnchor (line 86) | Future<bool?> addAnchor(ARAnchor anchor)
method uploadAnchor (line 100) | Future<bool?> uploadAnchor(ARAnchor anchor)
method downloadAnchor (line 112) | Future<bool?> downloadAnchor(String cloudanchorid)
FILE: lib/managers/ar_location_manager.dart
class ARLocationManager (line 5) | class ARLocationManager {
method getLastKnownPosition (line 10) | Future<Position?> getLastKnownPosition()
method startLocationUpdates (line 47) | Future<bool> startLocationUpdates()
method stopLocationUpdates (line 91) | void stopLocationUpdates()
method openAppPermissionSettings (line 96) | void openAppPermissionSettings()
method openLocationServicesSettings (line 101) | void openLocationServicesSettings()
method _determinePosition (line 109) | Future<Position> _determinePosition()
FILE: lib/managers/ar_object_manager.dart
type NodeTapResultHandler (line 10) | typedef NodeTapResultHandler = void Function(List<String> nodes);
type NodePanStartHandler (line 11) | typedef NodePanStartHandler = void Function(String node);
type NodePanChangeHandler (line 12) | typedef NodePanChangeHandler = void Function(String node);
type NodePanEndHandler (line 13) | typedef NodePanEndHandler = void Function(String node, Matrix4 transform);
type NodeRotationStartHandler (line 14) | typedef NodeRotationStartHandler = void Function(String node);
type NodeRotationChangeHandler (line 15) | typedef NodeRotationChangeHandler = void Function(String node);
type NodeRotationEndHandler (line 16) | typedef NodeRotationEndHandler = void Function(String node, Matrix4 tran...
class ARObjectManager (line 19) | class ARObjectManager {
method _platformCallHandler (line 43) | Future<void> _platformCallHandler(MethodCall call)
method addNode (line 123) | Future<bool?> addNode(ARNode node, {ARPlaneAnchor? planeAnchor})
FILE: lib/managers/ar_session_manager.dart
type ARHitResultHandler (line 13) | typedef ARHitResultHandler = void Function(List<ARHitTestResult> hits);
class ARSessionManager (line 16) | class ARSessionManager {
method getCameraPose (line 42) | Future<Matrix4?> getCameraPose()
method getPose (line 54) | Future<Matrix4?> getPose(ARAnchor anchor)
method getDistanceBetweenAnchors (line 71) | Future<double?> getDistanceBetweenAnchors(
method getDistanceFromAnchor (line 85) | Future<double?> getDistanceFromAnchor(ARAnchor anchor)
method getDistanceBetweenVectors (line 98) | double getDistanceBetweenVectors(Vector3 vector1, Vector3 vector2)
method _platformCallHandler (line 106) | Future<void> _platformCallHandler(MethodCall call)
method snapshot (line 192) | Future<ImageProvider> snapshot()
FILE: lib/models/ar_anchor.dart
class ARAnchor (line 8) | abstract class ARAnchor {
method toJson (line 36) | Map<String, dynamic> toJson()
class ARPlaneAnchor (line 40) | class ARPlaneAnchor extends ARAnchor {
method fromJson (line 62) | ARPlaneAnchor fromJson(Map<String, dynamic> json)
method toJson (line 66) | Map<String, dynamic> toJson()
function aRPlaneAnchorFromJson (line 70) | ARPlaneAnchor aRPlaneAnchorFromJson(Map<String, dynamic> json)
function aRPlaneAnchorToJson (line 85) | Map<String, dynamic> aRPlaneAnchorToJson(ARPlaneAnchor instance)
class ARUnkownAnchor (line 97) | class ARUnkownAnchor extends ARAnchor {
method fromJson (line 102) | ARUnkownAnchor fromJson(Map<String, dynamic> json)
method toJson (line 106) | Map<String, dynamic> toJson()
function aRUnkownAnchorFromJson (line 109) | ARUnkownAnchor aRUnkownAnchorFromJson(Map<String, dynamic> json)
function aRUnkownAnchorToJson (line 118) | Map<String, dynamic> aRUnkownAnchorToJson(ARUnkownAnchor instance)
FILE: lib/models/ar_hittest_result.dart
class ARHitTestResult (line 9) | class ARHitTestResult {
method fromJson (line 27) | ARHitTestResult fromJson(Map<String, dynamic> json)
method toJson (line 31) | Map<String, dynamic> toJson()
function _$ARHitTestResultFromJson (line 35) | ARHitTestResult _$ARHitTestResultFromJson(Map<String, dynamic> json)
function _$ARHitTestResultToJson (line 44) | Map<String, dynamic> _$ARHitTestResultToJson(ARHitTestResult instance)
function writeNotNull (line 47) | void writeNotNull(String key, dynamic value)
class ARHitTestResultTypeConverter (line 62) | class ARHitTestResultTypeConverter
method fromJson (line 68) | ARHitTestResultType fromJson(int json)
method toJson (line 81) | int toJson(ARHitTestResultType object)
FILE: lib/models/ar_node.dart
class ARNode (line 13) | class ARNode {
method toMap (line 99) | Map<String, dynamic> toMap()
method fromMap (line 108) | ARNode fromMap(Map<String, dynamic> map)
function createTransformMatrix (line 119) | Matrix4 createTransformMatrix(Matrix4? origin, Vector3? position,
function _copySign (line 189) | double _copySign(double magnitude, double sign)
class MatrixValueNotifierConverter (line 206) | class MatrixValueNotifierConverter
method fromJson (line 211) | ValueNotifier<Matrix4> fromJson(List<dynamic> json)
method toJson (line 216) | List<dynamic> toJson(ValueNotifier<Matrix4> matrix)
FILE: lib/utils/json_converters.dart
class MatrixConverter (line 5) | class MatrixConverter implements JsonConverter<Matrix4, List<dynamic>> {
method fromJson (line 9) | Matrix4 fromJson(List<dynamic> json)
method toJson (line 14) | List<dynamic> toJson(Matrix4 matrix)
FILE: lib/widgets/ar_view.dart
type ARViewCreatedCallback (line 11) | typedef ARViewCreatedCallback = void Function(
class PlatformARView (line 18) | abstract class PlatformARView {
method build (line 30) | Widget build(
method onPlatformViewCreated (line 36) | void onPlatformViewCreated(int id)
function createManagers (line 40) | createManagers(
class AndroidARView (line 56) | class AndroidARView implements PlatformARView {
method onPlatformViewCreated (line 62) | void onPlatformViewCreated(int id)
method build (line 68) | Widget build(
class IosARView (line 91) | class IosARView implements PlatformARView {
method onPlatformViewCreated (line 97) | void onPlatformViewCreated(int id)
method build (line 103) | Widget build(
class ARView (line 131) | class ARView extends StatefulWidget {
method createState (line 157) | _ARViewState createState()
class _ARViewState (line 165) | class _ARViewState extends State<ARView> {
method initState (line 179) | void initState()
FILE: test/ar_flutter_plugin_test.dart
function main (line 5) | void main()
Condensed preview — 97 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (379K chars).
[
{
"path": ".gitignore",
"chars": 2440,
"preview": "# Credential files\n/example/ios/Runner/GoogleService-Info.plist\n/example/ios/Runner/cloudAnchorKey.json\n/example/android"
},
{
"path": ".metadata",
"chars": 306,
"preview": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrade"
},
{
"path": "CHANGELOG.md",
"chars": 5723,
"preview": "# 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 "
},
{
"path": "LICENSE",
"chars": 1067,
"preview": "MIT License\n\nCopyright (c) 2021 Lars Carius\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
},
{
"path": "README.md",
"chars": 9799,
"preview": "# ar_flutter_plugin\n[](https://pub.dev/packages/ar_flu"
},
{
"path": "android/.gitignore",
"chars": 97,
"preview": "*.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",
"chars": 1978,
"preview": "group 'io.carius.lars.ar_flutter_plugin'\nversion '1.0-SNAPSHOT'\n\nbuildscript {\n ext.kotlin_version = '1.3.50'\n rep"
},
{
"path": "android/gradle/wrapper/gradle-wrapper.properties",
"chars": 200,
"preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dist"
},
{
"path": "android/gradle.properties",
"chars": 82,
"preview": "org.gradle.jvmargs=-Xmx1536M\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
},
{
"path": "android/settings.gradle",
"chars": 39,
"preview": "rootProject.name = 'ar_flutter_plugin'\n"
},
{
"path": "android/src/main/AndroidManifest.xml",
"chars": 654,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" package=\"io.carius.lars.ar_flutter_plugin\">\n\n <use"
},
{
"path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/AndroidARView.kt",
"chars": 45782,
"preview": "package io.carius.lars.ar_flutter_plugin\n\nimport android.app.Activity\nimport android.app.Application\nimport android.cont"
},
{
"path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/AndroidARViewFactory.kt",
"chars": 684,
"preview": "package io.carius.lars.ar_flutter_plugin\n\nimport android.app.Activity\nimport android.content.Context\nimport io.flutter.p"
},
{
"path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/ArFlutterPlugin.kt",
"chars": 2280,
"preview": "package io.carius.lars.ar_flutter_plugin\n\nimport androidx.annotation.NonNull\nimport io.flutter.embedding.engine.plugins."
},
{
"path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/ArModelBuilder.kt",
"chars": 11097,
"preview": "package io.carius.lars.ar_flutter_plugin\n\nimport android.R\nimport android.app.Activity\nimport android.content.Context\nim"
},
{
"path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/CloudAnchorHandler.kt",
"chars": 2434,
"preview": "package io.carius.lars.ar_flutter_plugin\n\nimport com.google.ar.core.Anchor\nimport com.google.ar.core.Anchor.CloudAnchorS"
},
{
"path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/Serialization/Deserializers.kt",
"chars": 4321,
"preview": "package io.carius.lars.ar_flutter_plugin.Serialization\n\nimport com.google.ar.sceneform.math.Quaternion\nimport com.google"
},
{
"path": "android/src/main/kotlin/io/carius/lars/ar_flutter_plugin/Serialization/Serializers.kt",
"chars": 3207,
"preview": "package io.carius.lars.ar_flutter_plugin.Serialization\n\nimport com.google.ar.core.*\nimport com.google.ar.sceneform.Ancho"
},
{
"path": "cloudAnchorSetup.md",
"chars": 8715,
"preview": "# Getting Started with Cloud Anchors\n\nThe usual method of authentication is setting an API key. However, this authentica"
},
{
"path": "example/.gitignore",
"chars": 732,
"preview": "# 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*.i"
},
{
"path": "example/.metadata",
"chars": 303,
"preview": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrade"
},
{
"path": "example/Models/Chicken_01/Chicken_01.gltf",
"chars": 15758,
"preview": "{\n \"accessors\" : [\n {\n \"bufferView\" : 0,\n \"byteOffset\" : 0,\n \"componentType\" : 5123,\n "
},
{
"path": "example/Models/Chicken_01/license.txt",
"chars": 87,
"preview": "Licensed under CC-BY 3.0 by \"Poly by Google\" (https://poly.google.com/user/4aEd8rQgKu2)"
},
{
"path": "example/README.md",
"chars": 370,
"preview": "# ar_flutter_plugin_example\n\nDemonstrates how to use the ar_flutter_plugin plugin to create collaborative AR experiences"
},
{
"path": "example/android/.gitignore",
"chars": 262,
"preview": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n\n# Remembe"
},
{
"path": "example/android/app/build.gradle",
"chars": 1771,
"preview": "def localProperties = new Properties()\ndef localPropertiesFile = rootProject.file('local.properties')\nif (localPropertie"
},
{
"path": "example/android/app/src/debug/AndroidManifest.xml",
"chars": 348,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"io.carius.lars.ar_flutter_plugin_examp"
},
{
"path": "example/android/app/src/main/AndroidManifest.xml",
"chars": 2217,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"io.carius.lars.ar_flutter_plugin_examp"
},
{
"path": "example/android/app/src/main/kotlin/io/carius/lars/ar_flutter_plugin_example/MainActivity.kt",
"chars": 145,
"preview": "package io.carius.lars.ar_flutter_plugin_example\n\nimport io.flutter.embedding.android.FlutterActivity\n\nclass MainActivit"
},
{
"path": "example/android/app/src/main/res/drawable/launch_background.xml",
"chars": 434,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmln"
},
{
"path": "example/android/app/src/main/res/drawable-v21/launch_background.xml",
"chars": 438,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmln"
},
{
"path": "example/android/app/src/main/res/values/styles.xml",
"chars": 994,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <!-- Theme applied to the Android Window while the process is sta"
},
{
"path": "example/android/app/src/main/res/values-night/styles.xml",
"chars": 993,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <!-- Theme applied to the Android Window while the process is sta"
},
{
"path": "example/android/app/src/profile/AndroidManifest.xml",
"chars": 348,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package=\"io.carius.lars.ar_flutter_plugin_examp"
},
{
"path": "example/android/build.gradle",
"chars": 639,
"preview": "buildscript {\n ext.kotlin_version = '1.6.10'\n repositories {\n google()\n jcenter()\n }\n\n depende"
},
{
"path": "example/android/gradle/wrapper/gradle-wrapper.properties",
"chars": 231,
"preview": "#Fri Jun 23 08:50:38 CEST 2017\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER"
},
{
"path": "example/android/gradle.properties",
"chars": 82,
"preview": "org.gradle.jvmargs=-Xmx1536M\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
},
{
"path": "example/android/settings.gradle",
"chars": 462,
"preview": "include ':app'\n\ndef localPropertiesFile = new File(rootProject.projectDir, \"local.properties\")\ndef properties = new Prop"
},
{
"path": "example/ios/.gitignore",
"chars": 542,
"preview": "*.mode1v3\n*.mode2v3\n*.moved-aside\n*.pbxuser\n*.perspectivev3\n**/*sync/\n.sconsign.dblite\n.tags*\n**/.vagrant/\n**/DerivedDat"
},
{
"path": "example/ios/Flutter/AppFrameworkInfo.plist",
"chars": 774,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "example/ios/Flutter/Debug.xcconfig",
"chars": 107,
"preview": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"Generated.xcconfig\"\n"
},
{
"path": "example/ios/Flutter/Release.xcconfig",
"chars": 109,
"preview": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"Generated.xcconfig\"\n"
},
{
"path": "example/ios/Podfile",
"chars": 2306,
"preview": "# Uncomment this line to define a global platform for your project\n# platform :ios, '9.0'\n\n# Override firebase SDK versi"
},
{
"path": "example/ios/Runner/AppDelegate.swift",
"chars": 404,
"preview": "import UIKit\nimport Flutter\n\n@UIApplicationMain\n@objc class AppDelegate: FlutterAppDelegate {\n override func applicatio"
},
{
"path": "example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 2519,
"preview": "{\n \"images\" : [\n {\n \"size\" : \"20x20\",\n \"idiom\" : \"iphone\",\n \"filename\" : \"Icon-App-20x20@2x.png\",\n "
},
{
"path": "example/ios/Runner/Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
"chars": 391,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"LaunchImage.png\",\n \"scale\" : \"1x\"\n },\n "
},
{
"path": "example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md",
"chars": 336,
"preview": "# Launch Screen Assets\n\nYou can customize the launch screen with your own desired assets by replacing the image files in"
},
{
"path": "example/ios/Runner/Base.lproj/LaunchScreen.storyboard",
"chars": 2377,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
},
{
"path": "example/ios/Runner/Base.lproj/Main.storyboard",
"chars": 1605,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
},
{
"path": "example/ios/Runner/Info.plist",
"chars": 1889,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "example/ios/Runner/Runner-Bridging-Header.h",
"chars": 38,
"preview": "#import \"GeneratedPluginRegistrant.h\"\n"
},
{
"path": "example/ios/Runner.xcodeproj/project.pbxproj",
"chars": 25064,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 51;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 135,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:\">\n </FileRef"
},
{
"path": "example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
"chars": 226,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
"chars": 3185,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1300\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "example/ios/Runner.xcworkspace/contents.xcworkspacedata",
"chars": 224,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"group:Runner.xcodepr"
},
{
"path": "example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "example/lib/examples/cloudanchorexample.dart",
"chars": 16293,
"preview": "import 'dart:convert';\n\nimport 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter"
},
{
"path": "example/lib/examples/debugoptionsexample.dart",
"chars": 4013,
"preview": "import 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_sess"
},
{
"path": "example/lib/examples/externalmodelmanagementexample.dart",
"chars": 20894,
"preview": "import 'dart:convert';\n\nimport 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter"
},
{
"path": "example/lib/examples/localandwebobjectsexample.dart",
"chars": 9279,
"preview": "import 'dart:io';\n\nimport 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter_plug"
},
{
"path": "example/lib/examples/objectgesturesexample.dart",
"chars": 5984,
"preview": "import 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_sess"
},
{
"path": "example/lib/examples/objectsonplanesexample.dart",
"chars": 5067,
"preview": "import 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_sess"
},
{
"path": "example/lib/examples/screenshotexample.dart",
"chars": 5562,
"preview": "import 'package:ar_flutter_plugin/managers/ar_location_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_sess"
},
{
"path": "example/lib/main.dart",
"chars": 4829,
"preview": "import 'package:ar_flutter_plugin_example/examples/externalmodelmanagementexample.dart';\nimport 'package:ar_flutter_plug"
},
{
"path": "example/pubspec.yaml",
"chars": 2715,
"preview": "name: ar_flutter_plugin_example\ndescription: Demonstrates how to use the ar_flutter_plugin plugin.\n\n# The following line"
},
{
"path": "example/test/widget_test.dart",
"chars": 918,
"preview": "// This is a basic Flutter widget test.\n//\n// To perform an interaction with a widget in your test, use the WidgetTester"
},
{
"path": "ios/.gitignore",
"chars": 398,
"preview": ".idea/\n.vagrant/\n.sconsign.dblite\n.svn/\n\n.DS_Store\n*.swp\nprofile\n\nDerivedData/\nbuild/\nGeneratedPluginRegistrant.h\nGenera"
},
{
"path": "ios/Classes/ArFlutterPlugin.h",
"chars": 87,
"preview": "#import <Flutter/Flutter.h>\n\n@interface ArFlutterPlugin : NSObject<FlutterPlugin>\n@end\n"
},
{
"path": "ios/Classes/ArFlutterPlugin.m",
"chars": 601,
"preview": "#import \"ArFlutterPlugin.h\"\n#if __has_include(<ar_flutter_plugin/ar_flutter_plugin-Swift.h>)\n#import <ar_flutter_plugin/"
},
{
"path": "ios/Classes/ArModelBuilder.swift",
"chars": 9351,
"preview": "import UIKit\nimport Foundation\nimport ARKit\nimport GLTFSceneKit\nimport Combine\n\n// Responsible for creating Renderables "
},
{
"path": "ios/Classes/CloudAnchorHandler.swift",
"chars": 2644,
"preview": "import Foundation\nimport ARCoreCloudAnchors\n\n// Listener that can be attached to hosing or resolving processes\nprotocol "
},
{
"path": "ios/Classes/IosARView.swift",
"chars": 40446,
"preview": "import Flutter\nimport UIKit\nimport Foundation\nimport ARKit\nimport Combine\nimport ARCoreCloudAnchors\n\nclass IosARView: NS"
},
{
"path": "ios/Classes/IosARViewFactory.swift",
"chars": 565,
"preview": "import Flutter\nimport UIKit\n\nclass IosARViewFactory: NSObject, FlutterPlatformViewFactory {\n private var messenger: F"
},
{
"path": "ios/Classes/JWTGenerator.swift",
"chars": 2579,
"preview": "//\n// JWTGenerator.swift\n// ar_flutter_plugin\n//\n// Created by Lars Carius on 08.04.21.\n//\n\nimport Foundation\nimport "
},
{
"path": "ios/Classes/Serialization/Deserializers.swift",
"chars": 871,
"preview": "// The code in this file is adapted from Oleksandr Leuschenko' ARKit Flutter Plugin (https://github.com/olexale/arkit_fl"
},
{
"path": "ios/Classes/Serialization/Serializers.swift",
"chars": 2313,
"preview": "import Foundation\nimport ARKit\nimport ARCoreCloudAnchors\n\nfunc serializeHitResult(_ result: ARHitTestResult) -> Dictiona"
},
{
"path": "ios/Classes/SwiftArFlutterPlugin.swift",
"chars": 656,
"preview": "import Flutter\nimport UIKit\n\npublic class SwiftArFlutterPlugin: NSObject, FlutterPlugin {\n public static func register("
},
{
"path": "ios/ar_flutter_plugin.podspec",
"chars": 1309,
"preview": "#\n# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.\n# Run `pod lib lint ar_flutter_pl"
},
{
"path": "lib/ar_flutter_plugin.dart",
"chars": 529,
"preview": "export 'package:ar_flutter_plugin/widgets/ar_view.dart';\n\nimport 'dart:async';\n\nimport 'package:flutter/services.dart';\n"
},
{
"path": "lib/datatypes/anchor_types.dart",
"chars": 181,
"preview": "/// Determines which types of anchors the plugin supports\nenum AnchorType {\n plane,\n //Additional anchor Types can be "
},
{
"path": "lib/datatypes/config_planedetection.dart",
"chars": 156,
"preview": "/// Determines which types of planes ARCore and ARKit should track\nenum PlaneDetectionConfig {\n none,\n horizontal,\n v"
},
{
"path": "lib/datatypes/hittest_result_types.dart",
"chars": 122,
"preview": "/// Determines which types of hit results the plugin supports\nenum ARHitTestResultType {\n undefined,\n plane,\n point,\n"
},
{
"path": "lib/datatypes/node_types.dart",
"chars": 488,
"preview": "/// Determines which types of nodes the plugin supports\nenum NodeType {\n localGLTF2, // Node with renderable with filee"
},
{
"path": "lib/managers/ar_anchor_manager.dart",
"chars": 4331,
"preview": "import 'package:ar_flutter_plugin/models/ar_anchor.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutte"
},
{
"path": "lib/managers/ar_location_manager.dart",
"chars": 5752,
"preview": "import 'dart:async';\nimport 'package:geolocator/geolocator.dart';\n\n/// Can be used to get the current location of the de"
},
{
"path": "lib/managers/ar_object_manager.dart",
"chars": 5175,
"preview": "import 'dart:typed_data';\n\nimport 'package:ar_flutter_plugin/models/ar_anchor.dart';\nimport 'package:ar_flutter_plugin/m"
},
{
"path": "lib/managers/ar_session_manager.dart",
"chars": 7342,
"preview": "import 'dart:math' show sqrt;\nimport 'dart:typed_data';\n\nimport 'package:ar_flutter_plugin/datatypes/config_planedetecti"
},
{
"path": "lib/models/ar_anchor.dart",
"chars": 4041,
"preview": "import 'package:ar_flutter_plugin/datatypes/anchor_types.dart';\nimport 'package:ar_flutter_plugin/models/ar_node.dart';\n"
},
{
"path": "lib/models/ar_hittest_result.dart",
"chars": 3046,
"preview": "// The code in this file is adapted from Oleksandr Leuschenko' ARKit Flutter Plugin (https://github.com/olexale/arkit_fl"
},
{
"path": "lib/models/ar_node.dart",
"chars": 7420,
"preview": "// The code in this file is adapted from Oleksandr Leuschenko' ARKit Flutter Plugin (https://github.com/olexale/arkit_fl"
},
{
"path": "lib/utils/json_converters.dart",
"chars": 604,
"preview": "// The code in this file is adapted from Oleksandr Leuschenko' ARKit Flutter Plugin (https://github.com/olexale/arkit_fl"
},
{
"path": "lib/widgets/ar_view.dart",
"chars": 9480,
"preview": "import 'package:ar_flutter_plugin/managers/ar_anchor_manager.dart';\nimport 'package:ar_flutter_plugin/managers/ar_locati"
},
{
"path": "pubspec.yaml",
"chars": 1154,
"preview": "name: ar_flutter_plugin\ndescription: Flutter Plugin for creating (collaborative) Augmented Reality experiences - Support"
},
{
"path": "test/ar_flutter_plugin_test.dart",
"chars": 570,
"preview": "import 'package:flutter/services.dart';\nimport 'package:flutter_test/flutter_test.dart';\nimport 'package:ar_flutter_plug"
}
]
About this extraction
This page contains the full source code of the CariusLars/ar_flutter_plugin GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 97 files (349.7 KB), approximately 83.3k tokens, and a symbol index with 203 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.