Skip to content

Change a Layer's Color with Buttons in Flutter

This tutorial shows how to dynamically change the color of a map layer at runtime using buttons — no need to reload the map.

Prerequisites

Before you begin, ensure you have:

Change Fill Layer Color

Add a polygon and change its color by tapping buttons:

dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';

class ChangeLayerColorScreen extends StatefulWidget {
  @override
  _ChangeLayerColorScreenState createState() =>
      _ChangeLayerColorScreenState();
}

class _ChangeLayerColorScreenState extends State<ChangeLayerColorScreen> {
  MapMetricsController? mapController;
  String currentColor = '#3b82f6';

  final List<Map<String, dynamic>> colorOptions = [
    {'name': 'Blue', 'hex': '#3b82f6', 'color': Colors.blue},
    {'name': 'Red', 'hex': '#ef4444', 'color': Colors.red},
    {'name': 'Green', 'hex': '#22c55e', 'color': Colors.green},
    {'name': 'Purple', 'hex': '#8b5cf6', 'color': Colors.purple},
    {'name': 'Orange', 'hex': '#f59e0b', 'color': Colors.orange},
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Change Layer Color')),
      body: Column(
        children: [
          // Color buttons
          Container(
            padding: EdgeInsets.all(12),
            child: Wrap(
              spacing: 8,
              children: colorOptions.map((option) {
                return ElevatedButton(
                  onPressed: () => _changeColor(option['hex']),
                  style: ElevatedButton.styleFrom(
                    backgroundColor: option['color'],
                    foregroundColor: Colors.white,
                    shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(8),
                    ),
                  ),
                  child: Text(option['name']),
                );
              }).toList(),
            ),
          ),
          // Map
          Expanded(
            child: MapMetrics(
              styleUrl:
                  'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
              onMapCreated: (MapMetricsController controller) {
                mapController = controller;
              },
              initialCameraPosition: CameraPosition(
                target: LatLng(48.8566, 2.3522),
                zoom: 4.0,
              ),
              onStyleLoaded: () {
                _addRegionLayer();
              },
            ),
          ),
        ],
      ),
    );
  }

  void _addRegionLayer() {
    final geoJson = {
      'type': 'Feature',
      'properties': {},
      'geometry': {
        'type': 'Polygon',
        'coordinates': [
          [
            [-1.75, 43.3],
            [3.0, 43.3],
            [7.7, 43.8],
            [8.2, 48.9],
            [2.5, 51.1],
            [-4.8, 48.5],
            [-1.75, 43.3],
          ]
        ],
      },
    };

    mapController?.addGeoJsonSource('region', geoJson);

    mapController?.addFillLayer(
      'region-fill',
      'region',
      fillColor: currentColor,
      fillOpacity: 0.4,
    );

    mapController?.addLineLayer(
      'region-outline',
      'region',
      lineColor: currentColor,
      lineWidth: 2.0,
    );
  }

  void _changeColor(String hexColor) {
    setState(() {
      currentColor = hexColor;
    });

    // Update the fill layer color
    mapController?.setPaintProperty('region-fill', 'fill-color', hexColor);

    // Update the outline color too
    mapController?.setPaintProperty('region-outline', 'line-color', hexColor);
  }
}

Change Line Layer Color

Change the color of a route line dynamically:

dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';

class ChangeLineColorScreen extends StatefulWidget {
  @override
  _ChangeLineColorScreenState createState() => _ChangeLineColorScreenState();
}

class _ChangeLineColorScreenState extends State<ChangeLineColorScreen> {
  MapMetricsController? mapController;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Change Line Color')),
      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(48.0, 5.0),
              zoom: 4.0,
            ),
            onStyleLoaded: () {
              _addRouteLine();
            },
          ),
          // Floating color picker
          Positioned(
            bottom: 24,
            left: 16,
            right: 16,
            child: Container(
              padding: EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.circular(12),
                boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 6)],
              ),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text('Route Color',
                      style: TextStyle(fontWeight: FontWeight.bold)),
                  SizedBox(height: 8),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    children: [
                      _colorCircle(Colors.blue, '#3b82f6'),
                      _colorCircle(Colors.red, '#ef4444'),
                      _colorCircle(Colors.green, '#22c55e'),
                      _colorCircle(Colors.purple, '#8b5cf6'),
                      _colorCircle(Colors.orange, '#f59e0b'),
                      _colorCircle(Colors.teal, '#14b8a6'),
                    ],
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _colorCircle(Color color, String hex) {
    return GestureDetector(
      onTap: () {
        mapController?.setPaintProperty('route-line', 'line-color', hex);
      },
      child: Container(
        width: 36,
        height: 36,
        decoration: BoxDecoration(
          color: color,
          shape: BoxShape.circle,
          border: Border.all(color: Colors.white, width: 2),
          boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 2)],
        ),
      ),
    );
  }

  void _addRouteLine() {
    final geoJson = {
      'type': 'Feature',
      'properties': {},
      'geometry': {
        'type': 'LineString',
        'coordinates': [
          [-3.7038, 40.4168],  // Madrid
          [2.349902, 48.853],  // Paris
          [13.405, 52.52],     // Berlin
          [16.3738, 48.2082],  // Vienna
          [12.4964, 41.9028],  // Rome
        ],
      },
    };

    mapController?.addGeoJsonSource('route', geoJson);

    mapController?.addLineLayer(
      'route-line',
      'route',
      lineColor: '#3b82f6',
      lineWidth: 4.0,
      lineJoin: 'round',
      lineCap: 'round',
    );
  }
}

Key Method

MethodDescription
setPaintProperty(layerId, property, value)Update any paint property of a layer at runtime

Common paint properties:

Layer TypePropertyValues
Fillfill-colorHex color string
Fillfill-opacity0.0 to 1.0
Lineline-colorHex color string
Lineline-widthNumber (pixels)
Circlecircle-colorHex color string
Circlecircle-radiusNumber (pixels)

Next Steps


Tip: Use setPaintProperty for real-time theming — for example, change all layer colors at once when the user switches between light and dark mode.