AppSystemActivity with the standard lifecycle methods (registerFeatures, onCreate, onSceneReady, registerPanels)UISet theming to spatial panels with dark/light mode supportStarterSample project in Android Studio. Connect your Quest device via USB or Wi-Fi, then run the app. For detailed build instructions and troubleshooting, see the project’s README.| File / Scene | What it demonstrates | Key concepts |
|---|---|---|
StarterSampleActivity.kt | Main activity with lifecycle methods, feature registration, GLXF loading, and environment setup | AppSystemActivity lifecycle, async scene loading with coroutines, scene configuration |
WelcomePanel.kt | Compose UI panel with UISet theming | SpatialTheme, SpatialColorScheme, dark/light mode selection |
app/scenes/Composition/Main.scene | Scene graph with two nodes: Environment (3D room model) and Panel (UI) | Scene composition, node positioning, panel binding via resource ID |
app/scenes/collab_room/ | 3D collaboration room model with meshes, materials, and textures | GLXF model structure, mesh references, material assignment |
app/build.gradle.kts | SDK dependencies and Meta Spatial Editor integration via spatial {} block | Scene export configuration, hot reload setup |
AppSystemActivity and overrides four lifecycle methods: registerFeatures(), onCreate(), onSceneReady(), and registerPanels(), in that order. The framework calls registerFeatures() first to collect feature instances, then triggers onSceneReady() when the spatial scene is ready for configuration. The registerPanels() method links Compose UI to scene nodes. See StarterSampleActivity.kt for the complete implementation.if (BuildConfig.DEBUG) {
features.add(HotReloadFeature(this))
features.add(OVRMetricsFeature(this, ...))
}
VRFeature, ComposeFeature) are always registered. Debug features, including hot reload, metrics overlay, cast input forwarding, and data model inspector, are conditionally added only in debug builds. This pattern keeps release builds lean. See StarterSampleActivity.kt for the full feature list.onCreate using a coroutine:glXFManager.inflateGLXF(
"apk:///scenes/Composition.glxf".toUri(),
rootEntity = gltfxEntity!!,
onLoaded = onLoaded,
)
apk:/// URI scheme accesses assets bundled with the APK. The method creates a root entity, loads the scene asynchronously via coroutine, and provides an onLoaded callback that receives a GLXFInfo object with access to scene nodes by name. The sample uses this callback to override the environment mesh shader to SceneMaterial.UNLIT_SHADER. See the loadGLXF() method in StarterSampleActivity.kt.ComposeViewPanelRegistration(
R.id.panel,
composeViewCreator = { _, ctx ->
ComposeView(ctx).apply { setContent { WelcomePanel() } }
},
)
R.id.panel matches the panel: "@id/panel" property in the scene node definition. This binds the Compose UI to the panel node’s position and transform. The settingsCreator lambda configures the panel shape (QuadShapeOptions with width and height in meters), a transparent background style, and dp-per-meter display scaling. See StarterSampleActivity.kt for the complete panel registration.SpatialTheme(colorScheme = getPanelTheme()) {
Column(modifier = Modifier
.clip(SpatialTheme.shapes.large)
.background(brush = LocalColorScheme.current.panel))
}
SpatialTheme wrapper provides access to typography, shapes, and color schemes from the UISet design system. LocalColorScheme.current.panel supplies the background brush. The getPanelTheme() function selects darkSpatialColorScheme() or lightSpatialColorScheme() based on isSystemInDarkTheme(), so the panel automatically adapts to the system setting. See WelcomePanel.kt for the complete implementation.Object3DSample, CustomComponentsSample, and HybridSample in the same repository.