Mapview Circle Annotation

Introduction

This example shows how to add Circle annotations in MapView

  • Add Circle annotations from a set of Latlng
  • Set Circle annotation opacity, color, radius
  • Set Circle annotation stroke width, stroke color
docs-imagedocs-image
Android snapshotiOS snapshot

For all code examples, refer to Flutter Maps SDK Code Examples

PlaceCirclePage view source

1import 'dart:async';
2import 'dart:math';
3
4import 'package:flutter/material.dart';
5import 'package:nb_maps_flutter/nb_maps_flutter.dart';
6
7import 'page.dart';
8
9class PlaceCirclePage extends ExamplePage {
10 PlaceCirclePage() : super(const Icon(Icons.check_circle), 'Place circle');
11
12
13 Widget build(BuildContext context) {
14 return const PlaceCircleBody();
15 }
16}
17
18class PlaceCircleBody extends StatefulWidget {
19 const PlaceCircleBody();
20
21
22 State<StatefulWidget> createState() => PlaceCircleBodyState();
23}
24
25class PlaceCircleBodyState extends State<PlaceCircleBody> {
26 PlaceCircleBodyState();
27
28 static final LatLng center = const LatLng(-33.86711, 151.1947171);
29
30 NextbillionMapController? controller;
31 int _circleCount = 0;
32 Circle? _selectedCircle;
33
34 void _onMapCreated(NextbillionMapController controller) {
35 this.controller = controller;
36 controller.onCircleTapped.add(_onCircleTapped);
37 }
38
39
40 void dispose() {
41 controller?.onCircleTapped.remove(_onCircleTapped);
42 super.dispose();
43 }
44
45 void _onCircleTapped(Circle circle) {
46 if (_selectedCircle != null) {
47 _updateSelectedCircle(
48 const CircleOptions(circleRadius: 60),
49 );
50 }
51 setState(() {
52 _selectedCircle = circle;
53 });
54 _updateSelectedCircle(
55 CircleOptions(
56 circleRadius: 30,
57 ),
58 );
59 }
60
61 void _updateSelectedCircle(CircleOptions changes) {
62 controller!.updateCircle(_selectedCircle!, changes);
63 }
64
65 void _add() {
66 controller!.addCircle(
67 CircleOptions(
68 geometry: LatLng(
69 center.latitude + sin(_circleCount * pi / 6.0) / 20.0,
70 center.longitude + cos(_circleCount * pi / 6.0) / 20.0,
71 ),
72 circleColor: "#FF0000"),
73 );
74 setState(() {
75 _circleCount += 1;
76 });
77 }
78
79 void _remove() {
80 controller!.removeCircle(_selectedCircle!);
81 setState(() {
82 _selectedCircle = null;
83 _circleCount -= 1;
84 });
85 }
86
87 void _changePosition() {
88 final LatLng current = _selectedCircle!.options.geometry!;
89 final Offset offset = Offset(
90 center.latitude - current.latitude,
91 center.longitude - current.longitude,
92 );
93 _updateSelectedCircle(
94 CircleOptions(
95 geometry: LatLng(
96 center.latitude + offset.dy,
97 center.longitude + offset.dx,
98 ),
99 ),
100 );
101 }
102
103 void _changeDraggable() {
104 bool? draggable = _selectedCircle!.options.draggable;
105 if (draggable == null) {
106 // default value
107 draggable = false;
108 }
109 _updateSelectedCircle(
110 CircleOptions(
111 draggable: !draggable,
112 ),
113 );
114 }
115
116 void _getLatLng() async {
117 LatLng latLng = await controller!.getCircleLatLng(_selectedCircle!);
118 ScaffoldMessenger.of(context).showSnackBar(
119 SnackBar(
120 content: Text(latLng.toString()),
121 ),
122 );
123 }
124
125 void _changeCircleStrokeOpacity() {
126 double? current = _selectedCircle!.options.circleStrokeOpacity;
127 if (current == null) {
128 // default value
129 current = 1.0;
130 }
131
132 _updateSelectedCircle(
133 CircleOptions(circleStrokeOpacity: current < 0.1 ? 1.0 : current * 0.75),
134 );
135 }
136
137 void _changeCircleStrokeWidth() {
138 double? current = _selectedCircle!.options.circleStrokeWidth;
139 if (current == null) {
140 // default value
141 current = 0;
142 }
143 _updateSelectedCircle(
144 CircleOptions(circleStrokeWidth: current == 0 ? 5.0 : 0));
145 }
146
147 Future<void> _changeCircleStrokeColor() async {
148 String? current = _selectedCircle!.options.circleStrokeColor;
149 if (current == null) {
150 // default value
151 current = "#FFFFFF";
152 }
153
154 _updateSelectedCircle(
155 CircleOptions(
156 circleStrokeColor: current == "#FFFFFF" ? "#FF0000" : "#FFFFFF"),
157 );
158 }
159
160 Future<void> _changeCircleOpacity() async {
161 double? current = _selectedCircle!.options.circleOpacity;
162 if (current == null) {
163 // default value
164 current = 1.0;
165 }
166
167 _updateSelectedCircle(
168 CircleOptions(circleOpacity: current < 0.1 ? 1.0 : current * 0.75),
169 );
170 }
171
172 Future<void> _changeCircleRadius() async {
173 double? current = _selectedCircle!.options.circleRadius;
174 if (current == null) {
175 // default value
176 current = 0;
177 }
178 _updateSelectedCircle(
179 CircleOptions(circleRadius: current == 120.0 ? 30.0 : current + 30.0),
180 );
181 }
182
183 Future<void> _changeCircleColor() async {
184 String? current = _selectedCircle!.options.circleColor;
185 if (current == null) {
186 // default value
187 current = "#FF0000";
188 }
189
190 _updateSelectedCircle(
191 CircleOptions(circleColor: "#FFFF00"),
192 );
193 }
194
195 Future<void> _changeCircleBlur() async {
196 double? current = _selectedCircle!.options.circleBlur;
197 if (current == null) {
198 // default value
199 current = 0;
200 }
201 _updateSelectedCircle(
202 CircleOptions(circleBlur: current == 0.75 ? 0 : 0.75),
203 );
204 }
205
206
207 Widget build(BuildContext context) {
208 return Column(
209 mainAxisAlignment: MainAxisAlignment.spaceEvenly,
210 crossAxisAlignment: CrossAxisAlignment.stretch,
211 children: <Widget>[
212 Center(
213 child: SizedBox(
214 width: 300.0,
215 height: 200.0,
216 child: NBMap(
217 onMapCreated: _onMapCreated,
218 initialCameraPosition: const CameraPosition(
219 target: LatLng(-33.852, 151.211),
220 zoom: 11.0,
221 ),
222 ),
223 ),
224 ),
225 Expanded(
226 child: SingleChildScrollView(
227 child: Row(
228 mainAxisAlignment: MainAxisAlignment.spaceEvenly,
229 children: <Widget>[
230 Row(
231 children: <Widget>[
232 Column(
233 children: <Widget>[
234 TextButton(
235 child: const Text('add'),
236 onPressed: (_circleCount == 12) ? null : _add,
237 ),
238 TextButton(
239 child: const Text('remove'),
240 onPressed: (_selectedCircle == null) ? null : _remove,
241 ),
242 ],
243 ),
244 Column(
245 children: <Widget>[
246 TextButton(
247 child: const Text('change circle-opacity'),
248 onPressed: (_selectedCircle == null)
249 ? null
250 : _changeCircleOpacity,
251 ),
252 TextButton(
253 child: const Text('change circle-radius'),
254 onPressed: (_selectedCircle == null)
255 ? null
256 : _changeCircleRadius,
257 ),
258 TextButton(
259 child: const Text('change circle-color'),
260 onPressed: (_selectedCircle == null)
261 ? null
262 : _changeCircleColor,
263 ),
264 TextButton(
265 child: const Text('change circle-blur'),
266 onPressed: (_selectedCircle == null)
267 ? null
268 : _changeCircleBlur,
269 ),
270 TextButton(
271 child: const Text('change circle-stroke-width'),
272 onPressed: (_selectedCircle == null)
273 ? null
274 : _changeCircleStrokeWidth,
275 ),
276 TextButton(
277 child: const Text('change circle-stroke-color'),
278 onPressed: (_selectedCircle == null)
279 ? null
280 : _changeCircleStrokeColor,
281 ),
282 TextButton(
283 child: const Text('change circle-stroke-opacity'),
284 onPressed: (_selectedCircle == null)
285 ? null
286 : _changeCircleStrokeOpacity,
287 ),
288 TextButton(
289 child: const Text('change position'),
290 onPressed: (_selectedCircle == null)
291 ? null
292 : _changePosition,
293 ),
294 TextButton(
295 child: const Text('toggle draggable'),
296 onPressed: (_selectedCircle == null)
297 ? null
298 : _changeDraggable,
299 ),
300 TextButton(
301 child: const Text('get current LatLng'),
302 onPressed:
303 (_selectedCircle == null) ? null : _getLatLng,
304 ),
305 ],
306 ),
307 ],
308 )
309 ],
310 ),
311 ),
312 ),
313 ],
314 );
315 }
316}

Code summary

The above code snippet displays nbMap with various functionalities related to circles. The user can interact with the map, add and remove circles, change circle properties (such as opacity, radius, color, blur, and stroke), move circles, and toggle the circle's draggable state. It utilizes the nb_maps_flutter package for map-related functionalities.

PlaceCirclePage Class: extends the ExamplePage class and represents a page in the app that demonstrates circle-related functionalities. The page is associated with an icon (check_circle icon) and a title ('Place circle'). The build method returns a PlaceCircleBody widget.

PlaceCircleBody Class: is a StatefulWidget class representing the main body of the PlaceCirclePage. It manages the state of the PlaceCirclePage and contains the map and various buttons for interacting with circles.

PlaceCircleBodyState Class: is the state class for the PlaceCircleBody widget. It handles the interactions with the map, such as adding circles, updating circle properties, and removing circles. Some key methods include:

  • _onMapCreated: A callback method called when the map is created. It sets the map controller and adds a listener for circle taps.
  • _onCircleTapped: A method called when a circle on the map is tapped. It changes the properties of the selected circle, making it larger and updates its appearance. The previously selected circle, if any, is reverted to its original size and appearance.
  • _updateSelectedCircle: A method to update the properties of the selected circle.
  • _add: A method to add a new circle to the map. It calculates the position of the circle based on the current _circleCount and the center of the map.
  • _remove: A method to remove the selected circle from the map.
  • _changePosition: A method to change the position of the selected circle on the map.
  • _changeDraggable: A method to toggle the draggable property of the selected circle.
  • _getLatLng: A method to retrieve the latitude and longitude of the selected circle and display it using a SnackBar. Methods to change various circle properties like opacity, radius, color, blur, stroke width, and stroke color.

build Method: The build method returns a Column widget containing a map (NBMap) and a set of buttons for interacting with the circles. The buttons include 'add', 'remove', 'change circle-opacity', 'change circle-radius', 'change circle-color', 'change circle-blur', 'change circle-stroke-width', 'change circle-stroke-color', 'change circle-stroke-opacity', 'change position', 'toggle draggable', and 'get current LatLng'. The map displays circles, and when a circle is tapped, it becomes selected, and its properties can be modified using the buttons.