Draw GeoJSON Points in Flutter
This tutorial shows how to render multiple points from GeoJSON data on your MapMetrics Flutter map — efficient for displaying large datasets of locations.
Prerequisites
Before you begin, ensure you have:
- Completed the Flutter Setup Guide
- A MapMetrics API key and style URL from the MapMetrics Portal
Basic GeoJSON Points
Render European capital cities as circle markers from a GeoJSON FeatureCollection:
dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class GeoJsonPointsScreen extends StatefulWidget {
@override
_GeoJsonPointsScreenState createState() => _GeoJsonPointsScreenState();
}
class _GeoJsonPointsScreenState extends State<GeoJsonPointsScreen> {
MapMetricsController? mapController;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('GeoJSON Points')),
body: MapMetrics(
styleUrl:
'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
onMapCreated: (MapMetricsController controller) {
mapController = controller;
},
initialCameraPosition: CameraPosition(
target: LatLng(50.0, 10.0),
zoom: 3.0,
),
onStyleLoaded: () {
_addGeoJsonPoints();
},
),
);
}
void _addGeoJsonPoints() {
final geoJson = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'properties': {'name': 'Paris', 'population': 2161000},
'geometry': {
'type': 'Point',
'coordinates': [2.349902, 48.852966],
},
},
{
'type': 'Feature',
'properties': {'name': 'London', 'population': 8982000},
'geometry': {
'type': 'Point',
'coordinates': [-0.1276, 51.5074],
},
},
{
'type': 'Feature',
'properties': {'name': 'Berlin', 'population': 3645000},
'geometry': {
'type': 'Point',
'coordinates': [13.405, 52.52],
},
},
{
'type': 'Feature',
'properties': {'name': 'Rome', 'population': 2873000},
'geometry': {
'type': 'Point',
'coordinates': [12.4964, 41.9028],
},
},
{
'type': 'Feature',
'properties': {'name': 'Madrid', 'population': 3223000},
'geometry': {
'type': 'Point',
'coordinates': [-3.7038, 40.4168],
},
},
{
'type': 'Feature',
'properties': {'name': 'Vienna', 'population': 1897000},
'geometry': {
'type': 'Point',
'coordinates': [16.3738, 48.2082],
},
},
{
'type': 'Feature',
'properties': {'name': 'Amsterdam', 'population': 872680},
'geometry': {
'type': 'Point',
'coordinates': [4.9041, 52.3676],
},
},
],
};
// Add the GeoJSON source
mapController?.addGeoJsonSource('cities', geoJson);
// Add a circle layer to render points
mapController?.addCircleLayer(
'cities-circles',
'cities',
circleRadius: 8.0,
circleColor: '#3b82f6',
circleStrokeColor: '#ffffff',
circleStrokeWidth: 2.0,
);
}
}Points with Labels
Add text labels next to each point:
dart
void _addPointsWithLabels() {
final geoJson = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'properties': {'name': 'Paris'},
'geometry': {
'type': 'Point',
'coordinates': [2.349902, 48.852966],
},
},
{
'type': 'Feature',
'properties': {'name': 'London'},
'geometry': {
'type': 'Point',
'coordinates': [-0.1276, 51.5074],
},
},
{
'type': 'Feature',
'properties': {'name': 'Berlin'},
'geometry': {
'type': 'Point',
'coordinates': [13.405, 52.52],
},
},
],
};
mapController?.addGeoJsonSource('labeled-cities', geoJson);
// Circle layer
mapController?.addCircleLayer(
'labeled-cities-circles',
'labeled-cities',
circleRadius: 6.0,
circleColor: '#ef4444',
circleStrokeColor: '#ffffff',
circleStrokeWidth: 2.0,
);
// Text label layer
mapController?.addSymbolLayer(
'labeled-cities-labels',
'labeled-cities',
textField: '{name}',
textSize: 12.0,
textColor: '#1f2937',
textOffset: [0.0, 1.5],
textAnchor: 'top',
);
}Styled Points with Different Sizes
Use Flutter markers alongside GeoJSON data for richer styling:
dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';
class StyledPointsScreen extends StatefulWidget {
@override
_StyledPointsScreenState createState() => _StyledPointsScreenState();
}
class _StyledPointsScreenState extends State<StyledPointsScreen> {
MapMetricsController? mapController;
final List<Map<String, dynamic>> cities = [
{'name': 'London', 'lat': 51.5074, 'lng': -0.1276, 'pop': 8982000},
{'name': 'Berlin', 'lat': 52.52, 'lng': 13.405, 'pop': 3645000},
{'name': 'Madrid', 'lat': 40.4168, 'lng': -3.7038, 'pop': 3223000},
{'name': 'Rome', 'lat': 41.9028, 'lng': 12.4964, 'pop': 2873000},
{'name': 'Paris', 'lat': 48.853, 'lng': 2.3499, 'pop': 2161000},
{'name': 'Vienna', 'lat': 48.2082, 'lng': 16.3738, 'pop': 1897000},
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Styled Points')),
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, 8.0),
zoom: 4.0,
),
markers: _buildMarkers(),
),
// Legend
Positioned(
bottom: 16,
left: 16,
child: Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 4)],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text('Population', style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 4),
Row(children: [
Container(width: 10, height: 10, decoration: BoxDecoration(color: Colors.red, shape: BoxShape.circle)),
SizedBox(width: 6),
Text('> 5M'),
]),
Row(children: [
Container(width: 8, height: 8, decoration: BoxDecoration(color: Colors.orange, shape: BoxShape.circle)),
SizedBox(width: 6),
Text('2M - 5M'),
]),
Row(children: [
Container(width: 6, height: 6, decoration: BoxDecoration(color: Colors.blue, shape: BoxShape.circle)),
SizedBox(width: 6),
Text('< 2M'),
]),
],
),
),
),
],
),
);
}
Set<Marker> _buildMarkers() {
return cities.map((city) {
return Marker(
markerId: MarkerId(city['name']),
position: LatLng(city['lat'], city['lng']),
infoWindow: InfoWindow(
title: city['name'],
snippet: 'Pop: ${(city['pop'] / 1000000).toStringAsFixed(1)}M',
),
);
}).toSet();
}
}GeoJSON Circle Layer Properties
| Property | Type | Description |
|---|---|---|
circleRadius | double | Radius of the circle in pixels |
circleColor | String | Fill color of the circle |
circleOpacity | double | Fill opacity from 0.0 to 1.0 |
circleStrokeColor | String | Border color of the circle |
circleStrokeWidth | double | Border width in pixels |
Next Steps
- Add a GeoJSON Line — Draw lines from GeoJSON
- Add a GeoJSON Polygon — Draw filled areas
- Add Clusters — Cluster many points together
Tip: GeoJSON circle layers are more efficient than individual markers when displaying hundreds or thousands of points. Use them for large datasets and switch to Flutter markers only when you need custom widgets.