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

1
import 'dart:async';
2
import 'dart:math';
3
4
import 'package:flutter/material.dart';
5
import 'package:nb_maps_flutter/nb_maps_flutter.dart';
6
7
import 'page.dart';
8
9
class PlaceCirclePage extends ExamplePage {
10
PlaceCirclePage() : super(const Icon(Icons.check_circle), 'Place circle');
11
12
@override
13
Widget build(BuildContext context) {
14
return const PlaceCircleBody();
15
}
16
}
17
18
class PlaceCircleBody extends StatefulWidget {
19
const PlaceCircleBody();
20
21
@override
22
State<StatefulWidget> createState() => PlaceCircleBodyState();
23
}
24
25
class 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
@override
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
@override
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.

© 2024 NextBillion.ai all rights reserved.