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

For all code examples, refer to Flutter Navigation Code Example

FullNavigationExample 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 FullNavigationExample extends StatefulWidget {
11 static const String title = "Full Navigation Experience Example";
12
13 const FullNavigationExample({super.key});
14
15
16 FullNavigationExampleState createState() => FullNavigationExampleState();
17}
18
19class 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
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
260 void initState() {
261 super.initState();
262 }
263
264
265 void dispose() {
266 // TODO: implement dispose
267 super.dispose();
268 }
269
270
271 void didUpdateWidget(FullNavigationExample oldWidget) {
272 // TODO: implement didUpdateWidget
273 super.didUpdateWidget(oldWidget);
274 }
275
276
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.