MapView Markers

Introduction

This example shows how to add markers in bulk

  • Add markers in bulk to MapView
  • Long Click MapView to Add a marker with clicked point
docs-imagedocs-image
Android snapshotiOS snapshot

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

CustomMarkerPage view source

1
import 'dart:io';
2
import 'dart:math';
3
4
import 'package:flutter/foundation.dart';
5
import 'package:flutter/material.dart';
6
import 'package:flutter/widgets.dart'; // ignore: unnecessary_import
7
import 'package:nb_maps_flutter/nb_maps_flutter.dart';
8
9
import 'page.dart';
10
11
const randomMarkerNum = 10;
12
13
class CustomMarkerPage extends ExamplePage {
14
CustomMarkerPage() : super(const Icon(Icons.place), 'Custom marker');
15
16
@override
17
Widget build(BuildContext context) {
18
return CustomMarker();
19
}
20
}
21
22
class CustomMarker extends StatefulWidget {
23
const CustomMarker();
24
25
@override
26
State createState() => CustomMarkerState();
27
}
28
29
class CustomMarkerState extends State<CustomMarker> {
30
final Random _rnd = new Random();
31
32
late NextbillionMapController _mapController;
33
List<Marker> _markers = [];
34
List<_MarkerState> _markerStates = [];
35
36
void _addMarkerStates(_MarkerState markerState) {
37
_markerStates.add(markerState);
38
}
39
40
void _onMapCreated(NextbillionMapController controller) {
41
_mapController = controller;
42
controller.addListener(() {
43
if (controller.isCameraMoving) {
44
_updateMarkerPosition();
45
}
46
});
47
}
48
49
void _onStyleLoadedCallback() {
50
print('onStyleLoadedCallback');
51
}
52
53
void _onMapLongClickCallback(Point<double> point, LatLng coordinates) {
54
_addMarker(point, coordinates);
55
}
56
57
void _onCameraIdleCallback() {
58
_updateMarkerPosition();
59
}
60
61
void _updateMarkerPosition() {
62
final coordinates = <LatLng>[];
63
64
for (final markerState in _markerStates) {
65
coordinates.add(markerState.getCoordinate());
66
}
67
68
_mapController.toScreenLocationBatch(coordinates).then((points) {
69
_markerStates.asMap().forEach((i, value) {
70
_markerStates[i].updatePosition(points[i]);
71
});
72
});
73
}
74
75
void _addMarker(Point<double> point, LatLng coordinates) {
76
setState(() {
77
_markers.add(Marker(_rnd.nextInt(100000).toString(), coordinates, point,
78
_addMarkerStates));
79
});
80
}
81
82
@override
83
Widget build(BuildContext context) {
84
return new Container(
85
child: Stack(children: [
86
NBMap(
87
trackCameraPosition: true,
88
onMapCreated: _onMapCreated,
89
onMapLongClick: _onMapLongClickCallback,
90
onCameraIdle: _onCameraIdleCallback,
91
onStyleLoadedCallback: _onStyleLoadedCallback,
92
initialCameraPosition:
93
const CameraPosition(target: LatLng(35.0, 135.0), zoom: 5),
94
),
95
IgnorePointer(
96
ignoring: true,
97
child: Stack(
98
children: _markers,
99
)),
100
FloatingActionButton(
101
onPressed: () {
102
// Generate random markers
103
var param = <LatLng>[];
104
for (var i = 0; i < randomMarkerNum; i++) {
105
final lat = _rnd.nextDouble() * 20 + 30;
106
final lng = _rnd.nextDouble() * 20 + 125;
107
param.add(LatLng(lat, lng));
108
}
109
110
_mapController.toScreenLocationBatch(param).then((value) {
111
for (var i = 0; i < randomMarkerNum; i++) {
112
var point =
113
Point<double>(value[i].x as double, value[i].y as double);
114
_addMarker(point, param[i]);
115
}
116
});
117
},
118
child: Icon(Icons.add),
119
),
120
]),
121
);
122
}
123
124
// ignore: unused_element
125
void _measurePerformance() {
126
final trial = 10;
127
final batches = [500, 1000, 1500, 2000, 2500, 3000];
128
var results = Map<int, List<double>>();
129
for (final batch in batches) {
130
results[batch] = [0.0, 0.0];
131
}
132
133
_mapController.toScreenLocation(LatLng(0, 0));
134
Stopwatch sw = Stopwatch();
135
136
for (final batch in batches) {
137
//
138
// primitive
139
//
140
for (var i = 0; i < trial; i++) {
141
sw.start();
142
var list = <Future<Point<num>>>[];
143
for (var j = 0; j < batch; j++) {
144
var p = _mapController
145
.toScreenLocation(LatLng(j.toDouble() % 80, j.toDouble() % 300));
146
list.add(p);
147
}
148
Future.wait(list);
149
sw.stop();
150
results[batch]![0] += sw.elapsedMilliseconds;
151
sw.reset();
152
}
153
154
//
155
// batch
156
//
157
for (var i = 0; i < trial; i++) {
158
sw.start();
159
var param = <LatLng>[];
160
for (var j = 0; j < batch; j++) {
161
param.add(LatLng(j.toDouble() % 80, j.toDouble() % 300));
162
}
163
Future.wait([_mapController.toScreenLocationBatch(param)]);
164
sw.stop();
165
results[batch]![1] += sw.elapsedMilliseconds;
166
sw.reset();
167
}
168
169
print(
170
'batch=$batch,primitive=${results[batch]![0] / trial}ms, batch=${results[batch]![1] / trial}ms');
171
}
172
}
173
}
174
175
class Marker extends StatefulWidget {
176
final Point _initialPosition;
177
final LatLng _coordinate;
178
final void Function(_MarkerState) _addMarkerState;
179
180
Marker(
181
String key, this._coordinate, this._initialPosition, this._addMarkerState)
182
: super(key: Key(key));
183
184
@override
185
State<StatefulWidget> createState() {
186
final state = _MarkerState(_initialPosition);
187
_addMarkerState(state);
188
return state;
189
}
190
}
191
192
class _MarkerState extends State with TickerProviderStateMixin {
193
final _iconSize = 20.0;
194
195
Point _position;
196
197
_MarkerState(this._position);
198
199
@override
200
void initState() {
201
super.initState();
202
}
203
204
@override
205
void dispose() {
206
super.dispose();
207
}
208
209
@override
210
Widget build(BuildContext context) {
211
var ratio = 1.0;
212
213
// web does not support Platform._operatingSystem
214
if (!kIsWeb) {
215
// iOS returns logical pixel while Android returns screen pixel
216
ratio = Platform.isIOS ? 1.0 : MediaQuery.of(context).devicePixelRatio;
217
}
218
219
return Positioned(
220
left: _position.x / ratio - _iconSize / 2,
221
top: _position.y / ratio - _iconSize / 2,
222
child: Image.asset('assets/symbols/2.0x/custom-icon.png',
223
height: _iconSize));
224
}
225
226
void updatePosition(Point<num> point) {
227
setState(() {
228
_position = point;
229
});
230
}
231
232
LatLng getCoordinate() {
233
return (widget as Marker)._coordinate;
234
}
235
}

Code summary

The above code snippet demonstrates how to implement a custom marker feature in a Flutter application using the nb_maps_flutter package. The custom markers are added to the map when:

  • the user performs a long-press gesture
  • User click on the floating button on the top left corner And the marker positions are updated when the map is moved.

CustomMarker Class: is a StatefulWidget class that represents the custom marker widget. It uses the NextbillionMapController to interact with the map and manages a list of custom _MarkerState instances to keep track of the markers on the map.

CustomMarkerState Class: is the state class for the CustomMarker widget. It handles map-related events and manages the markers on the map. Notable methods include:

  • _onMapCreated: Handles the initialization of the map controller and sets up a listener for camera movements.
  • _onStyleLoadedCallback: A callback method that is called when the map style is loaded.
  • _onMapLongClickCallback: Handles the event when the user long-presses on the map to add a new custom marker.
  • _onCameraIdleCallback: Handles the event when the camera stops moving and triggers the update of marker positions.
  • _addMarker: Adds a new custom marker to the map and updates the list of markers.

build Method: returns a Container widget containing a Stack with multiple child widgets:

  • NBMap: The actual map widget, provided by the nb_maps_flutter package. It handles user interactions and displays the custom markers on the map.
  • IgnorePointer: A widget that ignores touch events, ensuring that the custom markers do not interfere with user interactions with the map.
  • FloatingActionButton: A button that, when pressed, generates and adds random markers to the map.

_MarkerState Class: This class represents the state of a custom marker. It extends State and is responsible for rendering the custom marker image on the map. Notable methods include:

  • build: Builds the marker widget based on the marker's position and icon image.
  • updatePosition: Updates the marker's position when the map is moved.
  • getCoordinate: Retrieves the coordinate of the marker on the map.

© 2024 NextBillion.ai all rights reserved.