You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

621 lines
22 KiB

3 years ago
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<BusinessLocator> {
String mapstyle;
var _future;
static LatLng _initialPosition;
Set<Marker> markers = {};
List<TesoShop> shops;
GoogleMapController mapController;
static const double CAMERA_ZOOM = 13.499910354614258;
bool routing = false;
List<LatLng> polylineCoordinates = [];
Map<PolylineId, Polyline> polylines = {};
String selectedshop = "";
Location location = Location();
String routingMessage = "Finding shops....";
LocationData _location;
String _error;
String _placeDistance;
final startAddressController = TextEditingController();
final destinationAddressController = TextEditingController();
bool ios = false;
Future<LatLng> _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<String, String> 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<TesoShop>.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<bool> 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<Polyline>.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: <Widget>[
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;
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<bool> _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,
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,
),
),
);
}
}