import 'dart:convert'; import 'dart:io'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'dart:async'; import 'package:flutter/services.dart' show PlatformException, rootBundle; import 'package:flutter/cupertino.dart'; import 'package:location/location.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:geolocator/geolocator.dart'; import 'package:teso/Classes/TesoShop.dart'; import 'package:teso/Pages/Sub_Pages/BusinessDetails.dart'; import 'package:flutter_polyline_points/flutter_polyline_points.dart'; import 'package:teso/util/SizeConfig.dart'; import 'package:teso/util/consts.dart'; import 'package:http/http.dart' as http; class BusinessLocator extends StatefulWidget { @override _BusinessLocatorState createState() => _BusinessLocatorState(); } class _BusinessLocatorState extends State { String? mapstyle; var _future; static LatLng? _initialPosition; Set markers = {}; List? shops; GoogleMapController? mapController; static const double CAMERA_ZOOM = 13.499910354614258; bool routing = false; List polylineCoordinates = []; Map polylines = {}; String? selectedshop = ""; Location location = Location(); String routingMessage = "Finding shops...."; late LocationData _location; String? _error; String? _placeDistance; final startAddressController = TextEditingController(); final destinationAddressController = TextEditingController(); bool ios = false; Future _determinePosition(context) async { setState(() { _error = null; }); try { final LocationData _locationResult = await location.getLocation(); setState(() { _location = _locationResult; _initialPosition = LatLng(_location.latitude!, _location.longitude!); }); await getLocations(); return _initialPosition; } on PlatformException catch (err) { setState(() { _error = err.code; }); return _initialPosition; } } getLocations() async { SharedPreferences prefs = await SharedPreferences.getInstance(); Map requestHeaders = { 'Content-type': 'application/json', 'Authorization': prefs.getString('tokensTeso')! }; var register2 = serverLocation + 'tesobusiness/available'; var client1 = await http.get(Uri.parse(register2), headers: requestHeaders); if (client1.statusCode == 200) { try { var data = jsonDecode(client1.body); shops = List.from( data.map((model) => TesoShop.fromJSON(model)).toList()); } catch (e) { print(e); } } if (shops!.length > 0 && shops != null) shops!.forEach((element) { MarkerId markerId = MarkerId(element.shopID!); Marker marker = Marker( markerId: markerId, position: LatLng(element.latitude!, element.longitude!), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueCyan), infoWindow: InfoWindow( title: element.shopName, snippet: element.shopAddress, ), onTap: () => showModalBottomSheet( context: context, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(30.0)), ), builder: (BuildContext bc) { return buildShopDetails(bc, element, navigateToShop); }, ), ); if (mounted) { setState(() { markers.add(marker); }); } }); } @override void initState() { super.initState(); ios = Platform.isIOS; SharedPreferences.getInstance().then((prefs) { String? currentTheme = prefs.getString("theme"); if (currentTheme == "light") { rootBundle.loadString('assets/styles/light.txt').then((string) { mapstyle = string; }); } else { rootBundle.loadString('assets/styles/dark.txt').then((string) { mapstyle = string; }); } }); _future = _determinePosition(context); location.onLocationChanged.listen((LocationData cLoc) { _initialPosition = LatLng(cLoc.latitude!, cLoc.longitude!); }); } void navigateToShop(TesoShop tesoShop) async { Position _northeastCoordinates; Position _southwestCoordinates; MarkerId markerId = MarkerId(tesoShop.shopName! + " Location"); Marker marker = Marker( markerId: markerId, position: LatLng(tesoShop.latitude!, tesoShop.longitude!), icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen), infoWindow: InfoWindow( title: tesoShop.shopName, snippet: tesoShop.shopAddress, ), ); setState(() { markers.clear(); markers.add(marker); selectedshop = tesoShop.shopName; destinationAddressController.text = selectedshop!; }); Position user = Position( latitude: _initialPosition!.latitude, longitude: _initialPosition!.longitude, accuracy: 100, altitude: 100, heading: 100, speed: 100, speedAccuracy: 100, timestamp: DateTime.now()); Position shopLoc = Position( latitude: tesoShop.latitude!, longitude: tesoShop.longitude!, accuracy: 100, altitude: 100, heading: 100, speed: 100, speedAccuracy: 100, timestamp: DateTime.now()); if (_initialPosition!.latitude <= tesoShop.latitude!) { _southwestCoordinates = user; _northeastCoordinates = shopLoc; } else { _southwestCoordinates = shopLoc; _northeastCoordinates = user; } await createPolylines(user, shopLoc); _calculateDistance(user, shopLoc); mapController!.animateCamera( CameraUpdate.newLatLngBounds( LatLngBounds( northeast: LatLng( _northeastCoordinates.latitude, _northeastCoordinates.longitude, ), southwest: LatLng( _southwestCoordinates.latitude, _southwestCoordinates.longitude, ), ), 100.0, // padding ), ); } Future createPolylines(Position start, Position destination) async { try { setState(() { routing = true; routingMessage = "Calculating route...."; startAddressController.text = "Current Location "; }); polylineCoordinates.clear(); PolylinePoints polylinePoints = PolylinePoints(); PolylineResult result = await polylinePoints.getRouteBetweenCoordinates( mapsKey, PointLatLng(start.latitude, start.longitude), PointLatLng(destination.latitude, destination.longitude), travelMode: TravelMode.driving, ); await Future.delayed(Duration(seconds: 5), () async { if (result.points.isNotEmpty) { result.points.forEach((PointLatLng point) { polylineCoordinates.add(LatLng(point.latitude, point.longitude)); }); } PolylineId id = PolylineId('poly'); Polyline polyline = Polyline( polylineId: id, color: Theme.of(context).colorScheme.secondary, points: polylineCoordinates, width: 5, ); polylines[id] = polyline; setState(() { routing = false; routingMessage = "Finding shops....."; }); }); return true; } catch (e) { print(e); return false; } } @override void dispose() { if (mapController != null) mapController!.dispose(); super.dispose(); } @override Widget build(BuildContext context) { SizeConfig().init(context); return new Scaffold( body: FutureBuilder( future: _future, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting || routing) { return Stack( children: [ ios ? Container( margin: EdgeInsets.only( top: (MediaQuery.of(context).size.height) - (MediaQuery.of(context).size.height * 0.935), left: 10), child: Material( elevation: 5, color: Color.fromRGBO(0, 0, 0, 0.4), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(25), bottomRight: Radius.circular(25), topLeft: Radius.circular(25), topRight: Radius.circular(25), ), child: ClipRRect( borderRadius: BorderRadius.circular(25.0), child: IconButton( icon: Icon( Icons.arrow_back_ios, size: 20, ), color: Colors.white, onPressed: () => Navigator.pop(context), ), ), ), ) : Container(), Container( padding: EdgeInsets.only( top: MediaQuery.of(context).size.width * 0.7), height: MediaQuery.of(context).size.height, width: MediaQuery.of(context).size.width, child: Center( child: Column( children: [ CupertinoActivityIndicator( animating: true, radius: 15, ), Text(routingMessage), ], ), ), ), ], ); } else if (snapshot.data == null && snapshot.connectionState == ConnectionState.done) { print(_error.toString()); Navigator.of(context).pop(); return Container(); } else { return Stack( children: [ GoogleMap( padding: EdgeInsets.only( top: 70.0, ), zoomGesturesEnabled: true, zoomControlsEnabled: false, compassEnabled: true, myLocationButtonEnabled: true, myLocationEnabled: true, markers: markers, initialCameraPosition: CameraPosition( target: _initialPosition!, zoom: CAMERA_ZOOM, //bearing: CAMERA_BEARING, ), onMapCreated: (GoogleMapController controller) { controller.setMapStyle(mapstyle); mapController = controller; }, onCameraMove: (position) { setState(() { try { _initialPosition = LatLng(position.target.latitude, position.target.longitude); } catch (e) { print(e); } }); }, polylines: Set.of(polylines.values), ), Container( margin: EdgeInsets.only( top: (MediaQuery.of(context).size.height) - (MediaQuery.of(context).size.height * 0.935), left: 10), child: Material( elevation: 5, color: Color.fromRGBO(0, 0, 0, 0.4), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(25), bottomRight: Radius.circular(25), topLeft: Radius.circular(25), topRight: Radius.circular(25), ), child: ClipRRect( borderRadius: BorderRadius.circular(25.0), child: IconButton( icon: Icon( Icons.arrow_back_ios, size: 20, ), color: Colors.white, onPressed: () => Navigator.pop(context), ), ), ), ), Visibility( visible: selectedshop == null || selectedshop!.isEmpty ? false : true, child: Align( alignment: Alignment.topCenter, child: SafeArea( child: Align( alignment: Alignment.topCenter, child: Padding( padding: const EdgeInsets.only(top: 10.0), child: Container( decoration: BoxDecoration( color: Colors.white70, borderRadius: BorderRadius.all( Radius.circular(20.0), ), ), width: SizeConfig.safeBlockHorizontal * 80, child: Padding( padding: const EdgeInsets.only( top: 10.0, bottom: 10.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( 'Places', style: TextStyle( fontSize: SizeConfig.blockSizeHorizontal * 4.0, ), ), SizedBox(height: 10), _textField( label: 'Start', hint: 'Choose starting point', prefixIcon: Icon( Icons.looks_one, color: Colors.black, ), controller: startAddressController, width: SizeConfig.safeBlockHorizontal * 70, ), SizedBox(height: 10), _textField( label: 'Destination', hint: 'Choose destination', prefixIcon: Icon(Icons.looks_two, color: Colors.black), controller: destinationAddressController, width: SizeConfig.safeBlockHorizontal * 70, ), SizedBox(height: 10), Visibility( visible: _placeDistance == null ? false : true, child: Text( 'DISTANCE: $_placeDistance km', style: TextStyle( fontSize: SizeConfig.blockSizeHorizontal * 3.5, fontWeight: FontWeight.bold, ), ), ), SizedBox(height: 5), ElevatedButton( onPressed: () async { polylines.clear(); markers.clear(); await getLocations(); selectedshop = ""; }, child: Padding( padding: const EdgeInsets.all(8.0), child: Text( 'Cancel'.toUpperCase(), style: TextStyle( color: Colors.white, fontSize: SizeConfig.blockSizeHorizontal * 3.5, ), ), ), style: ElevatedButton.styleFrom( primary: Colors.red, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20.0), ), ), ), ], ), ), ), ), ), ), ), ), ], ); } }, ), ); } double _coordinateDistance(lat1, lon1, lat2, lon2) { var p = 0.017453292519943295; var c = cos as double Function(num?); var a = 0.5 - c((lat2 - lat1) * p) / 2 + c(lat1 * p) * c(lat2 * p) * (1 - c((lon2 - lon1) * p)) / 2; return 12742 * asin(sqrt(a)); } Future _calculateDistance( Position startPos, Position destination) async { try { // Calculating to check that the position relative // to the frame, and pan & zoom the camera accordingly. double miny = (startPos.latitude <= destination.latitude) ? startPos.latitude : destination.latitude; double minx = (startPos.longitude <= destination.longitude) ? startPos.longitude : destination.longitude; double maxy = (startPos.latitude <= destination.latitude) ? destination.latitude : startPos.latitude; double maxx = (startPos.longitude <= destination.longitude) ? destination.longitude : startPos.longitude; double southWestLatitude = miny; double southWestLongitude = minx; double northEastLatitude = maxy; double northEastLongitude = maxx; // Accommodate the two locations within the // camera view of the map mapController!.animateCamera( CameraUpdate.newLatLngBounds( LatLngBounds( northeast: LatLng(northEastLatitude, northEastLongitude), southwest: LatLng(southWestLatitude, southWestLongitude), ), 100.0, ), ); // Calculating the distance between the start and the end positions // with a straight path, without considering any route // double distanceInMeters = await Geolocator.bearingBetween( // startLatitude, // startLongitude, // destinationLatitude, // destinationLongitude, // ); await createPolylines(startPos, destination); double totalDistance = 0.0; // Calculating the total distance by adding the distance // between small segments for (int i = 0; i < polylineCoordinates.length - 1; i++) { totalDistance += _coordinateDistance( polylineCoordinates[i].latitude, polylineCoordinates[i].longitude, polylineCoordinates[i + 1].latitude, polylineCoordinates[i + 1].longitude, ); } setState(() { _placeDistance = totalDistance.toStringAsFixed(2); print('DISTANCE: $_placeDistance km'); }); return true; } catch (e) { print(e); } return false; } Widget _textField({ TextEditingController? controller, FocusNode? focusNode, String? label, String? hint, required double width, Icon? prefixIcon, Widget? suffixIcon, }) { return Container( width: width * 0.8, child: TextField( controller: controller, enabled: false, focusNode: focusNode, style: TextStyle( color: Colors.black, fontSize: SizeConfig.blockSizeHorizontal * 3.5, ), decoration: new InputDecoration( prefixIcon: prefixIcon, suffixIcon: suffixIcon, labelText: label, filled: true, fillColor: Colors.white, enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.all( Radius.circular(10.0), ), borderSide: BorderSide( color: Colors.grey.shade400, width: 2, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.all( Radius.circular(10.0), ), borderSide: BorderSide( color: Colors.blue.shade300, width: 2, ), ), contentPadding: EdgeInsets.all(15), hintText: hint, ), ), ); } }