Configure the environment settings in a local scene to change the lighting conditions and background appearance.
Use case
The scene environment defines the appearance of the local scene. This includes sky and background color settings, whether objects in the local scene cast shadows, and if virtual lighting or simulated sunlight is used to light the scene.
For an exploration scenario, like a city planning app, a developer might decide to enable sun lighting and direct shadows so the user can see how buildings and trees cast shadows at a given date and time.
For an analytic scenario, like examining a building scene layer, the developer may choose to use a virtual light source with shadows disabled and a solid background color instead of the atmosphere.
How to use the sample
At start-up, you will see a local scene with a set of scene environment controls at the bottom. Adjusting the controls will change the scene's environment altering the presentation of the scene.
Sky and Background color settings
Toggling the “Stars” and “Atmosphere” buttons will enable or disable those features.
Selecting a color from the dropdown will set a solid color for the background color. Selecting a new background color will disable the stars and atmosphere so you can see the new color.
Some notes about the behavior of the sky and background:
- The atmosphere is rendered in front of the stars and the stars are rendered in front of the background color.
- Stars are not rendered when using virtual lighting.
- To fully see the background color, atmosphere and stars must be deactivated.
- The background color shows in the night sky if the atmosphere is enabled and the stars are disabled.
Lighting settings
The lighting buttons switch between sun lighting and virtual lighting. Switching to virtual lighting disables the “Stars” button since stars do not exist in a virtually lit scene. The time slider is also disabled under virtual lighting since time does not have an effect on the virtual light.
The “Direct Shadows” button will enable or disable the rendering of shadows for 3d objects in the scene. Shadows are not rendered for surface terrain.
If sun lighting is active, the slider under the buttons will set the hour of the day ranging from midnight to 11pm (23:00). Dragging the bar will change the position of the simulated sun causing changes to shading and direct shadows.
How it works
- Create an
ArcGISScenefrom an online resource and add it to theArcGISLocalSceneView. The sample’s controls are updated to reflect the web scene's initial environment. - Changes to the sky and background color settings will set values directly on the
SceneEnvironmentobject.isAtmosphereEnabledandareStarsEnabledare boolean properties dictating whether the atmosphere and star field are visible.- Colors selected from the dropdown are set to the
backgroundColor.
- Changes to the settings in the lighting controls manipulate the
SceneLightingobject in theSceneEnvironment.lightingproperty.- Switching between “Sun” and “Virtual” lighting assigns a new
SunLightingorVirtualLightingobject to the lighting property.- Sun lighting simulates the position of the sun based on a given date and time. This includes lighting conditions for day, twilight, and night.
- For virtual lighting, the light source is always on and is slightly offset from the camera. As the scene rotated or panned, the light source stays in the same position relative to the camera.
- The “Direct Shadows” button sets the
areDirectShadowsEnabledboolean property on the lightning object. This toggles the shadows cast by objects in the scene. - If
SunLightingis active, manipulating the slider changes the hour of thesimulatedDateproperty on the lighting object.VirtualLightingdoes not have a slider because the lighting is always the same regardless of time.
- Switching between “Sun” and “Virtual” lighting assigns a new
Relevant API
- ArcGISLocalSceneView
- ArcGISScene
- SceneEnvironment
- SceneLighting
- SunLighting
- VirtualLighting
About the data
The WebM Open 3D Esri Local Scene used for this sample contains a clipped local scene consisting of a surface and 3D objects representing the buildings. The scene is located in Santa Fe, New Mexico, USA.
Tags
3D, environment, lighting, scene
Sample Code
// Copyright 2026 Esri
//
// 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.
//
import 'package:arcgis_maps/arcgis_maps.dart';
import 'package:arcgis_maps_sdk_flutter_samples/common/common.dart';
import 'package:flutter/material.dart';
class ConfigureSceneEnvironment extends StatefulWidget {
const ConfigureSceneEnvironment({super.key});
@override
State<ConfigureSceneEnvironment> createState() =>
_ConfigureSceneEnvironmentState();
}
class _ConfigureSceneEnvironmentState extends State<ConfigureSceneEnvironment>
with SampleStateSupport {
// Get a controller for the ArcGISLocalSceneView.
final _localSceneViewController = ArcGISLocalSceneView.createController();
// Flag to activate the settings bottom sheet.
var _showBottomSheet = false;
// A flag for when the local scene view is ready and controls can be used.
var _ready = false;
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
top: false,
left: false,
right: false,
child: Stack(
children: [
Column(
children: [
Expanded(
// Add a local scene view to the widget tree and set a controller.
child: ArcGISLocalSceneView(
controllerProvider: () => _localSceneViewController,
onLocalSceneViewReady: onLocalSceneReady,
),
),
Center(
// Button to show the scene environment settings.
child: ElevatedButton(
onPressed: () =>
setState(() => _showBottomSheet = !_showBottomSheet),
child: const Text('Show scene environment settings'),
),
),
],
),
// Display a progress indicator and prevent interaction until state is ready.
LoadingIndicator(visible: !_ready),
],
),
),
// Bottom sheet to show the scene environment settings controls.
bottomSheet: _showBottomSheet
? SceneEnvironmentBottomSheet(
localSceneViewController: _localSceneViewController,
onClose: () => setState(() => _showBottomSheet = false),
)
: null,
);
}
Future<void> onLocalSceneReady() async {
// Create and load the local scene from a ArcGISOnline web scene.
final websceneUri = Uri.parse(
'https://maps.arcgis.com/home/item.html?id=fcebd77958634ac3874bbc0e6b0677a4',
);
final scene = ArcGISScene.withUri(websceneUri)!;
await scene.load();
// Set the scene on the local scene view.
_localSceneViewController.arcGISScene = scene;
// The view is ready for interaction.
setState(() => _ready = true);
}
}
// Bottom sheet for the scene environment controls widget.
class SceneEnvironmentBottomSheet extends StatelessWidget {
const SceneEnvironmentBottomSheet({
required this.localSceneViewController,
required this.onClose,
super.key,
});
final ArcGISLocalSceneViewController localSceneViewController;
final VoidCallback onClose;
@override
Widget build(BuildContext context) {
return BottomSheetSettings(
title: 'Scene Environment Settings',
onCloseIconPressed: onClose,
settingsWidgets: (context) => [
SceneEnvironmentSettings(scene: localSceneViewController.arcGISScene!),
],
);
}
}
// Widget containing all of the controls for configuring the scene environment settings.
class SceneEnvironmentSettings extends StatefulWidget {
const SceneEnvironmentSettings({required this.scene, super.key});
final ArcGISScene scene;
@override
State<StatefulWidget> createState() => _SceneEnvironmentSettingsState();
}
class _SceneEnvironmentSettingsState extends State<SceneEnvironmentSettings> {
// Listing used for background color drop down.
final _backgroundColorOptions = [
(name: 'None', color: Colors.transparent),
(name: 'Black', color: Colors.black),
(name: 'Red', color: Colors.red),
(name: 'Orange', color: Colors.orange),
(name: 'Yellow', color: Colors.yellow),
(name: 'Green', color: Colors.green),
(name: 'Blue', color: Colors.blue),
(name: 'Purple', color: Colors.purple),
(name: 'White', color: Colors.white),
];
// Initialize state variables for the control panel.
var _isStarsEnabled = true;
var _isAtmosphereEnabled = true;
var _isDirectShadowsEnabled = false;
var _backgroundColor = Colors.black;
var _isSunLighting = false;
var _lightingDateTime = DateTime.utc(2026, 3, 20, 12);
var _lightingTimeZoneOffset = Duration.zero;
var _lightingHour = 12;
// Convenience variable to get the ArcGISScene from the widget.
ArcGISScene get _scene => widget.scene;
@override
void initState() {
super.initState();
// Set the state variables based on the actual scene environment.
_isAtmosphereEnabled = _scene.environment.isAtmosphereEnabled;
_isStarsEnabled = _scene.environment.areStarsEnabled;
_isDirectShadowsEnabled =
_scene.environment.lighting.areDirectShadowsEnabled;
_backgroundColor = _scene.environment.backgroundColor;
if (_scene.environment.lighting is SunLighting) {
// Record the simulated time from the web scene.
final sunLighting = _scene.environment.lighting as SunLighting;
_isSunLighting = true;
_lightingDateTime = sunLighting.simulatedDate;
// Record the time zone offset if one was set on the web scene.
if (sunLighting.displayTimeZone != null) {
_lightingTimeZoneOffset = Duration(
hours: sunLighting.displayTimeZone!.hours,
minutes: sunLighting.displayTimeZone!.minutes,
);
}
// Record the localized hour from the web scene lighting.
_lightingHour = sunLighting.simulatedDate
.add(_lightingTimeZoneOffset)
.hour;
}
}
@override
Widget build(BuildContext context) {
return Column(
children: [
const Divider(),
// Section for the Sky setting controls.
Row(
children: [
const Text('Sky:'),
const Spacer(),
ToggleButtons(
borderRadius: const BorderRadius.all(Radius.circular(8)),
isSelected: [_isStarsEnabled],
onPressed: _isSunLighting
? (_) => changeEnableStars(!_isStarsEnabled)
: null,
children: const [
Padding(
padding: EdgeInsetsGeometry.fromLTRB(10, 0, 10, 0),
child: Text('Stars'),
),
],
),
// Space between buttons.
const SizedBox(width: 8),
ToggleButtons(
borderRadius: const BorderRadius.all(Radius.circular(8)),
isSelected: [_isAtmosphereEnabled],
onPressed: (_) {
changeEnableAtmosphere(!_isAtmosphereEnabled);
},
children: const [
Padding(
padding: EdgeInsetsGeometry.fromLTRB(10, 0, 10, 0),
child: Text('Atmosphere'),
),
],
),
],
),
const Divider(),
// Section for the background color setting controls.
Row(
children: [
const Text('Background color:'),
const Spacer(),
DropdownButton(
value: _backgroundColor,
items: _backgroundColorOptions
.map(
(colorOption) => DropdownMenuItem(
value: colorOption.color,
child: Text(colorOption.name),
),
)
.toList(),
onChanged: changeBackgroundColor,
),
],
),
const Divider(),
// Section for the lighting controls.
Column(
children: [
Row(
children: [
const Text('Lighting:'),
const Spacer(),
ToggleButtons(
borderRadius: const BorderRadius.all(Radius.circular(8)),
isSelected: [_isSunLighting, !_isSunLighting],
onPressed: (index) => changeLightingType(index == 0),
children: const [
Padding(
padding: EdgeInsetsGeometry.fromLTRB(10, 0, 10, 0),
child: Text('Sun'),
),
Padding(
padding: EdgeInsetsGeometry.fromLTRB(10, 0, 10, 0),
child: Text('Virtual'),
),
],
),
const Spacer(),
ToggleButtons(
isSelected: [_isDirectShadowsEnabled],
borderRadius: const BorderRadius.all(Radius.circular(8)),
onPressed: (_) =>
changeEnableShadows(!_isDirectShadowsEnabled),
children: const [
Padding(
padding: EdgeInsetsGeometry.fromLTRB(10, 0, 10, 0),
child: Text('Direct Shadows'),
),
],
),
],
),
Row(
children: [
const Text('Hour:'),
Expanded(
child: Slider(
value: _lightingHour.toDouble(),
max: 23,
divisions: 23,
label: '$_lightingHour:00',
onChanged: _isSunLighting ? updateLightingHour : null,
),
),
],
),
],
),
],
);
}
// Function that handles a change in the stars flag.
void changeEnableStars(bool enabled) {
if (enabled == _isStarsEnabled) return;
_scene.environment.areStarsEnabled = enabled;
setState(() => _isStarsEnabled = enabled);
}
// Function that handles a change in the atmoshpere flag.
void changeEnableAtmosphere(bool enabled) {
if (enabled == _isAtmosphereEnabled) return;
_scene.environment.isAtmosphereEnabled = enabled;
setState(() => _isAtmosphereEnabled = enabled);
}
// Function that handles a change in the background color.
void changeBackgroundColor(Color? backgroundColor) {
final newColor = backgroundColor ?? _backgroundColorOptions.first.color;
_scene.environment.backgroundColor = newColor;
setState(() {
_backgroundColor = newColor;
});
// Disable atmosphere and stars to see new color.
changeEnableAtmosphere(false);
changeEnableStars(false);
}
// Function that handles a change in the direct shadows flag.
void changeEnableShadows(bool enabled) {
if (enabled == _isDirectShadowsEnabled) return;
_scene.environment.lighting.areDirectShadowsEnabled = enabled;
setState(() => _isDirectShadowsEnabled = enabled);
}
// Function to change the scene lighting type.
void changeLightingType(bool isSunLighting) {
// Build the new scene lighting object.
final SceneLighting newSceneLighting;
if (isSunLighting) {
newSceneLighting = SunLighting(
simulatedDate: _lightingDateTime,
areDirectShadowsEnabled: _isDirectShadowsEnabled,
);
} else {
newSceneLighting = VirtualLighting(
areDirectShadowsEnabled: _isDirectShadowsEnabled,
);
}
// Set the new lighting object to the scene.
_scene.environment.lighting = newSceneLighting;
setState(() {
// Set the lighting type button state.
_isSunLighting = isSunLighting;
if (_isSunLighting) {
// Ensure the slider is showing the correct hour.
_lightingHour = _lightingDateTime.add(_lightingTimeZoneOffset).hour;
}
});
}
// Function to handle the change in the lighting hour.
void updateLightingHour(double newHourValue) {
final newHourInt = newHourValue.round();
final hourDif = newHourInt - _lightingHour;
// Update the slider control.
setState(() => _lightingHour = newHourInt);
// Update the time on the lightning object.
final hourChangeDuration = Duration(hours: hourDif);
_lightingDateTime = _lightingDateTime.add(hourChangeDuration);
(_scene.environment.lighting as SunLighting).simulatedDate =
_lightingDateTime;
}
}