Launch Embedded NavigationView

The example shows:

  1. Displaying a Map: Using the NBMap widget to display a map.
  2. Fetching Routes: Fetching routes from a given origin to a destination with RouteRequestParams.
  3. Drawing Routes: Drawing fetched routes on the map.
  4. Handling User Interactions: Handling map interactions such as long-press and click events to select destinations and fetch routes.
  5. Displaying Embedded Navigation View: Launching an embedded navigation view with specific navigation options.
  6. Get Navigation Progress: Updates and displays navigation progress, including distance remaining, duration remaining, and waypoint arrival information.
  7. Starting and Cancelling Navigation: Manages the state of starting and cancelling navigation, displaying an embedded navigation view when navigation starts.
documentation imagedocumentation image
Android snapshotiOS snapshot

For all code examples, refer to Flutter Navigation Code Example

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

Code Summary

  • Initialization: Initialises the NBMap and sets up necessary callbacks.
  • Fetching and Drawing Routes: Handles the logic for fetching routes from a specified origin to a destination and drawing these routes on the map.
  • Navigation Management: Manages navigation states, such as starting and cancelling navigation, and integrates the embedded navigation view to provide turn-by-turn navigation.
  • User Interface: Displays navigation-related information, such as distance remaining, duration remaining, and waypoint arrival information, as well as buttons for starting and clearing routes.

Detailed Analysis

Displaying NBMap Widget

  • Initialization: The NBMap widget is initialised with various parameters, including callbacks for map creation, style loading, long clicks, and user location updates. When the map is created and the style is loaded, the map is set up to handle user interactions and display the current location.

Fetching Route with RouteRequestParams

  • Route Request: When the user long-presses on the map to set a destination, a route request is initiated using the RouteRequestParams. This request includes the origin (current location), destination (selected point), and other route options such as avoiding tolls or ferries and selecting travel modes.

Drawing Routes with Route Result

  • Visualisation: After fetching the routes, they are drawn on the map. The map is updated to display the routes, and the camera view is adjusted to fit the bounds of the routes.

Launch Embedded NavigationView with Navigation Options

  • Starting Navigation: When the user decides to start navigation, the embedded navigation view is launched with the specified navigation options. This view provides real-time navigation updates, including progress changes, waypoint arrivals, and rerouting information.
  • Handling Navigation States: The state of navigation (started or cancelled) is managed within the widget, and relevant updates are displayed to the user.

Above example provides a comprehensive demonstration of functionalities - initialising the map, fetching and displaying routes, and managing an embedded navigation experience - which are critical when integrating Nextbillion Maps for navigation purposes within a Flutter application.

© 2024 NextBillion.ai all rights reserved.