Skip to content

Draw a Circle in Flutter

This tutorial shows how to draw circles on your MapMetrics Flutter map. Circles are useful for showing a radius around a point — like a search area, delivery zone, or coverage range.

Prerequisites

Before you begin, ensure you have:

Basic Circle

Draw a circle by specifying a center point and a radius in meters:

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

class CircleExampleScreen extends StatefulWidget {
  @override
  _CircleExampleScreenState createState() => _CircleExampleScreenState();
}

class _CircleExampleScreenState extends State<CircleExampleScreen> {
  MapMetricsController? mapController;

  final Set<Circle> circles = {
    Circle(
      circleId: CircleId('search_radius'),
      center: LatLng(48.8566, 2.3522),
      radius: 2000, // 2 km radius
      strokeWidth: 2,
      strokeColor: Colors.blue,
      fillColor: Colors.blue.withOpacity(0.15),
    ),
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Circle Example')),
      body: 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: 13.0,
        ),
        circles: circles,
      ),
    );
  }
}

Multiple Circles with Different Radii

Show concentric circles or multiple zones:

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

class MultipleCirclesScreen extends StatefulWidget {
  @override
  _MultipleCirclesScreenState createState() => _MultipleCirclesScreenState();
}

class _MultipleCirclesScreenState extends State<MultipleCirclesScreen> {
  MapMetricsController? mapController;

  final LatLng center = LatLng(40.7128, -74.0060);

  Set<Circle> get circles => {
    // Inner zone - 1 km
    Circle(
      circleId: CircleId('inner'),
      center: center,
      radius: 1000,
      strokeWidth: 2,
      strokeColor: Colors.green,
      fillColor: Colors.green.withOpacity(0.2),
    ),
    // Middle zone - 3 km
    Circle(
      circleId: CircleId('middle'),
      center: center,
      radius: 3000,
      strokeWidth: 2,
      strokeColor: Colors.orange,
      fillColor: Colors.orange.withOpacity(0.1),
    ),
    // Outer zone - 5 km
    Circle(
      circleId: CircleId('outer'),
      center: center,
      radius: 5000,
      strokeWidth: 2,
      strokeColor: Colors.red,
      fillColor: Colors.red.withOpacity(0.05),
    ),
  };

  // Center marker
  Set<Marker> get markers => {
    Marker(
      markerId: MarkerId('center'),
      position: center,
      infoWindow: InfoWindow(
        title: 'Center Point',
        snippet: '1 km / 3 km / 5 km zones',
      ),
    ),
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Coverage Zones')),
      body: MapMetrics(
        styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
        onMapCreated: (controller) => mapController = controller,
        initialCameraPosition: CameraPosition(
          target: center,
          zoom: 12.0,
        ),
        circles: circles,
        markers: markers,
      ),
    );
  }
}

Dynamic Circle: Tap to Place

Let users tap the map to place a circle at any location:

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

class DynamicCircleScreen extends StatefulWidget {
  @override
  _DynamicCircleScreenState createState() => _DynamicCircleScreenState();
}

class _DynamicCircleScreenState extends State<DynamicCircleScreen> {
  MapMetricsController? mapController;
  Set<Circle> circles = {};
  Set<Marker> markers = {};
  double radiusInMeters = 1000;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Tap to Draw Circle')),
      body: Column(
        children: [
          // Radius slider
          Padding(
            padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
            child: Row(
              children: [
                Text('Radius: ${radiusInMeters.toInt()} m'),
                Expanded(
                  child: Slider(
                    value: radiusInMeters,
                    min: 200,
                    max: 5000,
                    divisions: 24,
                    onChanged: (value) {
                      setState(() {
                        radiusInMeters = value;
                      });
                    },
                  ),
                ),
              ],
            ),
          ),
          // Map
          Expanded(
            child: MapMetrics(
              styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
              onMapCreated: (controller) => mapController = controller,
              onMapClick: (Point point, LatLng coordinates) {
                _addCircle(coordinates);
              },
              initialCameraPosition: CameraPosition(
                target: LatLng(48.8566, 2.3522),
                zoom: 13.0,
              ),
              circles: circles,
              markers: markers,
            ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            circles.clear();
            markers.clear();
          });
        },
        child: Icon(Icons.clear_all),
      ),
    );
  }

  void _addCircle(LatLng position) {
    final String id = 'circle_${DateTime.now().millisecondsSinceEpoch}';

    setState(() {
      circles.add(
        Circle(
          circleId: CircleId(id),
          center: position,
          radius: radiusInMeters,
          strokeWidth: 2,
          strokeColor: Colors.blue,
          fillColor: Colors.blue.withOpacity(0.15),
        ),
      );
      markers.add(
        Marker(
          markerId: MarkerId(id),
          position: position,
          infoWindow: InfoWindow(
            title: 'Circle',
            snippet: 'Radius: ${radiusInMeters.toInt()} m',
          ),
        ),
      );
    });
  }
}

Circle Properties

PropertyTypeDescription
circleIdCircleIdUnique identifier for the circle
centerLatLngCenter point of the circle
radiusdoubleRadius in meters
strokeWidthintBorder width in pixels
strokeColorColorBorder color
fillColorColorFill color (use withOpacity for transparency)
visibleboolWhether the circle is visible
zIndexintDrawing order relative to other overlays
consumeTapEventsboolIf true, tap events are consumed by the circle

Next Steps


Tip: The radius is always in meters and the circle scales correctly at all zoom levels — it represents a real geographic area on the map.