diff options
-rw-r--r-- | .gitreview | 5 | ||||
-rw-r--r-- | License.md | 201 | ||||
-rw-r--r-- | assets/car2.png | bin | 0 -> 25120 bytes | |||
-rw-r--r-- | lib/config.dart | 129 | ||||
-rw-r--r-- | lib/homepage.dart | 191 | ||||
-rw-r--r-- | lib/kuksa/class-provider.dart | 39 | ||||
-rw-r--r-- | lib/kuksa/class.dart | 52 | ||||
-rw-r--r-- | lib/kuksa/config.dart | 47 | ||||
-rw-r--r-- | lib/kuksa/intial-connection.dart | 43 | ||||
-rw-r--r-- | lib/kuksa/onBoarding.dart | 64 | ||||
-rw-r--r-- | lib/kuksa/paths.dart | 11 | ||||
-rw-r--r-- | lib/kuksa/vehicle-methods.dart | 148 | ||||
-rw-r--r-- | lib/main.dart | 29 | ||||
-rw-r--r-- | lib/map/Show-route.dart | 152 | ||||
-rw-r--r-- | lib/map/bottom-card.dart | 55 | ||||
-rw-r--r-- | lib/map/map-response.dart | 65 | ||||
-rw-r--r-- | lib/map/turnNavigation.dart | 231 | ||||
-rw-r--r-- | lib/mapbox.dart | 293 | ||||
-rw-r--r-- | lib/provider.dart | 95 | ||||
-rw-r--r-- | lib/search.dart | 69 | ||||
-rw-r--r-- | pubspec.yaml | 96 |
21 files changed, 2015 insertions, 0 deletions
diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..11fc1dd --- /dev/null +++ b/.gitreview @@ -0,0 +1,5 @@ +[gerrit] +host=gerrit.automotivelinux.org +port=29418 +project=apps/flutter-navigation +defaultbranch=master diff --git a/License.md b/License.md new file mode 100644 index 0000000..a0b7d15 --- /dev/null +++ b/License.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2022] [Hritik Chouhan] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/assets/car2.png b/assets/car2.png Binary files differnew file mode 100644 index 0000000..5b5af09 --- /dev/null +++ b/assets/car2.png diff --git a/lib/config.dart b/lib/config.dart new file mode 100644 index 0000000..cda553b --- /dev/null +++ b/lib/config.dart @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter_navigation/kuksa/intial-connection.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:yaml/yaml.dart'; + + +class GetConfig extends ConsumerStatefulWidget { + const GetConfig({Key? key, required this.client}) : super(key: key); + final HttpClient client; + + @override + ConsumerState<GetConfig> createState() => _GetConfigState(); +} + +class _GetConfigState extends ConsumerState<GetConfig> { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + final configStateProvider = ref.read(ConfigStateprovider.notifier); + + String configFilePath = '/etc/xdg/AGL/nav_config.yaml'; + String mapboxFilePath = '/etc/default/mapboxkey'; + + String keyContent = ""; + + final mapboxKeyFile = File(mapboxFilePath); + + final configFile = File(configFilePath); + configFile.readAsString().then((content) { + final dynamic yamlMap = loadYaml(content); + configStateProvider.update( + hostname: yamlMap['hostname'], + port: yamlMap['port'], + kuksaAuthToken: yamlMap['kuskaAuthToken'], + ); + }); + mapboxKeyFile.readAsString().then((content) { + keyContent = content.split(':')[1].trim(); + if (keyContent.isNotEmpty && keyContent != 'YOU_NEED_TO_SET_IT_IN_LOCAL_CONF') { + configStateProvider.update(mapboxAccessToken: keyContent); + } else { + print("WARNING: Mapbox API Key not found !"); + } + }); + }); + } + + @override + Widget build(BuildContext context) { + final config = ref.watch(ConfigStateprovider); + if (config.hostname == "" || + config.port == 0 || + config.kuksaAuthToken == "" || + config.mapboxAccessToken == "") { + return Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: const [ + Text("ERROR", + style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), + Text( + "Something Wrong with config file! Check config.yaml file and restart"), + ], + )), + ); + } + return InitialScreen(client: widget.client); + } +} + +class Config { + Config({ + required this.hostname, + required this.port, + required this.kuksaAuthToken, + required this.mapboxAccessToken, + + }); + final String hostname; + final int port; + final String kuksaAuthToken; + final String mapboxAccessToken; + + Config copywith({ + String? hostname, + int? port, + String? kuksaAuthToken, + String? mapboxAccessToken, + }) => + Config( + hostname: hostname ?? this.hostname, + port: port ?? this.port, + kuksaAuthToken: kuksaAuthToken ?? this.kuksaAuthToken, + mapboxAccessToken: mapboxAccessToken ?? this.mapboxAccessToken, + ); +} + +final ConfigStateprovider = +StateNotifierProvider<ConfigStateNotifier, Config>( + (ref) => ConfigStateNotifier()); + +class ConfigStateNotifier extends StateNotifier<Config> { + ConfigStateNotifier() : super(_initialValue); + static final Config _initialValue = Config( + hostname: "", + port: 0, + kuksaAuthToken: "", + mapboxAccessToken: "", + ); + void update({ + String? hostname, + int? port, + String? kuksaAuthToken, + String? mapboxAccessToken, + }) { + state = state.copywith( + hostname: hostname, + port: port, + kuksaAuthToken: kuksaAuthToken, + mapboxAccessToken: mapboxAccessToken, + ); + } +} diff --git a/lib/homepage.dart b/lib/homepage.dart new file mode 100644 index 0000000..4933741 --- /dev/null +++ b/lib/homepage.dart @@ -0,0 +1,191 @@ +// 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_navigation/kuksa/class-provider.dart'; +import 'package:flutter_navigation/kuksa/class.dart'; +import 'package:flutter_navigation/map/map-response.dart'; +import 'package:flutter_navigation/provider.dart'; +import 'package:flutter_navigation/search.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:latlong2/latlong.dart'; + + +import 'map/Show-route.dart'; + +class MyHomePage extends ConsumerStatefulWidget { + const MyHomePage({Key? key, required this.title}) : super(key: key); + + + final String title; + + @override + ConsumerState<MyHomePage> createState() => _MyHomePageState(); +} + +class _MyHomePageState extends ConsumerState<MyHomePage> { + + + late MapController mapController; + TextEditingController _destinationController = TextEditingController(); + + + + @override + void initState() { + // TODO: implement initState + super.initState(); + mapController = MapController(); + } + + 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 + Widget build(BuildContext context) { + + VehicleSignal vehicleSignal = ref.watch(vehicleSignalProvider); + LatLng center = LatLng(vehicleSignal.currentLatitude, vehicleSignal.currentLongitude); + + _destinationController.text = ref.watch(DestinationAdressProvider); + final config = ref.read(ConfigStateprovider); + + + + return Scaffold( + + body: Stack( + children: [ + + FlutterMap( + mapController: mapController, + options: MapOptions( + center: center, + minZoom: 12, + zoom: 12, + + maxZoom: 25.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, + + }, + ), + MarkerLayerOptions( + rotate: true, + markers: [ + Marker( + point: center, + width: 70, + height: 70, + builder: (context) => + const Icon( + + Icons.location_pin, + size: 40, + color: Colors.red, + ) + + ), + ], + ), + + ], + ), + Container( + padding: EdgeInsets.all(8), + + alignment: Alignment.topLeft, + child: TextFormField( + + controller: _destinationController, + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + border: OutlineInputBorder(), + icon: Icon(Icons.location_pin,color: Colors.black,), + hintText: "Choose your destination", + + + ), + + onTap: (){ + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => SearchPage(iscurrent: false), + ), + ); + }, + ), + ), + ], + ), + floatingActionButton: FloatingActionButton.extended( + onPressed: () async{ + + + LatLng current = LatLng(vehicleSignal.currentLatitude, vehicleSignal.currentLongitude); + LatLng destination = LatLng(vehicleSignal.destinationLatitude,vehicleSignal.destinationLongitude); + + if(destination != LatLng(0,0)){ + + Map RouteResponse = await getDirectionsAPIResponse(current,destination,ref); + + if(RouteResponse.isNotEmpty){ + List RouteCoordinates = RouteResponse['geometry']['coordinates']; + List<LatLng> polyline =[]; + + for(int i =0; i<RouteCoordinates.length ;i++){ + polyline.add(LatLng((RouteCoordinates[i][1]).toDouble(),(RouteCoordinates[i][0]).toDouble())); + + } + ref.read(polylineprovider.notifier).update(polyline); + Map response = await getAdress(current,ref); + String curradress = response['features'][0]['place_name']; + num duration = RouteResponse['duration']; + String time = ConvertToTime(duration); + + + double distanc = ((RouteResponse['distance']).toDouble()); + int distance = (distanc/1000).toInt(); + + + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => + NavigationHome( polyLine: polyline, CurrAddress: curradress,Duration: time,Distance: distance,))); + + } + + + + } + + + }, + label: const Text('Show Route'), + backgroundColor: Colors.purple, + icon: const Icon(Icons.drive_eta_rounded), + ), + + + ); + } +} diff --git a/lib/kuksa/class-provider.dart b/lib/kuksa/class-provider.dart new file mode 100644 index 0000000..1169f6c --- /dev/null +++ b/lib/kuksa/class-provider.dart @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'class.dart'; + +final vehicleSignalProvider = +StateNotifierProvider<VehicleSignalNotifier, VehicleSignal>( + (ref) => VehicleSignalNotifier(), +); + +class VehicleSignalNotifier extends StateNotifier<VehicleSignal> { + VehicleSignalNotifier() : super(_initialValue); + static final VehicleSignal _initialValue = VehicleSignal( + + currentLatitude: 31.706964, + currentLongitude: 76.933138, + + destinationLatitude: 0, + destinationLongitude: 0, + ); + void update({ + + double? currentLatitude, + double? currentLongitude, + + double? destinationLatitude, + double? destinationLongitude, + }) { + state = state.copyWith( + + currentLatitude: currentLatitude, + currentLongitude: currentLongitude, + + destinationLatitude: destinationLatitude, + destinationLongitude: destinationLongitude, + ); + } +}
\ No newline at end of file diff --git a/lib/kuksa/class.dart b/lib/kuksa/class.dart new file mode 100644 index 0000000..2d64ac9 --- /dev/null +++ b/lib/kuksa/class.dart @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: Apache-2.0 + +class VehicleSignal { + VehicleSignal({ + + required this.currentLatitude, + required this.currentLongitude, + + required this.destinationLatitude, + required this.destinationLongitude, + }); + + + final double currentLongitude; + final double currentLatitude; + final double destinationLongitude; + final double destinationLatitude; + + + VehicleSignal copyWith({ + + double? currentLongitude, + double? currentLatitude, + + double? destinationLongitude, + double? destinationLatitude, + }) { + return VehicleSignal( + + currentLatitude: currentLatitude ?? this.currentLatitude, + currentLongitude: currentLongitude ?? this.currentLongitude, + + destinationLatitude: destinationLatitude ?? this.destinationLatitude, + destinationLongitude: destinationLongitude ?? this.destinationLongitude, + ); + } +} + +class info{ + info({required this.Duration, required this.Distance, required this.instruction}); + + final num Duration; + final num Distance; + final String instruction; + + info copywith({num? Duration , num? Distance, String? instruction}){ + return info(Duration: Duration ?? this.Duration, + Distance: Distance ?? this.Distance, + instruction: instruction ?? this.instruction); + + } +}
\ No newline at end of file diff --git a/lib/kuksa/config.dart b/lib/kuksa/config.dart new file mode 100644 index 0000000..5fb6965 --- /dev/null +++ b/lib/kuksa/config.dart @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:io'; + +import 'package:flutter_navigation/config.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:http/http.dart' as http; + + +// class VehicleSignalConfig { +// static String authTokenPath = "cert/all_read_write.json.token"; +// static String hostname = "localhost"; +// static int port = 8090; +// static String uri = "ws://${hostname}:${port}"; +// static String s_uri = "wss://${hostname}:${port}"; +// static String authToken = +// "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJrdWtzYS52YWwiLCJpc3MiOiJFY2xpcHNlIEtVS1NBIERldiIsImFkbWluIjp0cnVlLCJtb2RpZnlUcmVlIjp0cnVlLCJpYXQiOjE1MTYyMzkwMjIsImV4cCI6MTc2NzIyNTU5OSwia3Vrc2EtdnNzIjp7IioiOiJydyJ9fQ.p2cnFGH16QoQ14l6ljPVKggFXZKmD-vrw8G6Vs6DvAokjsUG8FHh-F53cMsE-GDjyZH_1_CrlDCnbGlqjsFbgAylqA7IAJWp9_N6dL5p8DHZTwlZ4IV8L1CtCALs7XVqvcQKHCCzB63Y8PgVDCAqpQSRb79JPVD4pZwkBKpOknfEY5y9wfbswZiRKdgz7o61_oFnd-yywpse-23HD6v0htThVF1SuGL1PuvGJ8p334nt9bpkZO3gaTh1xVD_uJMwHzbuBCF33_f-I5QMZO6bVooXqGfe1zvl3nDrPEjq1aPulvtP8RgREYEqE6b2hB8jouTiC_WpE3qrdMw9sfWGFbm04qC-2Zjoa1yYSXoxmYd0SnliSYHAad9aXoEmFENezQV-of7sc-NX1-2nAXRAEhaqh0IRuJwB4_sG7SvQmnanwkz-sBYxKqkoFpOsZ6hblgPDOPYY2NAsZlYkjvAL2mpiInrsmY_GzGsfwPeAx31iozImX75rao8rm-XucAmCIkRlpBz6MYKCjQgyRz3UtZCJ2DYF4lKqTjphEAgclbYZ7KiCuTn9HualwtEmVzHHFneHMKl7KnRQk-9wjgiyQ5nlsVpCCblg6JKr9of4utuPO3cBvbjhB4_ueQ40cpWVOICcOLS7_w0i3pCq1ZKDEMrYDJfz87r2sU9kw1zeFQk"; +// } + +final sockConnectprovider = FutureProvider.family<WebSocket, HttpClient>( + (ref, client) => connect(client,ref)); + + + +// load certificates and set context and returns http client +Future<HttpClient> initializeClient() async { + + + SecurityContext ctx = SecurityContext.defaultContext; + + HttpClient client = HttpClient(context: ctx) + ..findProxy = null + ..badCertificateCallback = (cert, host, port) { + return true; + }; + return client; +} + + + +Future<WebSocket> connect(HttpClient client, ref) async { + final config = ref.read(ConfigStateprovider); + WebSocket socket = await WebSocket.connect( + "wss://${config.hostname}:${config.port}", + customClient: client); + return socket; +} diff --git a/lib/kuksa/intial-connection.dart b/lib/kuksa/intial-connection.dart new file mode 100644 index 0000000..53879d9 --- /dev/null +++ b/lib/kuksa/intial-connection.dart @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'config.dart'; +import 'onBoarding.dart'; + + +class InitialScreen extends ConsumerWidget { + InitialScreen({Key? key, required this.client}) : super(key: key); + final HttpClient client; + late WebSocket socket; + + @override + Widget build(BuildContext context, ref) { + + final sockConnect = ref.watch(sockConnectprovider(client)); + + + return sockConnect.when( + data: (socket) { + + this.socket = socket; + this.socket.pingInterval = const Duration(seconds: 2); + return OnBoardingPage(client: client, socket: this.socket); + }, + error: (e, stk) { + print(e); + ref.refresh(sockConnectprovider(client)); + return const Scaffold( + backgroundColor: Colors.black, + body: Center(child: Text('error',style: TextStyle(color: Colors.white),)), + ); + }, + loading: () => const Scaffold( + backgroundColor: Colors.black, + body: Center(child: Text('loading',style: TextStyle(color: Colors.white))), + ), + ); + } +}
\ No newline at end of file diff --git a/lib/kuksa/onBoarding.dart b/lib/kuksa/onBoarding.dart new file mode 100644 index 0000000..84c85bd --- /dev/null +++ b/lib/kuksa/onBoarding.dart @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_navigation/homepage.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_navigation/kuksa/vehicle-methods.dart'; + +import 'config.dart'; + + +class OnBoardingPage extends ConsumerStatefulWidget { + const OnBoardingPage({Key? key, required this.client, required this.socket}) + : super(key: key); + final WebSocket socket; + final HttpClient client; + + @override + ConsumerState<OnBoardingPage> createState() => _OnBoardingPageState(); +} + +class _OnBoardingPageState extends ConsumerState<OnBoardingPage> { + late Timer _timer; + + + @override + void initState() { + + super.initState(); + VISS.init(widget.socket,ref); + _timer = Timer.periodic(const Duration(seconds: 2), (timer) { + // print(widget.socket.readyState); + if (widget.socket.readyState == 3) { + ref.refresh(sockConnectprovider(widget.client)); + } + }); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + widget.socket.listen( + (data) { + VISS.parseData(ref, data); + // print(data); + }, + onError: (e, stk) { + print(e.toString()); + ref.refresh(sockConnectprovider(widget.client)); + }, + ); + }); + } + + @override + void dispose() { + super.dispose(); + _timer.cancel(); + widget.socket.close(786887, "Connection lost with server!"); + } + + @override + Widget build(BuildContext context) { + return MyHomePage(title: 'navigation',); + } +}
\ No newline at end of file diff --git a/lib/kuksa/paths.dart b/lib/kuksa/paths.dart new file mode 100644 index 0000000..df22644 --- /dev/null +++ b/lib/kuksa/paths.dart @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: Apache-2.0 + +class VSPath { + + static const String vehicleCurrentLatitude = "Vehicle.CurrentLocation.Latitude"; + static const String vehicleCurrentLongitude = "Vehicle.CurrentLocation.Longitude"; + static const String vehicleDestinationLatitude = "Vehicle.Cabin.Infotainment.Navigation.DestinationSet.Latitude"; + static const String vehicleDestinationLongitude = "Vehicle.Cabin.Infotainment.Navigation.DestinationSet.Longitude"; + + +}
\ No newline at end of file diff --git a/lib/kuksa/vehicle-methods.dart b/lib/kuksa/vehicle-methods.dart new file mode 100644 index 0000000..dbbdc3b --- /dev/null +++ b/lib/kuksa/vehicle-methods.dart @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:convert'; +import 'dart:io'; + + +import 'package:flutter_navigation/config.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_navigation/kuksa/paths.dart'; + +import 'class-provider.dart'; + +class VISS { + static const requestId = "test-id"; + static void init(WebSocket socket, WidgetRef ref) { + authorize(socket,ref); + subscribe(socket,ref, VSPath.vehicleCurrentLatitude); + subscribe(socket,ref, VSPath.vehicleCurrentLongitude); + subscribe(socket,ref, VSPath.vehicleDestinationLatitude); + subscribe(socket,ref, VSPath.vehicleDestinationLongitude); + + } + + static void update(WebSocket socket,WidgetRef ref) { + get(socket,ref, VSPath.vehicleCurrentLatitude); + get(socket,ref, VSPath.vehicleCurrentLongitude); + get(socket, ref,VSPath.vehicleDestinationLatitude); + get(socket,ref, VSPath.vehicleDestinationLongitude); + + + + } + + static void authorize(WebSocket socket,WidgetRef ref) { + final config = ref.read(ConfigStateprovider); + Map<String, dynamic> map = { + "action": "authorize", + "tokens": config.kuksaAuthToken, + "requestId": requestId + }; + socket.add(jsonEncode(map)); + } + + static void get(WebSocket socket, WidgetRef ref,String path) { + final config = ref.read(ConfigStateprovider); + + Map<String, dynamic> map = { + "action": "get", + "tokens": config.kuksaAuthToken, + "path": path, + "requestId": requestId + }; + socket.add(jsonEncode(map)); + } + + static void set(WebSocket socket, WidgetRef ref,String path, String value) { + final config = ref.read(ConfigStateprovider); + + Map<String, dynamic> map = { + "action": "set", + "tokens": config.kuksaAuthToken, + "path": path, + "requestId": requestId, + "value": value + }; + socket.add(jsonEncode(map)); + } + + static void subscribe(WebSocket socket,WidgetRef ref, String path) { + final config = ref.read(ConfigStateprovider); + + Map<String, dynamic> map = { + "action": "subscribe", + "tokens": config.kuksaAuthToken, + "path": path, + "requestId": requestId + }; + socket.add(jsonEncode(map)); + } + + static String? numToGear(int? number) { + switch (number) { + case -1: + return 'R'; + case 0: + return 'N'; + case 126: + return 'P'; + case 127: + return 'D'; + default: + return null; + } + } + + static void parseData(WidgetRef ref, String data) { + final vehicleSignal = ref.read(vehicleSignalProvider.notifier); + Map<String, dynamic> dataMap = jsonDecode(data); + if (dataMap["action"] == "subscription" || dataMap["action"] == "get") { + if (dataMap.containsKey("data")) { + if ((dataMap["data"] as Map<String, dynamic>).containsKey("dp") && + (dataMap["data"] as Map<String, dynamic>).containsKey("path")) { + String path = dataMap["data"]["path"]; + Map<String, dynamic> dp = dataMap["data"]["dp"]; + if (dp.containsKey("value")) { + if (dp["value"] != "---") { + switch (path) { + + + case VSPath.vehicleCurrentLatitude: + vehicleSignal.update( + currentLatitude: double.parse(dp["value"])); + break; + case VSPath.vehicleCurrentLongitude: + vehicleSignal.update( + currentLongitude: double.parse(dp["value"])); + break; + case VSPath.vehicleDestinationLatitude: + vehicleSignal.update( + destinationLatitude: double.parse(dp["value"])); + break; + case VSPath.vehicleDestinationLongitude: + vehicleSignal.update( + destinationLongitude: double.parse(dp["value"])); + break; + + default: + print("$path Not Available yet!"); + } + } else { + print("ERROR:Value not available yet! Set Value of $path"); + } + } else { + print("ERROR:'value': Key not found!"); + } + } else if ((!dataMap["data"] as Map<String, dynamic>) + .containsKey("path")) { + print("ERROR:'path':key not found !"); + } else if ((dataMap["data"] as Map<String, dynamic>) + .containsKey("dp")) { + print("ERROR:'dp':key not found !"); + } + } else { + print("ERROR:'data':key not found!"); + } + } + } +}
\ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..8300961 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_navigation/config.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + + +import 'kuksa/config.dart'; + + + +Future<void> main() async { + WidgetsFlutterBinding.ensureInitialized(); + HttpClient client = await initializeClient(); + + + runApp( + ProviderScope( + child: MaterialApp( + home: GetConfig(client: client), + ), + ), + ); +} + + + 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<LatLng> 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<Map> 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<Map> 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<TurnNavigation> createState() => _TurnNavigationState(); +} + +class _TurnNavigationState extends ConsumerState<TurnNavigation> { + + late Timer timer; + late MapController mapController; + List<LatLng> 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<LatLng> currpolyline =[]; + for(int i =0; i<RouteCoordinates.length ;i++){ + currpolyline.add(LatLng((RouteCoordinates[i][1]).toDouble(),(RouteCoordinates[i][0]).toDouble())); + + } + ref.read(polylineprovider.notifier).update(currpolyline); + double rotationDegree = 0; + int n = currpolyline.length; + if (currpolyline.isNotEmpty && n > 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<double> newA = convertCoord(a); + List<double> newB = convertCoord(b); + double slope = (newB[1] - newA[1]) / (newB[0] - newA[0]); + + return ((atan(slope) * 180) / pi); + } + + List<double> 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); + }, + ) + ), + ], + ), + ); + + } +} + + diff --git a/lib/mapbox.dart b/lib/mapbox.dart new file mode 100644 index 0000000..f7bf91e --- /dev/null +++ b/lib/mapbox.dart @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:mapbox_search/mapbox_search.dart'; + +class MapBoxPlaceSearchWidget extends StatefulWidget { + MapBoxPlaceSearchWidget({ + required this.apiKey, + required this.onSelected, + // this.onSearch, + this.fontSize, + this.searchHint = 'Search', + required this.context, + this.height, + this.popOnSelect = false, + this.location, + this.country, + }); + + /// True if there is different search screen and you want to pop screen on select + final bool popOnSelect; + + ///To get the height of the page + final BuildContext context; + + /// Height of whole search widget + final double? height; + + /// API Key of the MapBox. + final String apiKey; + + /// The callback that is called when one Place is selected by the user. + final void Function(MapBoxPlace place) onSelected; + + /// The callback that is called when the user taps on the search icon. + // final void Function(MapBoxPlaces place) onSearch; + + /// The point around which you wish to retrieve place information. + final Location? location; + + ///Limits the search to the given country + /// + /// Check the full list of [supported countries](https://docs.mapbox.com/api/search/) for the MapBox API + final String? country; + + ///Search Hint Localization + final String? searchHint; + + ///Font Size + final String? fontSize; + + @override + _MapBoxPlaceSearchWidgetState createState() => + _MapBoxPlaceSearchWidgetState(); +} + +class _MapBoxPlaceSearchWidgetState extends State<MapBoxPlaceSearchWidget> + with SingleTickerProviderStateMixin { + TextEditingController _textEditingController = TextEditingController(); + late AnimationController _animationController; + + + late Animation _containerHeight; + + + late Animation _listOpacity; + + List<MapBoxPlace> _placePredictions = []; + + + + late Timer _debounceTimer; + + + + + @override + void initState() { + _animationController = + AnimationController(vsync: this, duration: Duration(milliseconds: 500)); + + _containerHeight = Tween<double>( + begin: 73, + end: widget.height ?? + MediaQuery.of(widget.context).size.height - 60 ?? + 300) + .animate( + CurvedAnimation( + curve: Interval(0.0, 0.5, curve: Curves.easeInOut), + parent: _animationController, + ), + ); + _listOpacity = Tween<double>( + begin: 0, + end: 1, + ).animate( + CurvedAnimation( + curve: Interval(0.5, 1.0, curve: Curves.easeInOut), + parent: _animationController, + ), + ); + super.initState(); + } + + @override + void dispose() { + _debounceTimer.cancel(); + _animationController?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) => Container( + padding: EdgeInsets.symmetric(horizontal: 5), + width: MediaQuery.of(context).size.width, + child: _searchContainer( + child: _searchInput(context), + ), + ); + + // Widgets + Widget _searchContainer({Widget? child}) { + return AnimatedBuilder( + animation: _animationController, + builder: (context, _) { + return Container( + height: _containerHeight.value, + decoration: _containerDecoration(), + padding: EdgeInsets.only(left: 0, right: 0, top: 15), + alignment: Alignment.center, + child: Column( + children: <Widget>[ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: child, + ), + SizedBox(height: 10), + Expanded( + child: Opacity( + opacity: _listOpacity.value, + child: ListView( + // addSemanticIndexes: true, + // itemExtent: 10, + children: <Widget>[ + for (var places in _placePredictions) + _placeOption(places), + ], + ), + ), + ), + ], + ), + ); + }); + } + + Widget _searchInput(BuildContext context) { + return Center( + child: Row( + children: <Widget>[ + Expanded( + child: TextField( + decoration: _inputStyle(), + controller: _textEditingController, + style: TextStyle( + fontSize: MediaQuery.of(context).size.width * 0.04, + ), + onChanged: (value) async { + // _debounceTimer?.cancel(); + _debounceTimer = Timer( + Duration(milliseconds: 750), + () async { + await _autocompletePlace(value); + if (mounted) { + setState(() {}); + } + }, + ); + }, + ), + ), + Container(width: 15), + GestureDetector( + child: Icon(Icons.search, color: Colors.blue), + onTap: () {}, + ) + ], + ), + ); + } + + Widget _placeOption(MapBoxPlace prediction) { + String? place = prediction.text; + String? fullName = prediction.placeName; + + return MaterialButton( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 3), + onPressed: () => _selectPlace(prediction), + child: ListTile( + title: Text( + place?.length != 0 ? place!.length < 45 + ? "$place" + : "${place?.replaceRange(45, place.length, "")} ..." : "", + style: TextStyle(fontSize: MediaQuery.of(context).size.width * 0.04), + maxLines: 1, + ), + subtitle: Text( + fullName!, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: MediaQuery.of(context).size.width * 0.03), + maxLines: 1, + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 0, + ), + ), + ); + } + + // Styling + InputDecoration _inputStyle() { + return InputDecoration( + hintText: widget.searchHint, + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 0.0, vertical: 0.0), + ); + } + + BoxDecoration _containerDecoration() { + return BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(6.0)), + boxShadow: [ + BoxShadow(color: Colors.black, blurRadius: 0, spreadRadius: 0) + ], + ); + } + + // Methods + Future _autocompletePlace(String input) async { + /// Will be called when the input changes. Making callbacks to the Places + /// Api and giving the user Place options + /// + if (input.length > 0) { + var placesSearch = PlacesSearch( + apiKey: widget.apiKey, + country: widget.country, + ); + + final predictions = await placesSearch.getPlaces( + input, + location: widget.location, + ); + + await _animationController.animateTo(0.5); + + + + setState(() => _placePredictions = predictions!); + + await _animationController.forward(); + } else { + + await _animationController.animateTo(0.5); + setState(() => _placePredictions = []); + await _animationController.reverse(); + } + } + + void _selectPlace(MapBoxPlace prediction) async { + /// Will be called when a user selects one of the Place options. + // Sets TextField value to be the location selected + _textEditingController.value = TextEditingValue( + text: prediction.placeName.toString(), + selection: TextSelection.collapsed(offset: prediction.placeName!.length), + ); + + // Makes animation + await _animationController.animateTo(0.5); + setState(() { + _placePredictions = []; + + }); + _animationController.reverse(); + + // Calls the `onSelected` callback + widget.onSelected(prediction); + if (widget.popOnSelect) Navigator.pop(context); + } +}
\ No newline at end of file diff --git a/lib/provider.dart b/lib/provider.dart new file mode 100644 index 0000000..228672f --- /dev/null +++ b/lib/provider.dart @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:latlong2/latlong.dart'; + +import 'kuksa/class.dart'; + + +final currlnglatProvider = StateNotifierProvider<currentLngLat,LatLng>( + (ref)=> currentLngLat(), +); + +class currentLngLat extends StateNotifier<LatLng>{ + currentLngLat() : super( + LatLng(31.706964,76.933138), + ); + Future<void> update(value) async{ + + state = value; + } + +} + +final destinationlnglatProvider = StateNotifierProvider<distiLngLat,LatLng>( + (ref)=> distiLngLat(), +); + +class distiLngLat extends StateNotifier<LatLng>{ + distiLngLat() : super( + LatLng(0,0), + ); + Future<void> update(value) async{ + state = value; + } + +} + +final CurrentAdressProvider = StateNotifierProvider<currentAdress,String>( + (ref)=> currentAdress(), +); + +class currentAdress extends StateNotifier<String>{ + currentAdress() : super( + '' + ); + Future<void> update(value)async{ + state = value; + } +} + +final DestinationAdressProvider = StateNotifierProvider<destiAdress,String>( + (ref)=> destiAdress(), +); + +class destiAdress extends StateNotifier<String>{ + destiAdress() : super( + '' + ); + Future<void> update(value)async{ + state = value; + } +} + +final polylineprovider = StateNotifierProvider<PolyLine,List<LatLng>>((ref) => PolyLine()); + +class PolyLine extends StateNotifier<List<LatLng>> { + + PolyLine() : super([]); + void update(List<LatLng> list) { + state = list; + } +} + +final Infoprovider = StateNotifierProvider<Info,info>((ref) => Info()); + +class Info extends StateNotifier<info> { + + Info() : super(initial); + static final info initial = info(Duration: 0, Distance: 0, instruction: ''); + + void update({ + num? Duration, + num? Distance, + String? instruction, + + }){ + state = state.copywith( + Duration: Duration, + Distance: Distance, + instruction: instruction, + ); + + + } +}
\ No newline at end of file diff --git a/lib/search.dart b/lib/search.dart new file mode 100644 index 0000000..9e02450 --- /dev/null +++ b/lib/search.dart @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 + +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_navigation/config.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'package:http/http.dart' as http; +import 'package:latlong2/latlong.dart'; +import 'package:flutter_navigation/kuksa/class-provider.dart'; +import 'package:flutter_navigation/provider.dart'; + +import 'mapbox.dart'; + +class SearchPage extends ConsumerWidget { + bool iscurrent; + + SearchPage({Key? key, required this.iscurrent}) : super(key: key); + final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); + + + @override + Widget build(BuildContext context,ref) { + final config = ref.read(ConfigStateprovider); + + return Scaffold( + key: _scaffoldKey, + floatingActionButton: FloatingActionButton( + child: Icon(Icons.done), + onPressed: () { + Navigator.pop(context); + }, + ), + body: SafeArea( + bottom: false, + child: MapBoxPlaceSearchWidget( + height: 600, + popOnSelect: false, + apiKey: config.mapboxAccessToken, + searchHint: 'Search around your place', + onSelected: (place) async{ + var url = 'https://api.mapbox.com/geocoding/v5/mapbox.places/${place.placeName}.json?proximity=ip&types=place%2Cpostcode%2Caddress&access_token=${config.mapboxAccessToken}'; + http.Response response = await http.get(Uri.parse(url)); + Map data = json.decode(response.body); + double longi = data['features'][0]['center'][0]; + double lati = data['features'][0]['center'][1]; + if(iscurrent){ + LatLng value = LatLng(lati,longi); + ref.read(vehicleSignalProvider.notifier).update(currentLatitude: lati,currentLongitude: longi); + ref.read(CurrentAdressProvider.notifier).update(place.placeName); + + } + else{ + ref.read(vehicleSignalProvider.notifier).update(destinationLatitude: lati,destinationLongitude: longi); + + ref.read(DestinationAdressProvider.notifier).update(place.placeName); + + } + + + + }, + context: context, + ), + ), + ); + } +}
\ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..251aad3 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,96 @@ +name: flutter_navigation +description: A new Flutter project. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.17.6 <3.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + http: ^0.13.5 + flutter_riverpod: ^1.0.4 + mapbox_search: ^3.0.1+2 + flutter_map: ^2.2.0 + latlong2: ^0.8.1 + dio: ^4.0.6 + yaml: ^3.1.1 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - assets/ + + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages |