Sky, Fog, and Terrain in Flutter
This tutorial shows how to add atmospheric effects — sky gradient, fog, and terrain — for immersive 3D map experiences.
Prerequisites
Before you begin, ensure you have:
- Completed the Flutter Setup Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
Sky and Terrain Atmosphere
Create an immersive view with sky, fog, and 3D terrain:
dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class SkyFogTerrainScreen extends StatefulWidget {
@override
_SkyFogTerrainScreenState createState() => _SkyFogTerrainScreenState();
}
class _SkyFogTerrainScreenState extends State<SkyFogTerrainScreen> {
MapMetricsController? mapController;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Sky, Fog & Terrain')),
body: MapMetrics(
styleUrl:
'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
onMapCreated: (MapMetricsController controller) {
mapController = controller;
},
initialCameraPosition: CameraPosition(
target: LatLng(46.8182, 8.2275), // Swiss Alps
zoom: 10.0,
tilt: 70.0,
bearing: 30.0,
),
onStyleLoaded: () {
_addAtmosphericEffects();
},
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton.small(
heroTag: 'immersive',
onPressed: _setImmersiveView,
child: Icon(Icons.landscape),
tooltip: 'Immersive',
),
SizedBox(height: 8),
FloatingActionButton.small(
heroTag: 'overhead',
onPressed: _setOverheadView,
child: Icon(Icons.map),
tooltip: 'Overhead',
),
],
),
);
}
void _addAtmosphericEffects() {
// Add terrain
mapController?.addRasterDemSource(
'terrain-source',
'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png',
tileSize: 256,
);
mapController?.setTerrain('terrain-source', exaggeration: 1.5);
// Add sky layer for a blue sky gradient
mapController?.addSkyLayer(
'sky',
skyType: 'gradient',
skyGradient: [
'interpolate',
['linear'],
['sky-radial-progress'],
0.8, '#87CEEB', // Light blue at horizon
1.0, '#1E3A5F', // Dark blue at zenith
],
skyGradientCenter: [0, 0],
skyGradientRadius: 90,
);
// Add fog for depth perception
mapController?.setFog(
color: '#ffffff',
highColor: '#87CEEB',
horizonBlend: 0.1,
range: [0.5, 10.0],
);
}
void _setImmersiveView() {
mapController?.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(46.8182, 8.2275),
zoom: 10.0,
tilt: 70.0,
bearing: 30.0,
),
),
);
}
void _setOverheadView() {
mapController?.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(46.8182, 8.2275),
zoom: 10.0,
tilt: 0.0,
bearing: 0.0,
),
),
);
}
}Customizable Atmosphere
Let users control fog density and sky color:
dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class CustomAtmosphereScreen extends StatefulWidget {
@override
_CustomAtmosphereScreenState createState() =>
_CustomAtmosphereScreenState();
}
class _CustomAtmosphereScreenState extends State<CustomAtmosphereScreen> {
MapMetricsController? mapController;
double fogDensity = 0.1;
String timeOfDay = 'day'; // day, sunset, night
final Map<String, Map<String, String>> atmospheres = {
'day': {
'fogColor': '#ffffff',
'skyLow': '#87CEEB',
'skyHigh': '#1E3A5F',
'name': 'Day',
},
'sunset': {
'fogColor': '#FFE0B2',
'skyLow': '#FF6F00',
'skyHigh': '#4A148C',
'name': 'Sunset',
},
'night': {
'fogColor': '#1a1a2e',
'skyLow': '#16213e',
'skyHigh': '#0f0f1a',
'name': 'Night',
},
};
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Custom Atmosphere')),
body: Stack(
children: [
MapMetrics(
styleUrl:
'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
onMapCreated: (MapMetricsController controller) {
mapController = controller;
},
initialCameraPosition: CameraPosition(
target: LatLng(45.9763, 7.6586), // Matterhorn
zoom: 11.0,
tilt: 65.0,
bearing: 200.0,
),
onStyleLoaded: () {
_setupTerrain();
_applyAtmosphere();
},
),
// Controls panel
Positioned(
bottom: 16,
left: 16,
right: 16,
child: Card(
child: Padding(
padding: EdgeInsets.all(12),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Time of day selector
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: atmospheres.entries.map((entry) {
final isActive = timeOfDay == entry.key;
return ChoiceChip(
label: Text(entry.value['name']!),
selected: isActive,
onSelected: (selected) {
if (selected) {
setState(() => timeOfDay = entry.key);
_applyAtmosphere();
}
},
);
}).toList(),
),
SizedBox(height: 8),
// Fog density
Row(
children: [
Icon(Icons.cloud, size: 18, color: Colors.grey),
SizedBox(width: 8),
Text('Fog'),
Expanded(
child: Slider(
value: fogDensity,
min: 0.0,
max: 0.5,
onChanged: (val) {
setState(() => fogDensity = val);
_applyAtmosphere();
},
),
),
],
),
],
),
),
),
),
],
),
);
}
void _setupTerrain() {
mapController?.addRasterDemSource(
'terrain-source',
'https://gateway.mapmetrics.org/terrain/{z}/{x}/{y}.png',
tileSize: 256,
);
mapController?.setTerrain('terrain-source', exaggeration: 1.5);
}
void _applyAtmosphere() {
final atm = atmospheres[timeOfDay]!;
mapController?.setFog(
color: atm['fogColor']!,
highColor: atm['skyLow']!,
horizonBlend: fogDensity,
range: [0.5, 10.0],
);
// Remove and re-add sky layer with new colors
mapController?.removeLayer('sky');
mapController?.addSkyLayer(
'sky',
skyType: 'gradient',
skyGradient: [
'interpolate',
['linear'],
['sky-radial-progress'],
0.8, atm['skyLow']!,
1.0, atm['skyHigh']!,
],
skyGradientCenter: [0, 0],
skyGradientRadius: 90,
);
}
}Atmosphere Properties
| Property | Description |
|---|---|
| Sky | |
skyType | gradient or atmosphere |
skyGradient | Color interpolation expression |
skyGradientRadius | Radius of the gradient (degrees) |
| Fog | |
color | Fog color near the camera |
highColor | Fog color at the horizon |
horizonBlend | Blend amount (0.0 = no fog, 0.5 = heavy) |
range | Start and end distance of fog |
| Terrain | |
exaggeration | Height multiplier (0.0 - 3.0) |
Next Steps
- 3D Terrain — Terrain elevation basics
- 3D Buildings — Extruded buildings
- Satellite Terrain — Satellite with elevation
Tip: Sky and fog effects are most visible at high tilt angles (60-80 degrees). Set tilt: 70.0 for the most dramatic atmosphere. At tilt: 0.0 (overhead view), the sky and fog are invisible.