Full Navigation Experience Example

Introduction

  • This example shows:
    • Display a MapView
    • Track Current Location
    • onMapClick
    • onMapLongClick
    • onUserLocationUpdate
  • Customize marker image of route origin and destination
  • Fetch route with RouteRequestParams
    • NBNavigation.fetchRoute(requestParams, (routes, error) async
  • Draw routes with route result
  • navNextBillionMap.drawRoute(routes);
  • Start Navigation with LauncherConfig
    • NBNavigation.startNavigation(config)
documentation imagedocumentation image
Android snapshotiOS snapshot

For all code examples, refer to Flutter Navigation Code Example

FullNavigationExample view source

1
import 'dart:math';
2
3
import 'package:flutter/foundation.dart';
4
import 'package:flutter/material.dart';
5
import 'package:flutter/services.dart';
6
import 'package:fluttertoast/fluttertoast.dart';
7
import 'package:nb_navigation_flutter/nb_navigation_flutter.dart';
8
import 'package:nb_navigation_flutter_example/constants.dart';
9
10
class FullNavigationExample extends StatefulWidget {
11
static const String title = "Full Navigation Experience Example";
12
13
const FullNavigationExample({super.key});
14
15
@override
16
FullNavigationExampleState createState() => FullNavigationExampleState();
17
}
18
19
class FullNavigationExampleState extends State<FullNavigationExample> {
20
NextbillionMapController? controller;
21
List<DirectionsRoute> routes = [];
22
late NavNextBillionMap navNextBillionMap;
23
Symbol? mapMarkerSymbol;
24
25
String locationTrackImage = "assets/location_on.png";
26
UserLocation? currentLocation;
27
List<LatLng> waypoints = [];
28
29
void _onMapCreated(NextbillionMapController controller) {
30
this.controller = controller;
31
}
32
33
_onStyleLoadedCallback() {
34
if (controller != null) {
35
NavNextBillionMap.create(controller!).then((value) {
36
navNextBillionMap = value;
37
loadAssetImage();
38
Fluttertoast.showToast(
39
msg: "Long press to select a destination to fetch a route");
40
if (currentLocation != null) {
41
controller?.animateCamera(
42
CameraUpdate.newLatLngZoom(currentLocation!.position, 14),
43
duration: const Duration(milliseconds: 400));
44
}
45
});
46
}
47
}
48
49
_onMapLongClick(Point<double> point, LatLng coordinates) {
50
_fetchRoute(coordinates);
51
}
52
53
_onMapClick(Point<double> point, LatLng coordinates) {
54
navNextBillionMap.addRouteSelectedListener(coordinates,
55
(selectedRouteIndex) {
56
if (routes.isNotEmpty && selectedRouteIndex != 0) {
57
var selectedRoute = routes[selectedRouteIndex];
58
routes.removeAt(selectedRouteIndex);
59
routes.insert(0, selectedRoute);
60
setState(() {
61
routes = routes;
62
});
63
navNextBillionMap.drawRoute(routes);
64
}
65
});
66
}
67
68
_onUserLocationUpdate(UserLocation location) {
69
currentLocation = location;
70
}
71
72
_onCameraTrackingChanged() {
73
setState(() {
74
locationTrackImage = 'assets/location_off.png';
75
});
76
}
77
78
@override
79
Widget build(BuildContext context) {
80
return Stack(
81
children: [
82
NBMap(
83
onMapCreated: _onMapCreated,
84
onStyleLoadedCallback: _onStyleLoadedCallback,
85
initialCameraPosition: const CameraPosition(
86
target: LatLng(0, 0),
87
zoom: 14.0,
88
),
89
trackCameraPosition: true,
90
myLocationEnabled: true,
91
myLocationTrackingMode: MyLocationTrackingMode.Tracking,
92
onMapLongClick: _onMapLongClick,
93
onUserLocationUpdated: _onUserLocationUpdate,
94
onCameraTrackingDismissed: _onCameraTrackingChanged,
95
onMapClick: _onMapClick,
96
styleString: NbNavigationStyles.nbMapDefaultLightStyle,
97
),
98
Padding(
99
padding: const EdgeInsets.all(8.0),
100
child: Column(
101
mainAxisAlignment: MainAxisAlignment.end,
102
crossAxisAlignment: CrossAxisAlignment.start,
103
children: [
104
Padding(
105
padding: const EdgeInsets.all(8.0),
106
child: GestureDetector(
107
child: Image(
108
image: AssetImage(locationTrackImage),
109
width: 28,
110
height: 28,
111
),
112
onTap: () {
113
controller?.updateMyLocationTrackingMode(
114
MyLocationTrackingMode.Tracking);
115
setState(() {
116
locationTrackImage = 'assets/location_on.png';
117
});
118
}),
119
),
120
const Padding(padding: EdgeInsets.only(top: 35)),
121
Row(
122
mainAxisAlignment: MainAxisAlignment.center,
123
children: [
124
ElevatedButton(
125
style: ButtonStyle(
126
backgroundColor: WidgetStateProperty.all(
127
routes.isEmpty ? Colors.grey : Colors.blueAccent),
128
enableFeedback: routes.isNotEmpty),
129
onPressed: () {
130
clearRouteResult();
131
waypoints.clear();
132
},
133
child: const Text("Clear Routes")),
134
const Padding(padding: EdgeInsets.only(left: 8)),
135
ElevatedButton(
136
style: ButtonStyle(
137
backgroundColor: WidgetStateProperty.all(
138
routes.isEmpty ? Colors.grey : Colors.blueAccent),
139
enableFeedback: routes.isNotEmpty),
140
onPressed: () {
141
_startNavigation();
142
},
143
child: const Text("Start Navigation")),
144
],
145
),
146
const Padding(padding: EdgeInsets.only(top: 48)),
147
// Padding(
148
// padding: const EdgeInsets.only(top: 8.0),
149
// child: Text("route response: ${routeResult}"),
150
// ),
151
],
152
),
153
)
154
],
155
);
156
}
157
158
void _fetchRoute(LatLng destination) async {
159
if (currentLocation == null) {
160
return;
161
}
162
LatLng origin = currentLocation!.position;
163
waypoints.add(destination);
164
RouteRequestParams requestParams = RouteRequestParams(
165
origin: origin,
166
destination: waypoints.last,
167
// overview: ValidOverview.simplified,
168
// avoid: [SupportedAvoid.toll, SupportedAvoid.ferry],
169
// option: SupportedOption.flexible,
170
// truckSize: [200, 200, 600],
171
// truckWeight: 100,
172
// unit: SupportedUnits.imperial,
173
alternatives: true,
174
mode: ValidModes.car,
175
);
176
177
if (waypoints.length > 1) {
178
requestParams.waypoints = waypoints.sublist(0, waypoints.length - 1);
179
}
180
181
DirectionsRouteResponse routeResponse =
182
await NBNavigation.fetchRoute(requestParams);
183
if (routeResponse.directionsRoutes.isNotEmpty) {
184
clearRouteResult();
185
setState(() {
186
routes = routeResponse.directionsRoutes;
187
});
188
drawRoutes(routes);
189
fitCameraToBounds(routes);
190
addImageFromAsset(destination);
191
} else if (routeResponse.message != null) {
192
if (kDebugMode) {
193
print(
194
"====error====${routeResponse.message}===${routeResponse.errorCode}");
195
}
196
}
197
}
198
199
Future<void> drawRoutes(List<DirectionsRoute> routes) async {
200
// navNextBillionMap.toggleDurationSymbolVisibilityWith(false);
201
navNextBillionMap.drawRoute(routes);
202
}
203
204
void fitCameraToBounds(List<DirectionsRoute> routes) {
205
List<LatLng> multiPoints = [];
206
for (var route in routes) {
207
var routePoints =
208
decode(route.geometry ?? '', _getDecodePrecision(route.routeOptions));
209
multiPoints.addAll(routePoints);
210
}
211
if (multiPoints.isNotEmpty) {
212
var latLngBounds = LatLngBounds.fromMultiLatLng(multiPoints);
213
controller?.animateCamera(CameraUpdate.newLatLngBounds(latLngBounds,
214
top: 50, left: 50, right: 50, bottom: 50));
215
}
216
}
217
218
int _getDecodePrecision(RouteRequestParams? routeOptions) {
219
return routeOptions?.geometry == SupportedGeometry.polyline
220
? precision
221
: precision6;
222
}
223
224
void clearRouteResult() async {
225
navNextBillionMap.clearRoute();
226
controller?.clearSymbols();
227
setState(() {
228
routes.clear();
229
});
230
}
231
232
void _startNavigation() {
233
if (routes.isEmpty) return;
234
NavigationLauncherConfig config =
235
NavigationLauncherConfig(route: routes.first, routes: routes);
236
config.locationLayerRenderMode = LocationLayerRenderMode.gps;
237
config.shouldSimulateRoute = false;
238
config.themeMode = NavigationThemeMode.system;
239
config.useCustomNavigationStyle = false;
240
NBNavigation.startNavigation(config);
241
}
242
243
Future<void> loadAssetImage() async {
244
final ByteData bytes = await rootBundle.load("assets/map_marker_light.png");
245
final Uint8List list = bytes.buffer.asUint8List();
246
await controller?.addImage("ic_marker_destination", list);
247
}
248
249
Future<void> addImageFromAsset(LatLng coordinates) async {
250
controller?.clearSymbols();
251
var symbolOptions = SymbolOptions(
252
geometry: coordinates,
253
iconImage: "ic_marker_destination",
254
);
255
await controller?.addSymbol(symbolOptions);
256
controller?.symbolManager?.setTextAllowOverlap(false);
257
}
258
259
@override
260
void initState() {
261
super.initState();
262
}
263
264
@override
265
void dispose() {
266
// TODO: implement dispose
267
super.dispose();
268
}
269
270
@override
271
void didUpdateWidget(FullNavigationExample oldWidget) {
272
// TODO: implement didUpdateWidget
273
super.didUpdateWidget(oldWidget);
274
}
275
276
@override
277
void didChangeDependencies() {
278
// TODO: implement didChangeDependencies
279
super.didChangeDependencies();
280
}
281
}
282
283
284
285

Code summary

The above code snippet demonstrates a full navigation experience using the "nb_navigation_flutter" and "nb_maps_flutter" packages. It provides functionalities to display a map with a user's current location, select a destination by long-pressing on the map, fetch a route from the user's current location to the selected destination, and start navigation with turn-by-turn directions.

How to display NBMap Widget:

  • The NBMap widget is displayed using the **"NBMap" **class from the "nb_maps_flutter" package. It is placed in the stack of widgets and is provided with various properties like onMapCreated, initialCameraPosition, myLocationEnabled, etc., to configure its behavior.

Customize marker image of route origin and destination:

The marker image for the route origin and destination is customized using the method loadAssetImage. It loads a custom marker image from the assets folder and adds it to the map using the "addImage" method of the "NextbillionMapController".

Fetch route with RouteRequestParams: The route is fetched using the _fetchRoute method. It takes the destination coordinates, constructs a RouteRequestParams object with various route parameters like origin, destination, unit, mode, and geometryType, and then calls the NBNavigation.fetchRoute method to retrieve the route information.

Draw routes with route result: The drawn routes are displayed on the map using the drawRoutes method. It takes a list of DirectionsRoute objects and calls navNextBillionMap.drawRoute to render the route on the map.

Start Navigation with LauncherConfig: The navigation is started using the _startNavigation method. It constructs a NavigationLauncherConfig object with various configuration options like route, locationLayerRenderMode, enableDissolvedRouteLine, shouldSimulateRoute, etc., and then calls NBNavigation.startNavigation to begin the navigation.

The code also includes various event handlers like _onMapCreated, _onStyleLoadedCallback, _onMapLongClick, _onMapClick, _onUserLocationUpdate, and _onCameraTrackingChanged, which handle map interactions, user location updates, and camera tracking changes.

© 2024 NextBillion.ai all rights reserved.