From 0e820517cbcc6d799cf7f23c7041f3f15b732dc0 Mon Sep 17 00:00:00 2001 From: Hritik Chouhan Date: Thu, 1 Sep 2022 21:47:45 +0200 Subject: Upload Flutter-Navigation app for IVI Flutter Navigation app which Shows current location, Route for destination, search for destination in search bar, turn by turn navigation,duration and distance for destination. Used Mapbox api and Flutter_map plugin and integrated with KUKSA.VAL for current location. Update UI ,Removed unused code and remove hard coded access token. Bug-AGL: SPEC-4548 Signed-off-by: Hritik Chouhan Change-Id: I7314285f7b9cdc6940175758761fcc8615c5ab0e --- lib/map/Show-route.dart | 152 +++++++++++++++++++++++++++++ lib/map/bottom-card.dart | 55 +++++++++++ lib/map/map-response.dart | 65 +++++++++++++ lib/map/turnNavigation.dart | 231 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 503 insertions(+) create mode 100644 lib/map/Show-route.dart create mode 100644 lib/map/bottom-card.dart create mode 100644 lib/map/map-response.dart create mode 100644 lib/map/turnNavigation.dart (limited to 'lib/map') diff --git a/lib/map/Show-route.dart b/lib/map/Show-route.dart new file mode 100644 index 0000000..6bb9987 --- /dev/null +++ b/lib/map/Show-route.dart @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: Apache-2.0 + + +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_navigation/config.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:flutter_navigation/kuksa/class-provider.dart'; +import 'package:flutter_navigation/kuksa/class.dart'; +import 'package:flutter_navigation/map/bottom-card.dart'; +import 'package:flutter_navigation/map/turnNavigation.dart'; + +class NavigationHome extends ConsumerWidget { + final List polyLine; + String CurrAddress; + String Duration; + num Distance; + + NavigationHome({ + Key? key, + required this.polyLine, + required this.CurrAddress, + required this.Duration, + required this.Distance, + + }) : super(key: key); + + + + double tempangle = 0; + @override + Widget build(BuildContext context, WidgetRef ref) { + + final config = ref.read(ConfigStateprovider); + + + + MapController mapController = MapController(); + VehicleSignal vehicleSignal = ref.watch(vehicleSignalProvider); + LatLng currPos = LatLng(vehicleSignal.currentLatitude, vehicleSignal.currentLongitude); + LatLng destiPos = LatLng(vehicleSignal.destinationLatitude, vehicleSignal.destinationLongitude); + + return Scaffold( + + + body: Stack( + children: [ + FlutterMap( + mapController: mapController, + options: MapOptions( + + center: currPos, + minZoom: 1, + zoom: 8, + + maxZoom: 22.0, + keepAlive: true, + ), + layers: [ + + TileLayerOptions( + urlTemplate: "https://api.mapbox.com/styles/v1/hritik3961/cl7j225qm001w14o4xeiqtv36/tiles/256/{z}/{x}/{y}@2x?access_token=${config.mapboxAccessToken}", + additionalOptions: { + "access_token": config.mapboxAccessToken, + }, + ), + if (polyLine.isNotEmpty) + PolylineLayerOptions( + polylineCulling: false, + polylines: [ + + Polyline( + color : Colors.blue, + strokeWidth: 6, + + points: polyLine, + ), + + ], + ), + MarkerLayerOptions( + rotate: true, + markers: [ + Marker( + point: currPos, + width: 70, + height: 70, + builder: (context) => + const Icon( + + Icons.location_pin, + size: 50, + color: Colors.red, + ) + + ), + ], + ), + MarkerLayerOptions( + rotate: true, + markers: [ + Marker( + point: destiPos, + width: 70, + height: 70, + builder: (context) => + const Icon( + + Icons.location_pin, + size: 50, + color: Colors.green, + ) + + ), + ], + ), + + ], + ), + Container( + alignment: Alignment.topLeft, + child :IconButton( + icon: Icon(Icons.arrow_back, color: Colors.white,), + onPressed: (){ + Navigator.pop(context); + }, + ) + ), + bottomDetailCard(context,ref,Distance.toString(),Duration.toString(),CurrAddress), + ], + ), + + floatingActionButton: FloatingActionButton.extended( + backgroundColor: Colors.black, + onPressed: () async{ + + + + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => + TurnNavigation())); + + }, + label: const Text('lets go'), + icon: const Icon(Icons.drive_eta_rounded), + ), + ); + } +} \ No newline at end of file diff --git a/lib/map/bottom-card.dart b/lib/map/bottom-card.dart new file mode 100644 index 0000000..0dc2c34 --- /dev/null +++ b/lib/map/bottom-card.dart @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter/material.dart'; + +import 'package:flutter_navigation/kuksa/class-provider.dart'; +import 'package:flutter_navigation/kuksa/class.dart'; +import 'package:flutter_navigation/provider.dart'; + +Widget bottomDetailCard( + BuildContext context, ref,String distance, String dropOffTime,String CurrAdd) { + VehicleSignal vehicleSignal = ref.watch(vehicleSignalProvider); + String destiadd = ref.read(DestinationAdressProvider); + + return Container( + alignment: Alignment.bottomLeft, + + child: SizedBox( + + width: MediaQuery.of(context).size.width, + child: Card( + + clipBehavior: Clip.antiAlias, + child: Padding( + padding: const EdgeInsets.all(15), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text('$CurrAdd ➡ $destiadd', + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(color: Colors.indigo)), + Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: ListTile( + tileColor: Colors.grey[200], + leading: const Image( + image: AssetImage('assets/car2.png'), + height: 50, + width: 50), + title: const Text('Happy Journey', + style: TextStyle( + fontSize: 18, fontWeight: FontWeight.bold)), + subtitle: Text('$distance km $dropOffTime',style: TextStyle(color: Colors.black),), + + ), + ), + + ]), + ), + ), + ), + ); +} \ No newline at end of file diff --git a/lib/map/map-response.dart b/lib/map/map-response.dart new file mode 100644 index 0000000..cfc50c2 --- /dev/null +++ b/lib/map/map-response.dart @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:convert'; + +import 'package:dio/dio.dart'; +import 'package:flutter_navigation/config.dart'; + +import 'package:http/http.dart' as http; +import 'package:latlong2/latlong.dart'; + + + +String baseUrl = 'https://api.mapbox.com/directions/v5/mapbox'; +String navType = 'driving'; + +Dio _dio = Dio(); + +Future getdrivingRouteUsingMapbox(LatLng source, LatLng destination,ref) async { + final config = ref.read(ConfigStateprovider); + + String url = + '$baseUrl/$navType/${source.longitude},${source.latitude};${destination.longitude},${destination.latitude}?alternatives=true&continue_straight=true&geometries=geojson&language=en&overview=full&steps=true&access_token=${config.mapboxAccessToken}'; + try { + _dio.options.contentType = Headers.jsonContentType; + final responseData = await _dio.get(url); + return responseData.data; + } catch (e) { + print(e); + } +} + +Future getDirectionsAPIResponse(LatLng currentLatLng,LatLng destiLatLng,ref) async { + + final response = await getdrivingRouteUsingMapbox(currentLatLng, destiLatLng,ref); + + if(response != null){ + Map geometry = response['routes'][0]['geometry']; + num duration = response['routes'][0]['duration']; + num distance = response['routes'][0]['distance']; + Map legs = response['routes'][0]['legs'][0]; + + Map modifiedResponse = { + "geometry": geometry, + "duration": duration, + "distance": distance, + "legs" : legs, + }; + return modifiedResponse; + + } + else{ + Map map = {}; + return map; + } + +} + +Future getAdress(LatLng pos,ref) async{ + final config = ref.read(ConfigStateprovider); + + var url = 'https://api.mapbox.com/geocoding/v5/mapbox.places/${pos.longitude},${pos.latitude}.json?&access_token=${config.mapboxAccessToken}'; + http.Response response = await http.get(Uri.parse(url)); + Map data = json.decode(response.body); + return data; +} \ No newline at end of file diff --git a/lib/map/turnNavigation.dart b/lib/map/turnNavigation.dart new file mode 100644 index 0000000..6370e44 --- /dev/null +++ b/lib/map/turnNavigation.dart @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:async'; +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_navigation/config.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:flutter_navigation/kuksa/class-provider.dart'; +import 'package:flutter_navigation/kuksa/class.dart'; +import 'package:flutter_navigation/provider.dart'; + +import 'map-response.dart'; + +class TurnNavigation extends ConsumerStatefulWidget { + TurnNavigation({Key? key,}) : super(key: key); + + @override + ConsumerState createState() => _TurnNavigationState(); +} + +class _TurnNavigationState extends ConsumerState { + + late Timer timer; + late MapController mapController; + List polyLine = []; + + String ConvertToTime(num duration){ + int hour = (duration/3600).toInt(); + int min = (duration%3600).toInt() ; + min = (min/60).toInt(); + String mini = min.toString(); + String Hour = hour.toString(); + String time = "$Hour hr $mini min"; + + return time; + } + + + + + @override + void initState() { + // TODO: implement initState + super.initState(); + mapController = MapController(); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + + timer = Timer.periodic(Duration(seconds: 7), (timer) async{ + VehicleSignal vehicleSignal = ref.read(vehicleSignalProvider); + + LatLng current = LatLng(vehicleSignal.currentLatitude, vehicleSignal.currentLongitude); + mapController.move(current, 18); + LatLng destination = LatLng(vehicleSignal.destinationLatitude,vehicleSignal.destinationLongitude); + + Map RouteResponse = await getDirectionsAPIResponse(current,destination,ref); + + + if(RouteResponse.isNotEmpty){ + + List RouteCoordinates = RouteResponse['geometry']['coordinates']; + + Map steps = RouteResponse['legs']['steps'][0]; + + ref.read(Infoprovider.notifier).update(Duration: RouteResponse['duration'], + Distance: RouteResponse['distance'], instruction: steps['maneuver']['instruction']); + List currpolyline =[]; + for(int i =0; i 1) { + rotationDegree = calcAngle( + currpolyline[0], currpolyline[1]); + + rotationDegree = (rotationDegree.isNaN) ? 0 : rotationDegree; + } + + mapController.rotate(-1 * rotationDegree); + + + } + + + }); + + }); + } + + void dispose(){ + + super.dispose(); + timer.cancel(); + + } + + double tempangle = 0; + double calcAngle(LatLng a, LatLng b) { + List newA = convertCoord(a); + List newB = convertCoord(b); + double slope = (newB[1] - newA[1]) / (newB[0] - newA[0]); + + return ((atan(slope) * 180) / pi); + } + + List convertCoord(LatLng coord) { + double oldLat = coord.latitude; + double oldLong = coord.longitude; + double newLong = (oldLong * 20037508.34 / 180); + double newlat = + (log(tan((90 + oldLat) * pi / 360)) / (pi / 180)) * (20037508.34 / 180); + return [newlat, newLong]; + } + @override + Widget build(BuildContext context) { + + + + + VehicleSignal vehicleSignal = ref.watch(vehicleSignalProvider); + LatLng currPos = LatLng(vehicleSignal.currentLatitude, vehicleSignal.currentLongitude); + polyLine = ref.watch(polylineprovider); + info routeinfo = ref.watch(Infoprovider); + final config = ref.read(ConfigStateprovider); + + + return Scaffold( + body: Stack( + children: [ + FlutterMap( + mapController: mapController, + options: MapOptions( + + center: currPos, + minZoom: 1, + zoom: 12, + + maxZoom: 30.0, + keepAlive: true, + ), + layers: [ + + TileLayerOptions( + urlTemplate: "https://api.mapbox.com/styles/v1/hritik3961/cl7hxzrrf002t15o2j2yh14lm/tiles/256/{z}/{x}/{y}@2x?access_token=${config.mapboxAccessToken}", + additionalOptions: { + "access_token": config.mapboxAccessToken, + }, + ), + if (polyLine.isNotEmpty) + PolylineLayerOptions( + polylineCulling: false, + polylines: [ + + Polyline( + strokeWidth: 3, + points: polyLine, + color: Colors.purple, + ), + + ], + ), + MarkerLayerOptions( + rotate: true, + markers: [ + Marker( + point: currPos, + width: 70, + height: 70, + builder: (context) => + const Icon( + Icons.circle, + size: 40, + color: Colors.green, + + ) + + + ), + ], + ), + + ], + ), + Container( + alignment: Alignment.bottomCenter, + + child: Card( + + color: Colors.black54, + elevation: 5, + child: ListTile( + leading: Icon(Icons.drive_eta_rounded,color: Colors.greenAccent,), + title: Text(routeinfo.instruction,style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ),), + subtitle: Text('Remaining Distance : ${(routeinfo.Distance/1000).toInt().toString()} KM',style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ),), + trailing: Text('Remaining Time : ${ConvertToTime(routeinfo.Duration)}',style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ),), + + ), + + ), + ), + Container( + alignment: Alignment.topLeft, + child :IconButton( + icon: Icon(Icons.arrow_back), + onPressed: (){ + Navigator.pop(context); + }, + ) + ), + ], + ), + ); + + } +} + + -- cgit