diff --git a/.gitignore b/.gitignore
index 09808ae..95f0716 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,9 +5,11 @@
*.swp
.DS_Store
.atom/
+.build/
.buildlog/
.history
.svn/
+.swiftpm/
migrate_working_dir/
# IntelliJ related
@@ -26,6 +28,7 @@ migrate_working_dir/
/pubspec.lock
**/doc/api/
.dart_tool/
-.packages
+.flutter-plugins
+.flutter-plugins-dependencies
build/
-.fvm
+.vscode/
\ No newline at end of file
diff --git a/analysis_options.yaml b/analysis_options.yaml
index a5744c1..5bcd91a 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,4 +1,3 @@
include: package:flutter_lints/flutter.yaml
-
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
diff --git a/android/build.gradle b/android/build.gradle
index 930ffed..662b929 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,53 +1,77 @@
-group 'io.github.sceneview.sceneview_flutter'
-version '1.0-SNAPSHOT'
+group = "io.github.sceneview.sceneview_flutter"
+version = "1.0-SNAPSHOT"
buildscript {
- ext.kotlin_version = '1.9.0'
+ ext.kotlin_version = "1.8.22"
repositories {
- mavenLocal()
google()
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.1.3'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath("com.android.tools.build:gradle:8.1.4")
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
}
}
allprojects {
repositories {
- mavenLocal()
google()
mavenCentral()
}
}
-apply plugin: 'com.android.library'
-apply plugin: 'kotlin-android'
+apply plugin: "com.android.library"
+apply plugin: "kotlin-android"
android {
- compileSdk 34
- namespace 'io.github.sceneview.sceneview_flutter'
+ namespace = "io.github.sceneview.sceneview_flutter"
+
+ compileSdk = 35
+
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
- jvmTarget = '1.8'
+ jvmTarget = JavaVersion.VERSION_11
}
sourceSets {
- main.java.srcDirs += 'src/main/kotlin'
+ main.java.srcDirs += "src/main/kotlin"
+ test.java.srcDirs += "src/test/kotlin"
}
defaultConfig {
- minSdkVersion 28
+ minSdk = 28
+ consumerProguardFiles 'proguard-rules.pro'
+ }
+
+ dependencies {
+ implementation("io.github.sceneview:arsceneview:2.2.1")
+
+ testImplementation("org.jetbrains.kotlin:kotlin-test")
+ testImplementation("org.mockito:mockito-core:5.1.1")
+ }
+
+ testOptions {
+ unitTests.all {
+ useJUnitPlatform()
+
+ testLogging {
+ events "passed", "skipped", "failed", "standardOut", "standardError"
+ outputs.upToDateWhen { false }
+ showStandardStreams = true
+ }
+ }
+ }
+ buildFeatures {
+ viewBinding true
}
}
dependencies {
- implementation 'io.github.sceneview:arsceneview:1.2.3'
- implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
-}
\ No newline at end of file
+ implementation 'androidx.appcompat:appcompat:1.7.0'
+ implementation 'com.google.android.material:material:1.12.0'
+}
diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 41d9927..0000000
Binary files a/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 8049c68..0000000
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,5 +0,0 @@
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
diff --git a/android/gradlew b/android/gradlew
deleted file mode 100644
index 1b6c787..0000000
--- a/android/gradlew
+++ /dev/null
@@ -1,234 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright © 2015-2021 the original authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-##############################################################################
-#
-# Gradle start up script for POSIX generated by Gradle.
-#
-# Important for running:
-#
-# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
-# noncompliant, but you have some other compliant shell such as ksh or
-# bash, then to run this script, type that shell name before the whole
-# command line, like:
-#
-# ksh Gradle
-#
-# Busybox and similar reduced shells will NOT work, because this script
-# requires all of these POSIX shell features:
-# * functions;
-# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
-# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
-# * compound commands having a testable exit status, especially «case»;
-# * various built-in commands including «command», «set», and «ulimit».
-#
-# Important for patching:
-#
-# (2) This script targets any POSIX shell, so it avoids extensions provided
-# by Bash, Ksh, etc; in particular arrays are avoided.
-#
-# The "traditional" practice of packing multiple parameters into a
-# space-separated string is a well documented source of bugs and security
-# problems, so this is (mostly) avoided, by progressively accumulating
-# options in "$@", and eventually passing that to Java.
-#
-# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
-# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
-# see the in-line comments for details.
-#
-# There are tweaks for specific operating systems such as AIX, CygWin,
-# Darwin, MinGW, and NonStop.
-#
-# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
-# within the Gradle project.
-#
-# You can find Gradle at https://github.com/gradle/gradle/.
-#
-##############################################################################
-
-# Attempt to set APP_HOME
-
-# Resolve links: $0 may be a link
-app_path=$0
-
-# Need this for daisy-chained symlinks.
-while
- APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
- [ -h "$app_path" ]
-do
- ls=$( ls -ld "$app_path" )
- link=${ls#*' -> '}
- case $link in #(
- /*) app_path=$link ;; #(
- *) app_path=$APP_HOME$link ;;
- esac
-done
-
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-APP_NAME="Gradle"
-APP_BASE_NAME=${0##*/}
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD=maximum
-
-warn () {
- echo "$*"
-} >&2
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-} >&2
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "$( uname )" in #(
- CYGWIN* ) cygwin=true ;; #(
- Darwin* ) darwin=true ;; #(
- MSYS* | MINGW* ) msys=true ;; #(
- NONSTOP* ) nonstop=true ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD=$JAVA_HOME/jre/sh/java
- else
- JAVACMD=$JAVA_HOME/bin/java
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD=java
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
- case $MAX_FD in #(
- max*)
- MAX_FD=$( ulimit -H -n ) ||
- warn "Could not query maximum file descriptor limit"
- esac
- case $MAX_FD in #(
- '' | soft) :;; #(
- *)
- ulimit -n "$MAX_FD" ||
- warn "Could not set maximum file descriptor limit to $MAX_FD"
- esac
-fi
-
-# Collect all arguments for the java command, stacking in reverse order:
-# * args from the command line
-# * the main class name
-# * -classpath
-# * -D...appname settings
-# * --module-path (only if needed)
-# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if "$cygwin" || "$msys" ; then
- APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
- CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
-
- JAVACMD=$( cygpath --unix "$JAVACMD" )
-
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- for arg do
- if
- case $arg in #(
- -*) false ;; # don't mess with options #(
- /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
- [ -e "$t" ] ;; #(
- *) false ;;
- esac
- then
- arg=$( cygpath --path --ignore --mixed "$arg" )
- fi
- # Roll the args list around exactly as many times as the number of
- # args, so each arg winds up back in the position where it started, but
- # possibly modified.
- #
- # NB: a `for` loop captures its iteration list before it begins, so
- # changing the positional parameters here affects neither the number of
- # iterations, nor the values presented in `arg`.
- shift # remove old arg
- set -- "$@" "$arg" # push replacement arg
- done
-fi
-
-# Collect all arguments for the java command;
-# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-# shell script including quotes and variable substitutions, so put them in
-# double quotes to make sure that they get re-expanded; and
-# * put everything else in single quotes, so that it's not re-expanded.
-
-set -- \
- "-Dorg.gradle.appname=$APP_BASE_NAME" \
- -classpath "$CLASSPATH" \
- org.gradle.wrapper.GradleWrapperMain \
- "$@"
-
-# Use "xargs" to parse quoted args.
-#
-# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
-#
-# In Bash we could simply go:
-#
-# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
-# set -- "${ARGS[@]}" "$@"
-#
-# but POSIX shell has neither arrays nor command substitution, so instead we
-# post-process each arg (as a line of input to sed) to backslash-escape any
-# character that might be a shell metacharacter, then use eval to reverse
-# that process (while maintaining the separation between arguments), and wrap
-# the whole thing up as a single "set" statement.
-#
-# This will of course break if any of these variables contains a newline or
-# an unmatched quote.
-#
-
-eval "set -- $(
- printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
- xargs -n1 |
- sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
- tr '\n' ' '
- )" '"$@"'
-
-exec "$JAVACMD" "$@"
diff --git a/android/gradlew.bat b/android/gradlew.bat
deleted file mode 100644
index 107acd3..0000000
--- a/android/gradlew.bat
+++ /dev/null
@@ -1,89 +0,0 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/android/proguard-rules.pro b/android/proguard-rules.pro
new file mode 100644
index 0000000..b29c697
--- /dev/null
+++ b/android/proguard-rules.pro
@@ -0,0 +1,13 @@
+-keep class com.google.android.gms.location.** { *; }
+-keep class com.google.android.gms.common.** { *; }
+-keep class com.google.android.gms.maps.** { *; }
+-keep class com.google.ar.core.** { *; }
+-keep class com.google.ar.sceneform.** { *; }
+
+-keep class com.google.android.gms.location.FusedLocationProviderClient { *; }
+
+-keep class com.google.ar.core.Config { *; }
+-keep class com.google.ar.core.Session { *; }
+
+-keepnames class com.google.ar.** { *; }
+-dontwarn com.google.ar.**
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index f856e75..27096df 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -1,3 +1,17 @@
+
+
+
+
+
+
+
+
+
diff --git a/android/src/main/kotlin/io/github/sceneview/sceneview_flutter/SceneViewWrapper.kt b/android/src/main/kotlin/io/github/sceneview/sceneview_flutter/SceneViewWrapper.kt
index d10935e..6b767db 100644
--- a/android/src/main/kotlin/io/github/sceneview/sceneview_flutter/SceneViewWrapper.kt
+++ b/android/src/main/kotlin/io/github/sceneview/sceneview_flutter/SceneViewWrapper.kt
@@ -7,7 +7,9 @@ import android.view.View
import android.widget.FrameLayout
import androidx.lifecycle.Lifecycle
import com.google.ar.core.Config
+import com.google.ar.core.Session
import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
@@ -27,92 +29,119 @@ class SceneViewWrapper(
id: Int,
) : PlatformView, MethodCallHandler {
private val TAG = "SceneViewWrapper"
- private var sceneView: ARSceneView
- private val _mainScope = CoroutineScope(Dispatchers.Main)
- private val _channel = MethodChannel(messenger, "scene_view_$id")
+ private val methodChannelIdentifier = "sceneview_methods";
+ private val eventChannelIdentifier = "sceneview_events";
- override fun getView(): View {
- Log.i(TAG, "getView:")
- return sceneView
- }
+ private var sceneView: ARSceneView? = null
+ private val _mainScope = CoroutineScope(Dispatchers.Main)
+ private val _methodChannel = MethodChannel(messenger, "$methodChannelIdentifier-$id")
+ private val _eventChannel = EventChannel(messenger, "$eventChannelIdentifier-$id")
+ private var eventSink: EventChannel.EventSink? = null
- override fun dispose() {
- Log.i(TAG, "dispose")
- }
+ private val container: FrameLayout = FrameLayout(context)
+ private var disposed: Boolean = false
init {
Log.i(TAG, "init")
- sceneView = ARSceneView(context, sharedLifecycle = lifecycle)
- sceneView.apply {
- configureSession { session, config ->
- config.lightEstimationMode = Config.LightEstimationMode.ENVIRONMENTAL_HDR
- config.depthMode = when (session.isDepthModeSupported(Config.DepthMode.AUTOMATIC)) {
- true -> Config.DepthMode.AUTOMATIC
- else -> Config.DepthMode.DISABLED
- }
- config.instantPlacementMode = Config.InstantPlacementMode.DISABLED
- }
- onSessionResumed = { session ->
+ sceneView = ARSceneView(
+ context,
+ sharedLifecycle = lifecycle,
+ sessionConfiguration = ::configureSession,
+ onSessionCreated = { session ->
Log.i(TAG, "onSessionCreated")
- }
+ },
+ onSessionResumed = { session ->
+ Log.i(TAG, "onSessionResumed")
+ val event = mapOf("type" to "onSessionResumed", "data" to true)
+ eventSink?.success(event)
+ },
onSessionFailed = { exception ->
Log.e(TAG, "onSessionFailed : $exception")
- }
- onSessionCreated = { session ->
- Log.i(TAG, "onSessionCreated")
- }
+ },
onTrackingFailureChanged = { reason ->
Log.i(TAG, "onTrackingFailureChanged: $reason");
}
+ ).apply {
+ keepScreenOn = true
+ layoutParams = FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ )
+ }
+ initializeChannels()
+ disposed = false
+ container.addView(sceneView)
+ }
+
+ override fun dispose() {
+ if (disposed) return
+
+ sceneView?.session?.close()
+ sceneView = null
+ container.removeAllViews()
+ val emptyView = View(container.context).apply {
+ layoutParams = FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ )
}
- sceneView.layoutParams = FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT
- )
- sceneView.keepScreenOn = true
- _channel.setMethodCallHandler(this)
+ container.addView(emptyView)
+ disposed = true
+ Log.i(TAG, "dispose")
+ }
+
+ override fun getView(): View {
+ return container
+ }
+
+ private fun configureSession(session: Session, config: Config) {
+ config.focusMode = Config.FocusMode.AUTO
+ config.updateMode = Config.UpdateMode.LATEST_CAMERA_IMAGE
+ config.planeFindingMode = Config.PlaneFindingMode.DISABLED
+ config.lightEstimationMode = Config.LightEstimationMode.DISABLED
+ config.textureUpdateMode = Config.TextureUpdateMode.BIND_TO_TEXTURE_EXTERNAL_OES
+
+ config.depthMode = Config.DepthMode.DISABLED
+ config.semanticMode = Config.SemanticMode.DISABLED
+ config.geospatialMode = Config.GeospatialMode.DISABLED
+ config.cloudAnchorMode = Config.CloudAnchorMode.DISABLED
+ config.augmentedFaceMode = Config.AugmentedFaceMode.DISABLED
+ config.imageStabilizationMode = Config.ImageStabilizationMode.OFF
+ config.instantPlacementMode = Config.InstantPlacementMode.DISABLED
+ config.streetscapeGeometryMode = Config.StreetscapeGeometryMode.DISABLED
+
+ Log.i(TAG, "Session Configured")
+ }
+
+ private fun initializeChannels() {
+ _methodChannel.setMethodCallHandler(this)
+ _eventChannel.setStreamHandler(object : EventChannel.StreamHandler {
+ override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
+ eventSink = events
+ Log.i(TAG, "Events initialized")
+ }
+
+ override fun onCancel(arguments: Any?) {
+ eventSink = null
+ }
+ })
+ Log.i(TAG, "Channels initialized")
}
private suspend fun addNode(flutterNode: FlutterSceneViewNode) {
val node = buildNode(flutterNode) ?: return
- sceneView.addChildNode(node)
- //AnchorNode(sceneView.engine, anchor).apply {}
- Log.d("Done", "Done")
+ sceneView?.addChildNode(node)
+ Log.d(TAG, "Model placed")
}
private suspend fun buildNode(flutterNode: FlutterSceneViewNode): ModelNode? {
var model: ModelInstance? = null
-
- /*
- AnchorNode(sceneView.engine, anchor)
- .apply {
- isEditable = true
- //isLoading = true
- sceneView.modelLoader.loadModelInstance(
- "https://sceneview.github.io/assets/models/DamagedHelmet.glb"
- )?.let { modelInstance ->
- addChildNode(
- ModelNode(
- modelInstance = modelInstance,
- // Scale to fit in a 0.5 meters cube
- scaleToUnits = 0.5f,
- // Bottom origin instead of center so the model base is on floor
- centerOrigin = Position(y = -0.5f)
- ).apply {
- isEditable = true
- }
- )
- }
- //isLoading = false
- anchorNode = this
- }
- */
when (flutterNode) {
is FlutterReferenceNode -> {
val fileLocation = Utils.getFlutterAssetKey(activity, flutterNode.fileLocation)
- Log.d("SceneViewWrapper", fileLocation)
+ Log.d(TAG, fileLocation)
model =
- sceneView.modelLoader.loadModelInstance(fileLocation)
+ sceneView?.modelLoader?.loadModelInstance(fileLocation)
}
}
if (model != null) {
@@ -120,12 +149,7 @@ class SceneViewWrapper(
transform(
position = flutterNode.position,
rotation = flutterNode.rotation,
- //scale = flutterNode.scale,
)
- //scaleToUnitsCube(flutterNode.scaleUnits)
- // TODO: Fix centerOrigin
- // centerOrigin(Position(x=-1.0f, y=-1.0f))
- //playAnimation()
}
return modelNode
}
@@ -135,7 +159,15 @@ class SceneViewWrapper(
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"init" -> {
- result.success(null)
+ result.success(true)
+ }
+
+ "dispose" -> {
+ _mainScope.launch {
+ dispose()
+ result.success(true)
+ _methodChannel.setMethodCallHandler(null)
+ }
}
"addNode" -> {
@@ -143,9 +175,8 @@ class SceneViewWrapper(
val flutterNode = FlutterSceneViewNode.from(call.arguments as Map)
_mainScope.launch {
addNode(flutterNode)
+ result.success(true)
}
- result.success(null)
- return
}
else -> result.notImplemented()
diff --git a/example/.gitignore b/example/.gitignore
index d2406ad..79c113f 100644
--- a/example/.gitignore
+++ b/example/.gitignore
@@ -5,9 +5,11 @@
*.swp
.DS_Store
.atom/
+.build/
.buildlog/
.history
.svn/
+.swiftpm/
migrate_working_dir/
# IntelliJ related
@@ -27,7 +29,6 @@ migrate_working_dir/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
-.packages
.pub-cache/
.pub/
/build/
@@ -42,5 +43,3 @@ app.*.map.json
/android/app/debug
/android/app/profile
/android/app/release
-
-.fvm
diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml
index 61b6c4d..0439848 100644
--- a/example/analysis_options.yaml
+++ b/example/analysis_options.yaml
@@ -13,8 +13,7 @@ linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
- # and their documentation is published at
- # https://dart-lang.github.io/linter/lints/index.html.
+ # and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
@@ -24,6 +23,5 @@ linter:
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
-
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
diff --git a/example/android/.gitignore b/example/android/.gitignore
index 6f56801..55afd91 100644
--- a/example/android/.gitignore
+++ b/example/android/.gitignore
@@ -7,7 +7,7 @@ gradle-wrapper.jar
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
-# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks
diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle
index 6080d95..f66390d 100644
--- a/example/android/app/build.gradle
+++ b/example/android/app/build.gradle
@@ -1,73 +1,44 @@
-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'
+plugins {
+ id "com.android.application"
+ id "kotlin-android"
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+ id "dev.flutter.flutter-gradle-plugin"
}
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
android {
- compileSdk 34
- ndkVersion flutter.ndkVersion
- namespace 'io.github.sceneview.sceneview_flutter_example'
+ namespace = "io.github.sceneview.sceneview_flutter_example"
+ compileSdk = flutter.compileSdkVersion
+ ndkVersion = flutter.ndkVersion
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
- jvmTarget = '1.8'
- }
-
- sourceSets {
- main.java.srcDirs += 'src/main/kotlin'
+ jvmTarget = JavaVersion.VERSION_1_8
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
- applicationId "io.github.sceneview.sceneview_flutter_example"
+ applicationId = "io.github.sceneview.sceneview_flutter_example"
// You can update the following values to match your application needs.
- // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
- minSdkVersion 28
- targetSdkVersion flutter.targetSdkVersion
- versionCode flutterVersionCode.toInteger()
- versionName flutterVersionName
+ // For more information, see: https://flutter.dev/to/review-gradle-config.
+ minSdk = 28
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutter.versionCode
+ versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
- signingConfig signingConfigs.debug
+ signingConfig = signingConfigs.debug
}
}
}
flutter {
- source '../..'
-}
-
-dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ source = "../.."
}
diff --git a/example/android/build.gradle b/example/android/build.gradle
index 0f650bb..d2ffbff 100644
--- a/example/android/build.gradle
+++ b/example/android/build.gradle
@@ -1,31 +1,16 @@
-buildscript {
- ext.kotlin_version = '1.9.0'
- repositories {
- mavenLocal()
- google()
- mavenCentral()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:8.1.3'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- }
-}
-
allprojects {
repositories {
- mavenLocal()
google()
mavenCentral()
}
}
-rootProject.buildDir = '../build'
+rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
- project.evaluationDependsOn(':app')
+ project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {
diff --git a/example/android/gradle.properties b/example/android/gradle.properties
index 94adc3a..2597170 100644
--- a/example/android/gradle.properties
+++ b/example/android/gradle.properties
@@ -1,3 +1,3 @@
-org.gradle.jvmargs=-Xmx1536M
+org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties
index 3b919e9..7bb2df6 100644
--- a/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Tue Oct 24 11:34:16 CEST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
diff --git a/example/android/settings.gradle b/example/android/settings.gradle
index 44e62bc..b9e43bd 100644
--- a/example/android/settings.gradle
+++ b/example/android/settings.gradle
@@ -1,11 +1,25 @@
-include ':app'
+pluginManagement {
+ def flutterSdkPath = {
+ def properties = new Properties()
+ file("local.properties").withInputStream { properties.load(it) }
+ def flutterSdkPath = properties.getProperty("flutter.sdk")
+ assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+ return flutterSdkPath
+ }()
-def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
-def properties = new Properties()
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
-assert localPropertiesFile.exists()
-localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
-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"
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "8.1.0" apply false
+ id "org.jetbrains.kotlin.android" version "1.8.22" apply false
+}
+
+include ":app"
diff --git a/example/lib/main.dart b/example/lib/main.dart
index d3ee098..3b5327b 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -1,43 +1,72 @@
import 'package:flutter/material.dart';
-import 'dart:async';
-
-import 'package:sceneview_flutter/sceneview_flutter.dart';
-import 'package:sceneview_flutter/sceneview_node.dart';
+import 'package:permission_handler/permission_handler.dart';
+import 'package:sceneview_flutter_example/sceneview_screen.dart';
void main() {
runApp(const MyApp());
}
-class MyApp extends StatefulWidget {
+class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
- State createState() => _MyAppState();
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ home: HomeScreen(),
+ );
+ }
}
-class _MyAppState extends State {
+class HomeScreen extends StatelessWidget {
+ HomeScreen({super.key}) {
+ checkCameraPermission();
+ }
+
@override
Widget build(BuildContext context) {
- return MaterialApp(
- home: Scaffold(
- appBar: AppBar(
- title: const Text('Scene view example app'),
- ),
- body: Stack(
- children: [
- SceneView(
- onViewCreated: (controller) {
- print('flutter: onViewCreated');
- controller.addNode(SceneViewNode(
- fileLocation: 'assets/models/MaterialSuite.glb',
- position: KotlinFloat3(z: -1.0),
- rotation: KotlinFloat3(x: 15),
- ));
- },
- ),
- ],
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Scene view example app'),
+ ),
+ body: SizedBox(
+ width: MediaQuery.of(context).size.width,
+ height: MediaQuery.of(context).size.height,
+ child: Center(
+ child: ElevatedButton(
+ onPressed: () => _openSceneViewScreen(context),
+ child: Text("Open SceneView Screen"),
+ ),
),
),
);
}
+
+ void _openSceneViewScreen(BuildContext context) {
+ Navigator.push(
+ context,
+ PageRouteBuilder(
+ settings: RouteSettings(name: "sceneview_screen"),
+ pageBuilder: (context, animation, secondaryAnimation) {
+ return SceneViewScreen();
+ },
+ transitionsBuilder: (context, animation, secondaryAnimation, child) {
+ return FadeTransition(
+ opacity: animation,
+ child: child,
+ );
+ },
+ ),
+ );
+ }
+
+ void checkCameraPermission() async {
+ var cameraStatus = await Permission.camera.status;
+ if (!cameraStatus.isGranted) {
+ await Permission.camera.request();
+ }
+ var geolocationStatus = await Permission.location.status;
+ if (!geolocationStatus.isGranted) {
+ await Permission.location.request();
+ }
+ }
}
diff --git a/example/lib/sceneview_screen.dart b/example/lib/sceneview_screen.dart
new file mode 100644
index 0000000..51c95c2
--- /dev/null
+++ b/example/lib/sceneview_screen.dart
@@ -0,0 +1,78 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:sceneview_flutter/sceneview_flutter.dart';
+import 'package:sceneview_flutter/sceneview_node.dart';
+
+class SceneViewScreen extends StatefulWidget {
+ const SceneViewScreen({super.key});
+
+ @override
+ State createState() => _SceneViewScreenState();
+}
+
+class _SceneViewScreenState extends State {
+ SceneViewController? _controller;
+ StreamSubscription? _onSessionResumed;
+
+ @override
+ void dispose() {
+ _onSessionResumed?.cancel();
+ _controller?.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return PopScope(
+ canPop: false,
+ onPopInvokedWithResult: (didPop, result) {
+ _controller?.dispose().then(_closeView);
+ },
+ child: Scaffold(
+ appBar: AppBar(
+ title: const Text('AR Scene view'),
+ ),
+ body: Stack(
+ children: [
+ SceneView(
+ onViewCreated: (controller) {
+ _controller = controller;
+ _onSessionResumed = _controller?.on(SceneViewEvent.onSessionResumed).listen((data) {
+ debugPrint("Flutter: onSessionResumed $data");
+ });
+ },
+ ),
+ Positioned(
+ bottom: 0,
+ left: 0,
+ right: 0,
+ child: Center(
+ child: ElevatedButton(
+ onPressed: _placeModel,
+ child: Text("Place model"),
+ ),
+ ),
+ )
+ ],
+ ),
+ ),
+ );
+ }
+
+ void _closeView(bool dispose) {
+ if (dispose) {
+ if (mounted) {
+ Navigator.pop(context);
+ }
+ }
+ }
+
+ void _placeModel() {
+ _controller?.addNode(SceneViewNode(
+ fileLocation: 'assets/models/MaterialSuite.glb',
+ position: KotlinFloat3(z: -1.0),
+ rotation: KotlinFloat3(x: 15),
+ ));
+ }
+}
diff --git a/example/pubspec.lock b/example/pubspec.lock
index bd05899..01435de 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -37,10 +37,10 @@ packages:
dependency: transitive
description:
name: collection
- sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
+ sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
url: "https://pub.dev"
source: hosted
- version: "1.17.2"
+ version: "1.19.0"
fake_async:
dependency: transitive
description:
@@ -49,48 +49,172 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
+ file:
+ dependency: transitive
+ description:
+ name: file
+ sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
+ flutter_driver:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_lints:
+ dependency: "direct dev"
+ description:
+ name: flutter_lints
+ sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
+ flutter_web_plugins:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ fuchsia_remote_debug_protocol:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ integration_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ leak_tracker:
+ dependency: transitive
+ description:
+ name: leak_tracker
+ sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06"
+ url: "https://pub.dev"
+ source: hosted
+ version: "10.0.7"
+ leak_tracker_flutter_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_flutter_testing
+ sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.8"
+ leak_tracker_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_testing
+ sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.1"
+ lints:
+ dependency: transitive
+ description:
+ name: lints
+ sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.1.1"
matcher:
dependency: transitive
description:
name: matcher
- sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
+ sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
- version: "0.12.16"
+ version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
- sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
+ sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
url: "https://pub.dev"
source: hosted
- version: "0.5.0"
+ version: "0.11.1"
meta:
dependency: transitive
description:
name: meta
- sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
+ sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
url: "https://pub.dev"
source: hosted
- version: "1.9.1"
+ version: "1.15.0"
path:
dependency: transitive
description:
name: path
- sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
+ sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.9.0"
+ permission_handler:
+ dependency: "direct main"
+ description:
+ name: permission_handler
+ sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849"
url: "https://pub.dev"
source: hosted
- version: "1.8.3"
+ version: "11.4.0"
+ permission_handler_android:
+ dependency: transitive
+ description:
+ name: permission_handler_android
+ sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc
+ url: "https://pub.dev"
+ source: hosted
+ version: "12.1.0"
+ permission_handler_apple:
+ dependency: transitive
+ description:
+ name: permission_handler_apple
+ sha256: f84a188e79a35c687c132a0a0556c254747a08561e99ab933f12f6ca71ef3c98
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.4.6"
+ permission_handler_html:
+ dependency: transitive
+ description:
+ name: permission_handler_html
+ sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.1.3+5"
+ permission_handler_platform_interface:
+ dependency: transitive
+ description:
+ name: permission_handler_platform_interface
+ sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.3.0"
+ permission_handler_windows:
+ dependency: transitive
+ description:
+ name: permission_handler_windows
+ sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.1"
+ platform:
+ dependency: transitive
+ description:
+ name: platform
+ sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.5"
plugin_platform_interface:
dependency: transitive
description:
@@ -99,6 +223,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.3"
+ process:
+ dependency: transitive
+ description:
+ name: process
+ sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.2"
sceneview_flutter:
dependency: "direct main"
description:
@@ -110,7 +242,7 @@ packages:
dependency: transitive
description: flutter
source: sdk
- version: "0.0.99"
+ version: "0.0.0"
source_span:
dependency: transitive
description:
@@ -123,26 +255,34 @@ packages:
dependency: transitive
description:
name: stack_trace
- sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
+ sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
url: "https://pub.dev"
source: hosted
- version: "1.11.0"
+ version: "1.12.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
- sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
+ sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
- version: "2.1.1"
+ version: "2.1.2"
string_scanner:
dependency: transitive
description:
name: string_scanner
- sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
+ sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
+ sync_http:
+ dependency: transitive
+ description:
+ name: sync_http
+ sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
url: "https://pub.dev"
source: hosted
- version: "1.2.0"
+ version: "0.3.1"
term_glyph:
dependency: transitive
description:
@@ -155,10 +295,10 @@ packages:
dependency: transitive
description:
name: test_api
- sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
+ sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
url: "https://pub.dev"
source: hosted
- version: "0.6.0"
+ version: "0.7.3"
vector_math:
dependency: transitive
description:
@@ -167,14 +307,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
+ vm_service:
+ dependency: transitive
+ description:
+ name: vm_service
+ sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b
+ url: "https://pub.dev"
+ source: hosted
+ version: "14.3.0"
web:
dependency: transitive
description:
name: web
- sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
+ sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ webdriver:
+ dependency: transitive
+ description:
+ name: webdriver
+ sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8"
url: "https://pub.dev"
source: hosted
- version: "0.1.4-beta"
+ version: "3.0.4"
sdks:
- dart: ">=3.1.0-185.0.dev <4.0.0"
- flutter: ">=2.5.0"
+ dart: ">=3.6.1 <4.0.0"
+ flutter: ">=3.24.0"
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 0acb0f0..6f57539 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -1,21 +1,26 @@
name: sceneview_flutter_example
description: Demonstrates how to use the sceneview_flutter plugin.
-publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+publish_to: "none" # Remove this line if you wish to publish to pub.dev
environment:
- sdk: '>=2.18.5 <3.0.0'
+ sdk: ^3.6.1
dependencies:
flutter:
sdk: flutter
+ permission_handler: ^11.4.0
sceneview_flutter:
path: ../
dev_dependencies:
+ integration_test:
+ sdk: flutter
flutter_test:
sdk: flutter
+ flutter_lints: ^5.0.0
flutter:
+ uses-material-design: true
assets:
- assets/models/MaterialSuite.glb
diff --git a/lib/scene_view.dart b/lib/scene_view.dart
index cdc4931..e0d3d5b 100644
--- a/lib/scene_view.dart
+++ b/lib/scene_view.dart
@@ -8,25 +8,24 @@ import 'package:flutter/services.dart';
import 'package:sceneview_flutter/sceneview_controller.dart';
class SceneView extends StatefulWidget {
+ final void Function(SceneViewController)? onViewCreated;
+
const SceneView({
super.key,
this.onViewCreated,
});
- final Function(SceneViewController)? onViewCreated;
-
@override
State createState() => _SceneViewState();
}
class _SceneViewState extends State {
- final Completer _controller =
- Completer();
+ final Completer _controller = Completer();
@override
Widget build(BuildContext context) {
// This is used in the platform side to register the view.
- const String viewType = 'SceneView';
+ const String viewType = "SceneView";
// Pass parameters to the platform side.
const Map creationParams = {};
@@ -51,9 +50,7 @@ class _SceneViewState extends State {
},
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
- ..addOnPlatformViewCreatedListener((id) {
- onPlatformViewCreated(id);
- });
+ ..addOnPlatformViewCreatedListener((id) => onPlatformViewCreated(id));
},
);
}
diff --git a/lib/sceneview_controller.dart b/lib/sceneview_controller.dart
index 1cb78c8..83b4cb9 100644
--- a/lib/sceneview_controller.dart
+++ b/lib/sceneview_controller.dart
@@ -1,27 +1,30 @@
-import 'package:flutter/services.dart';
+import 'dart:async';
+
+import 'package:sceneview_flutter/sceneview_flutter_events.dart';
import 'package:sceneview_flutter/sceneview_flutter_platform_interface.dart';
import 'package:sceneview_flutter/sceneview_node.dart';
+/// When creating new functions call SceneviewFlutterPlatform.instance.invokeMethod()
class SceneViewController {
- SceneViewController._({
- required this.sceneId,
- });
-
final int sceneId;
- static Future init(
- int sceneId,
- ) async {
+ SceneViewController._(this.sceneId);
+
+ static Future init(int sceneId) async {
await SceneviewFlutterPlatform.instance.init(sceneId);
- return SceneViewController._(sceneId: sceneId);
+ return SceneViewController._(sceneId);
}
- void addNode(SceneViewNode node) {
- SceneviewFlutterPlatform.instance.addNode(node);
+ Future dispose() async {
+ return await SceneviewFlutterPlatform.instance.dispose();
}
- void dispose() {
- SceneviewFlutterPlatform.instance.dispose(sceneId);
+ void addNode(SceneViewNode node) {
+ SceneviewFlutterPlatform.instance.invokeMethod("addNode", node.toMap());
}
+ /// All events available in native code through the eventChannel must be declared in SceneViewEvent
+ Stream on(SceneViewEvent event) {
+ return SceneviewFlutterPlatform.instance.on(event);
+ }
}
diff --git a/lib/sceneview_flutter.dart b/lib/sceneview_flutter.dart
index 8266837..27bfb11 100644
--- a/lib/sceneview_flutter.dart
+++ b/lib/sceneview_flutter.dart
@@ -1,6 +1,5 @@
-import 'sceneview_flutter_platform_interface.dart';
-
-export 'sceneview_controller.dart';
export 'scene_view.dart';
+export 'sceneview_controller.dart';
+export 'sceneview_flutter_events.dart';
class SceneviewFlutter {}
diff --git a/lib/sceneview_flutter_events.dart b/lib/sceneview_flutter_events.dart
new file mode 100644
index 0000000..a2a8fa7
--- /dev/null
+++ b/lib/sceneview_flutter_events.dart
@@ -0,0 +1,32 @@
+/// Define here all the event types
+enum SceneViewEvent {
+ onSessionResumed,
+}
+
+class SceneViewEventData {
+ final SceneViewEvent event;
+ final dynamic data;
+
+ SceneViewEventData({
+ required this.event,
+ required this.data,
+ });
+}
+
+bool isValidSceneViewEvent(String value) {
+ for (var type in SceneViewEvent.values) {
+ if (type.name.toLowerCase() == value.toLowerCase()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+SceneViewEvent? stringToSceneViewEvent(String value) {
+ for (var type in SceneViewEvent.values) {
+ if (type.name.toLowerCase() == value.toLowerCase()) {
+ return type;
+ }
+ }
+ return null;
+}
diff --git a/lib/sceneview_flutter_method_channel.dart b/lib/sceneview_flutter_method_channel.dart
index f4219c8..da55eac 100644
--- a/lib/sceneview_flutter_method_channel.dart
+++ b/lib/sceneview_flutter_method_channel.dart
@@ -1,11 +1,16 @@
+import 'dart:async';
+
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
-import 'package:sceneview_flutter/sceneview_node.dart';
+import 'package:sceneview_flutter/sceneview_flutter_events.dart';
import 'sceneview_flutter_platform_interface.dart';
/// An implementation of [SceneviewFlutterPlatform] that uses method channels.
class MethodChannelSceneViewFlutter extends SceneviewFlutterPlatform {
+ static const String methodChannelIdentifier = "sceneview_methods";
+ static const String eventChannelIdentifier = "sceneview_events";
+
/// Registers the Android implementation of SceneviewFlutterPlatform.
static void registerWith() {
SceneviewFlutterPlatform.instance = MethodChannelSceneViewFlutter();
@@ -13,33 +18,55 @@ class MethodChannelSceneViewFlutter extends SceneviewFlutterPlatform {
/// The method channel used to interact with the native platform.
@visibleForTesting
- final methodChannel = const MethodChannel('sceneview_flutter');
+ final methodChannel = const MethodChannel(methodChannelIdentifier);
+ final eventChannel = const EventChannel(eventChannelIdentifier);
- MethodChannel? _channel;
+ MethodChannel? _methodChannel;
+ EventChannel? _eventChannel;
+ StreamSubscription? _eventChannelSubscription;
+ final StreamController _eventController = StreamController.broadcast();
+ Stream get eventStream => _eventController.stream;
- MethodChannel ensureChannelInitialized(int sceneId) {
- MethodChannel? channel = _channel;
- if (channel == null) {
- channel = MethodChannel('scene_view_$sceneId');
- channel.setMethodCallHandler(
- (MethodCall call) => _handleMethodCall(call, sceneId));
- _channel = channel;
- }
- return channel;
+ @override
+ Future init(int sceneId) async {
+ _ensureMethodChannelInitialized(sceneId);
+ _ensureEventChannelInitialized(sceneId);
+ return invokeMethod("init");
}
+ @override
+ Future dispose() async {
+ final result = await invokeMethod("dispose") ?? false;
+ if (result) {
+ _methodChannel?.setMethodCallHandler(null);
+ _methodChannel = null;
+ _eventChannel = null;
+ await _eventChannelSubscription?.cancel();
+ _eventChannelSubscription = null;
+ _eventController.close();
+ }
+ return result;
+ }
@override
- Future init(int sceneId) async {
- final channel = ensureChannelInitialized(sceneId);
- return channel.invokeMethod('init');
+ Future invokeMethod(String method, [dynamic arguments]) async {
+ return await _methodChannel?.invokeMethod(method, arguments);
}
@override
- void addNode(SceneViewNode node) {
- _channel?.invokeMethod('addNode', node.toMap());
+ Stream on(SceneViewEvent event) => eventStream.where((e) => e.event == event).map((e) => e.data as T);
+
+ MethodChannel _ensureMethodChannelInitialized(int sceneId) {
+ MethodChannel? channel = _methodChannel;
+ if (channel == null) {
+ channel = MethodChannel("$methodChannelIdentifier-$sceneId");
+ channel.setMethodCallHandler((MethodCall call) => _handleMethodCall(call, sceneId));
+ _methodChannel = channel;
+ }
+ return channel;
}
+ /// Add here flutter functions you want to call from native
Future _handleMethodCall(MethodCall call, int mapId) async {
switch (call.method) {
default:
@@ -47,6 +74,42 @@ class MethodChannelSceneViewFlutter extends SceneviewFlutterPlatform {
}
}
- @override
- void dispose(int sceneId) {}
+ EventChannel _ensureEventChannelInitialized(int sceneId) {
+ EventChannel? channel = _eventChannel;
+ if (channel == null) {
+ channel = EventChannel("$eventChannelIdentifier-$sceneId");
+ _eventChannelSubscription = channel.receiveBroadcastStream().listen(
+ _handleEventCall,
+ onError: _handleEventError,
+ );
+ _eventChannel = channel;
+ }
+ return channel;
+ }
+
+ void _handleEventCall(dynamic map) {
+ if (map is! Map) {
+ _handleEventError("Event is not a Map");
+ return;
+ }
+
+ final String event = map["type"] ?? "";
+ final dynamic data = map["data"];
+
+ if (!isValidSceneViewEvent(event) || event.isEmpty) {
+ _handleEventError("Event type is empty or not valid");
+ return;
+ }
+
+ _eventController.add(
+ SceneViewEventData(
+ event: stringToSceneViewEvent(event)!,
+ data: data,
+ ),
+ );
+ }
+
+ void _handleEventError(dynamic e) {
+ debugPrint("Flutter: Error in EventChannel: $e");
+ }
}
diff --git a/lib/sceneview_flutter_platform_interface.dart b/lib/sceneview_flutter_platform_interface.dart
index fdbfa19..7cc67b8 100644
--- a/lib/sceneview_flutter_platform_interface.dart
+++ b/lib/sceneview_flutter_platform_interface.dart
@@ -1,5 +1,7 @@
+import 'dart:async';
+
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
-import 'package:sceneview_flutter/sceneview_node.dart';
+import 'package:sceneview_flutter/sceneview_flutter_events.dart';
import 'sceneview_flutter_method_channel.dart';
@@ -25,14 +27,18 @@ abstract class SceneviewFlutterPlatform extends PlatformInterface {
}
Future init(int sceneId) {
- throw UnimplementedError('init() has not been implemented.');
+ throw UnimplementedError("init() has not been implemented.");
+ }
+
+ Future dispose() async {
+ throw UnimplementedError("dispose() has not been implemented.");
}
- void addNode(SceneViewNode node) {
- throw UnimplementedError('addNode() has not been implemented.');
+ Future invokeMethod(String method, [dynamic arguments]) {
+ throw UnimplementedError("invokeMethod() has not been implemented.");
}
- void dispose(int sceneId){
- throw UnimplementedError('dispose() has not been implemented.');
+ Stream on(SceneViewEvent event) {
+ throw UnimplementedError("on() has not been implemented.");
}
}
diff --git a/pubspec.yaml b/pubspec.yaml
index 2e987b1..09de7fc 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,25 +1,25 @@
name: sceneview_flutter
description: A demonstration of how to integrate SceneView in flutter.
-version: 0.0.1
+version: 0.1.0
homepage: https://github.com/SceneView
environment:
- sdk: '>=2.18.5 <3.0.0'
- flutter: ">=2.5.0"
+ sdk: ^3.6.1
+ flutter: ">=3.3.0"
dependencies:
flutter:
sdk: flutter
- plugin_platform_interface: ^2.1.3
+ plugin_platform_interface: ^2.0.2
dev_dependencies:
flutter_test:
sdk: flutter
- flutter_lints: ^2.0.1
+ flutter_lints: ^5.0.0
flutter:
plugin:
platforms:
android:
package: io.github.sceneview.sceneview_flutter
- pluginClass: SceneviewFlutterPlugin
\ No newline at end of file
+ pluginClass: SceneviewFlutterPlugin