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:
- Completed the Flutter Setup Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
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
| Method | Description |
|---|---|
setPaintProperty(layerId, property, value) | Update any paint property of a layer at runtime |
Common paint properties:
| Layer Type | Property | Values |
|---|---|---|
| Fill | fill-color | Hex color string |
| Fill | fill-opacity | 0.0 to 1.0 |
| Line | line-color | Hex color string |
| Line | line-width | Number (pixels) |
| Circle | circle-color | Hex color string |
| Circle | circle-radius | Number (pixels) |
Next Steps
- Custom Map Styling — Apply full custom styles
- Add a GeoJSON Polygon — Draw filled areas
- Toggle Interactions — Control map behavior
Tip: Use setPaintProperty for real-time theming — for example, change all layer colors at once when the user switches between light and dark mode.