Skip to content

Commit 4bd89f7

Browse files
pjleonard37claude
andcommitted
Add custom colorized vector icons examples for Flutter
MAPSFLT-411 Port the custom colorized vector icons example from GL JS to Flutter. Demonstrates parameterized SVG icons with runtime color customization using the image expression with color parameters. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 0a11316 commit 4bd89f7

File tree

2 files changed

+119
-0
lines changed

2 files changed

+119
-0
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import 'dart:convert';
2+
import 'package:flutter/material.dart';
3+
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';
4+
import 'example.dart';
5+
6+
/// Example demonstrating custom colorized vector icons using parameterized SVG icons.
7+
/// This example shows how to dynamically color vector icons based on feature properties
8+
/// using the image expression with color parameters.
9+
///
10+
/// For this example to work, the SVGs must live inside the map style, like in the custom style
11+
/// used here. The SVG file was uploaded to Mapbox Studio with the name `flag`,
12+
/// making it available for customization at runtime.
13+
/// You can add vector icons to your own style in Mapbox Studio.
14+
class CustomColorizedVectorIconsExample extends StatefulWidget
15+
implements Example {
16+
@override
17+
final Widget leading = const Icon(Icons.flag);
18+
@override
19+
final String title = 'Custom Colorized Vector Icons';
20+
@override
21+
final String subtitle =
22+
'Dynamically color vector icons using parameterized SVGs';
23+
24+
@override
25+
State<StatefulWidget> createState() =>
26+
_CustomColorizedVectorIconsExampleState();
27+
}
28+
29+
class _CustomColorizedVectorIconsExampleState
30+
extends State<CustomColorizedVectorIconsExample> {
31+
MapboxMap? mapboxMap;
32+
33+
@override
34+
Widget build(BuildContext context) {
35+
return MapWidget(
36+
cameraOptions: CameraOptions(
37+
center: Point(coordinates: Position(24.6881, 60.185755)),
38+
zoom: 16.0,
39+
),
40+
styleUri: 'mapbox://styles/mapbox-map-design/cm4r19bcm00ao01qvhp3jc2gi',
41+
key: ValueKey<String>('mapWidget'),
42+
onMapCreated: _onMapCreated,
43+
onStyleLoadedListener: _onStyleLoaded,
44+
);
45+
}
46+
47+
_onMapCreated(MapboxMap mapboxMap) async {
48+
this.mapboxMap = mapboxMap;
49+
}
50+
51+
_onStyleLoaded(StyleLoadedEventData data) async {
52+
await _addFlagSymbols();
53+
}
54+
55+
/// Creates GeoJSON features with flag locations and colors.
56+
List<Map<String, dynamic>> _createFlagFeatures() {
57+
return [
58+
_createFlagFeature(24.68727, 60.185755, 'red'),
59+
_createFlagFeature(24.68827, 60.186255, 'yellow'),
60+
_createFlagFeature(24.68927, 60.186055, '#800080'),
61+
];
62+
}
63+
64+
/// Creates a feature with a flag at the specified location and color.
65+
Map<String, dynamic> _createFlagFeature(
66+
double longitude, double latitude, String color) {
67+
return {
68+
'type': 'Feature',
69+
'geometry': {
70+
'type': 'Point',
71+
'coordinates': [longitude, latitude],
72+
},
73+
'properties': {
74+
'flagColor': color,
75+
},
76+
};
77+
}
78+
79+
Future<void> _addFlagSymbols() async {
80+
if (mapboxMap == null) {
81+
throw Exception("MapboxMap is not ready yet");
82+
}
83+
84+
// Create GeoJSON FeatureCollection
85+
final geojson = {
86+
'type': 'FeatureCollection',
87+
'features': _createFlagFeatures(),
88+
};
89+
90+
// Add GeoJSON source with flag locations
91+
await mapboxMap?.style.addSource(
92+
GeoJsonSource(
93+
id: 'points',
94+
data: json.encode(geojson),
95+
),
96+
);
97+
98+
// Create symbol layer with parameterized icon
99+
// The expression uses the 'image' operator with a params object
100+
// that maps the 'flag_color' parameter to the 'flagColor' property
101+
final layer = SymbolLayer(
102+
id: 'points',
103+
sourceId: 'points',
104+
iconImageExpression: [
105+
'image',
106+
'flag',
107+
{
108+
'params': {
109+
'flag_color': ['get', 'flagColor']
110+
}
111+
}
112+
],
113+
);
114+
115+
await mapboxMap?.style.addLayer(layer);
116+
}
117+
}

example/lib/main.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:mapbox_maps_example/animation_example.dart';
44
import 'package:mapbox_maps_example/camera_example.dart';
55
import 'package:mapbox_maps_example/circle_annotations_example.dart';
66
import 'package:mapbox_maps_example/cluster_example.dart';
7+
import 'package:mapbox_maps_example/custom_colorized_vector_icons_example.dart';
78
import 'package:mapbox_maps_example/custom_header_example.dart';
89
import 'package:mapbox_maps_example/draggable-annotations-example.dart';
910
import 'package:mapbox_maps_example/edit_polygon_example.dart';
@@ -74,6 +75,7 @@ final List<Example> _allPages = <Example>[
7475
AnimatedRouteExample(),
7576
CustomHeaderExample(),
7677
TrafficLayerExample(),
78+
CustomColorizedVectorIconsExample(),
7779
];
7880

7981
class MapsDemo extends StatelessWidget {

0 commit comments

Comments
 (0)