Skip to content

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:

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

PropertyDescription
Sky
skyTypegradient or atmosphere
skyGradientColor interpolation expression
skyGradientRadiusRadius of the gradient (degrees)
Fog
colorFog color near the camera
highColorFog color at the horizon
horizonBlendBlend amount (0.0 = no fog, 0.5 = heavy)
rangeStart and end distance of fog
Terrain
exaggerationHeight multiplier (0.0 - 3.0)

Next Steps


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.