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.
docs-imagedocs-image
Android snapshotiOS snapshot

For all code examples, refer to Flutter Navigation Code Example

LaunchEmbeddedNavigationView view source

1import 'dart:math';
2
3import 'package:flutter/foundation.dart';
4import 'package:flutter/material.dart';
5import 'package:flutter/services.dart';
6import 'package:fluttertoast/fluttertoast.dart';
7import 'package:nb_navigation_flutter/nb_navigation_flutter.dart';
8import 'package:nb_navigation_flutter_example/constants.dart';
9
10class LaunchEmbeddedNavigationView extends StatefulWidget {
11 static const String title = "Launch Embedded NavigationView";
12
13 const LaunchEmbeddedNavigationView({super.key});
14
15
16 LaunchEmbeddedNavigationViewState createState() => LaunchEmbeddedNavigationViewState();
17}
18
19class 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
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
327 void initState() {
328 super.initState();
329 }
330
331
332 void dispose() {
333 // TODO: implement dispose
334 super.dispose();
335 }
336
337
338 void didUpdateWidget(LaunchEmbeddedNavigationView oldWidget) {
339 // TODO: implement didUpdateWidget
340 super.didUpdateWidget(oldWidget);
341 }
342
343
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.