1145 lines
41 KiB
Dart
1145 lines
41 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:geocoding/geocoding.dart' as geocoding;
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:geolocator/geolocator.dart';
|
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
|
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:location/location.dart';
|
|
import 'package:package_info_plus/package_info_plus.dart';
|
|
import 'package:prosappco/constansts.dart';
|
|
import 'package:prosappco/src/authentication/authentication_repository.dart';
|
|
import 'package:prosappco/src/components/network_utility.dart';
|
|
import 'package:prosappco/src/models/event_model.dart';
|
|
import 'package:prosappco/src/models/professional_model.dart';
|
|
import 'package:prosappco/src/models/setting_model.dart';
|
|
import 'package:prosappco/src/models/user_model.dart';
|
|
import 'package:prosappco/src/presentation/screens/professional.dart';
|
|
import 'package:prosappco/src/presentation/screens/profile/profile.dart';
|
|
import 'package:prosappco/src/presentation/screens/service_after.dart';
|
|
import 'package:prosappco/src/presentation/screens/service_type.dart';
|
|
import 'package:prosappco/src/presentation/widgets/shared/drawer_menu.dart';
|
|
import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart';
|
|
import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart';
|
|
import 'package:prosappco/src/providers/user_provider.dart';
|
|
import 'package:prosappco/src/utils/time_of_day_extension.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
|
|
class ServiceScreen extends StatefulWidget {
|
|
const ServiceScreen({
|
|
super.key,
|
|
});
|
|
|
|
@override
|
|
State<ServiceScreen> createState() => _ServiceScreenState();
|
|
}
|
|
|
|
class _ServiceScreenState extends State<ServiceScreen> {
|
|
late String appVersion;
|
|
final Uri _url = Uri.parse(
|
|
'https://play.google.com/store/apps/details?id=com.prosapp.prosapp');
|
|
final Uri _urlIos =
|
|
Uri.parse('https://apps.apple.com/co/app/prosapp/id6469028900');
|
|
|
|
final Location _locationController = Location();
|
|
|
|
final Completer<GoogleMapController> _mapController =
|
|
Completer<GoogleMapController>();
|
|
|
|
static const LatLng _pGooglePlex = LatLng(7.080486, -73.087447);
|
|
LatLng? _currentP;
|
|
|
|
Map<PolylineId, Polyline> polylines = {};
|
|
|
|
final TextEditingController _locacionController = TextEditingController();
|
|
final TextEditingController _ubicationController = TextEditingController();
|
|
final TextEditingController _profesionalController = TextEditingController();
|
|
final TextEditingController _serviceTypeController = TextEditingController();
|
|
final TextEditingController _observacionController = TextEditingController();
|
|
final DateTime now = DateTime.now();
|
|
EventoService eventoService = EventoService();
|
|
String _serviceType = 'Servicio';
|
|
String ubicacion = '';
|
|
String professionalId = '';
|
|
String professionalToken = '';
|
|
String professionalAddress = '';
|
|
String professionalUbicacion = '';
|
|
int? professionalTarifa;
|
|
double? professionalLatitude;
|
|
double? professionalLongitude;
|
|
DateTime? _selectedDate;
|
|
|
|
String _ciudad = '...';
|
|
|
|
TimeOfDay? _selectedTime;
|
|
final DateFormat formatter = DateFormat('dd/MM/yyyy');
|
|
dynamic coordinates;
|
|
Set<Marker> markers = {};
|
|
BitmapDescriptor? _markerIcon;
|
|
List<dynamic> _placesList = [];
|
|
String _coordsOfCity = '0.0,0.0';
|
|
|
|
final uid = AuthenticationRepository.instance.getCurrentUserUid();
|
|
|
|
String? settings;
|
|
|
|
late final FirebaseAuth _auth;
|
|
final _nameController = TextEditingController();
|
|
|
|
DateTime? fechaSeleccionada;
|
|
TimeOfDay? horaSeleccionada;
|
|
Professional? profesionalSeleccionado;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
_getAppVersion();
|
|
|
|
getLocationUpdates();
|
|
|
|
_auth = FirebaseAuth.instance;
|
|
|
|
final currentUser = _auth.currentUser;
|
|
|
|
if (currentUser != null && currentUser.displayName != null) {
|
|
_nameController.text = currentUser.displayName!;
|
|
}
|
|
|
|
if (settings == null) {
|
|
SettingModel.getSettings().then(
|
|
(SettingModel value) => (value) {
|
|
if (mounted) {
|
|
setState(() {
|
|
settings = value.version;
|
|
_getAppVersion();
|
|
});
|
|
}
|
|
},
|
|
);
|
|
}
|
|
|
|
if (_ciudad == '...') {
|
|
AuthenticationRepository.instance
|
|
.getCity(uid.toString())
|
|
.then((String s) {
|
|
if (s.isEmpty) {
|
|
FirebaseFirestore.instance.collection('users').doc(uid).set({
|
|
'city': 'Cúcuta',
|
|
}).then((_) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_ciudad = 'Cúcuta';
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
if (mounted) {
|
|
setState(() {
|
|
_ciudad = s;
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
if (_coordsOfCity == '0.0,0.0') {
|
|
AuthenticationRepository.instance
|
|
.getCoordsOfCity(uid.toString())
|
|
.then((String s) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_coordsOfCity = s;
|
|
|
|
_animateInitialCameraToPosition(_coordsOfCity);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
_saveToken();
|
|
|
|
if (Platform.isAndroid) {
|
|
BitmapDescriptor.fromAssetImage(
|
|
const ImageConfiguration(size: Size(2, 2)),
|
|
'images/pro_marke_android.png',
|
|
).then((icon) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_markerIcon = icon;
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
BitmapDescriptor.fromAssetImage(
|
|
const ImageConfiguration(size: Size(1, 1)),
|
|
'images/pro_marke.png',
|
|
).then((icon) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_markerIcon = icon;
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
updateMarkersForServiceType(_serviceType);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_locacionController.dispose();
|
|
_ubicationController.dispose();
|
|
_profesionalController.dispose();
|
|
_serviceTypeController.dispose();
|
|
_observacionController.dispose();
|
|
|
|
_locationController.onLocationChanged.listen((_) {}).cancel();
|
|
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final userProvider = Provider.of<UserProvider>(context);
|
|
UserModel? user = userProvider.user;
|
|
dynamic datos;
|
|
|
|
return SafeArea(
|
|
child: Scaffold(
|
|
drawer: DrawerMenu(),
|
|
body: Column(
|
|
children: [
|
|
Expanded(
|
|
child: Stack(
|
|
children: [
|
|
GoogleMap(
|
|
onMapCreated: ((GoogleMapController controller) {
|
|
_mapController.complete(controller);
|
|
|
|
_applyMapStyle(controller);
|
|
}),
|
|
initialCameraPosition: const CameraPosition(
|
|
target: _pGooglePlex,
|
|
zoom: 15,
|
|
),
|
|
markers: {
|
|
...markers,
|
|
if (_currentP != null)
|
|
Marker(
|
|
markerId: const MarkerId("_currentLocation"),
|
|
icon: BitmapDescriptor.defaultMarker,
|
|
position: _currentP!,
|
|
),
|
|
},
|
|
polylines: Set<Polyline>.of(polylines.values),
|
|
myLocationButtonEnabled: false,
|
|
onCameraIdle: () {
|
|
if (professionalAddress == '') {
|
|
if (coordinates != null) {
|
|
getLocationName(
|
|
coordinates.latitude, coordinates.longitude)
|
|
.then((locationName) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_locacionController.text = locationName;
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
},
|
|
onCameraMove: (position) {
|
|
if (mounted) {
|
|
setState(() {
|
|
coordinates = position.target;
|
|
});
|
|
}
|
|
},
|
|
),
|
|
Positioned(
|
|
top: 10,
|
|
left: 10,
|
|
child: Builder(
|
|
builder: (context) {
|
|
return ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.white,
|
|
shape: const CircleBorder(),
|
|
elevation: 3,
|
|
minimumSize: const Size(50, 50),
|
|
),
|
|
child: const Icon(
|
|
Icons.menu,
|
|
color: Colors.black,
|
|
size: 35,
|
|
),
|
|
onPressed: () {
|
|
Scaffold.of(context).openDrawer();
|
|
},
|
|
);
|
|
},
|
|
),
|
|
),
|
|
const Positioned(
|
|
bottom: 25,
|
|
right: 0,
|
|
left: 0,
|
|
top: 0,
|
|
child: Icon(
|
|
Icons.location_on,
|
|
size: 40,
|
|
color: Color(0xFFFF0000),
|
|
),
|
|
),
|
|
Positioned(
|
|
top: 10,
|
|
right: 20,
|
|
child: FloatingActionButton(
|
|
onPressed: () async {
|
|
try {
|
|
Position position = await _determinePosition();
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_currentP = LatLng(
|
|
position.latitude,
|
|
position.longitude,
|
|
);
|
|
});
|
|
|
|
_animateCameraToPosition(_currentP!);
|
|
}
|
|
} catch (e) {
|
|
WarningSnackbar.show(
|
|
title: 'Ubicación desactivada',
|
|
message:
|
|
'Por favor activa la ubicacion de tu telefono.',
|
|
);
|
|
}
|
|
},
|
|
elevation: 0,
|
|
child: const Icon(
|
|
Icons.gps_fixed,
|
|
size: 30,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 10),
|
|
color: Colors.white,
|
|
child: Form(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
TextFormField(
|
|
controller: _serviceTypeController,
|
|
readOnly: true,
|
|
onTap: () async {
|
|
if (_nameController.text == '') {
|
|
WarningSnackbar.show(
|
|
title: 'Completa tu perfil',
|
|
message:
|
|
'Asegurate de llenar todos los campos de tu perfil antes de seleccionar un tipo de servicio.',
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
final String? serviceType = await Navigator.push(
|
|
context,
|
|
CupertinoPageRoute(
|
|
builder: (BuildContext context) {
|
|
return const ServiceTypeScreen();
|
|
},
|
|
),
|
|
) as String?;
|
|
|
|
if (serviceType != null) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_serviceType = serviceType;
|
|
_serviceTypeController.text = _serviceType;
|
|
|
|
updateMarkersForServiceType(_serviceType);
|
|
});
|
|
|
|
datos = await Navigator.push(
|
|
context,
|
|
CupertinoPageRoute(
|
|
builder: (BuildContext context) {
|
|
return ProfessionalScreen(
|
|
profession: _serviceTypeController.text,
|
|
);
|
|
},
|
|
),
|
|
);
|
|
|
|
if (datos != null) {
|
|
Professional? profesional = datos[0];
|
|
ubicacion = datos[1];
|
|
|
|
if (profesional != null) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_profesionalController.text =
|
|
profesional.name.toString();
|
|
|
|
professionalId = profesional.id;
|
|
|
|
if (profesional.token != '') {
|
|
professionalToken = profesional.token!;
|
|
}
|
|
|
|
professionalTarifa =
|
|
profesional.tarifa ?? 0;
|
|
|
|
professionalUbicacion =
|
|
profesional.ubicacion;
|
|
|
|
professionalLatitude = profesional.latitude;
|
|
professionalLongitude =
|
|
profesional.longitude;
|
|
|
|
if (ubicacion == 'sitio') {
|
|
professionalAddress =
|
|
profesional.realAddress;
|
|
|
|
_locacionController.text =
|
|
profesional.realAddress;
|
|
|
|
LatLng professionalCoordinates = LatLng(
|
|
professionalLatitude!,
|
|
professionalLongitude!);
|
|
|
|
if (professionalLatitude != null &&
|
|
professionalLatitude != 0 &&
|
|
professionalLongitude != null &&
|
|
professionalLongitude != 0) {
|
|
_animateCameraToPosition(
|
|
LatLng(professionalLatitude!,
|
|
professionalLongitude!),
|
|
);
|
|
|
|
getPolylinePoints(_currentP!,
|
|
professionalCoordinates)
|
|
.then(
|
|
(coordinates) => {
|
|
generatePolyLineFromPoints(
|
|
coordinates),
|
|
},
|
|
);
|
|
}
|
|
} else {
|
|
polylines.clear();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
decoration: const InputDecoration(
|
|
prefixIcon: Icon(Icons.room_service_rounded),
|
|
suffixIcon: Icon(Icons.arrow_drop_down),
|
|
hintText: 'Tipo de Servicio',
|
|
),
|
|
),
|
|
const SizedBox(height: 15),
|
|
TextFormField(
|
|
controller: _profesionalController,
|
|
readOnly: true,
|
|
onTap: () async {
|
|
if (_currentP == null) {
|
|
try {
|
|
Position position = await _determinePosition();
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_currentP = LatLng(
|
|
position.latitude,
|
|
position.longitude,
|
|
);
|
|
});
|
|
|
|
_animateCameraToPosition(_currentP!);
|
|
}
|
|
} catch (e) {
|
|
WarningSnackbar.show(
|
|
title: 'Ubicación desactivada',
|
|
message:
|
|
'Por favor activa la ubicacion de tu telefono para solicitar un servicio.',
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (_nameController.text == '') {
|
|
WarningSnackbar.show(
|
|
title: 'Completa tu perfil',
|
|
message:
|
|
'Asegurate de llenar todos los campos de tu perfil antes de seleccionar un profesional.',
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
datos = await Navigator.push(
|
|
context,
|
|
CupertinoPageRoute(
|
|
builder: (BuildContext context) {
|
|
return ProfessionalScreen(
|
|
profession: _serviceTypeController.text,
|
|
);
|
|
},
|
|
),
|
|
);
|
|
|
|
if (datos != null) {
|
|
fechaSeleccionada = datos[0];
|
|
horaSeleccionada = datos[1];
|
|
Professional? profesional = datos[2];
|
|
profesionalSeleccionado = profesional;
|
|
|
|
if (profesional != null) {
|
|
ubicacion = profesional.ubicacion;
|
|
if (mounted) {
|
|
setState(() {
|
|
_profesionalController.text =
|
|
profesional.name.toString();
|
|
|
|
professionalId = profesional.id;
|
|
|
|
if (profesional.token != '') {
|
|
professionalToken = profesional.token!;
|
|
}
|
|
|
|
professionalTarifa = profesional.tarifa ?? 0;
|
|
|
|
professionalUbicacion = profesional.ubicacion;
|
|
|
|
professionalLatitude = profesional.latitude;
|
|
professionalLongitude = profesional.longitude;
|
|
|
|
if (ubicacion == 'sitio') {
|
|
professionalAddress = profesional.realAddress;
|
|
|
|
_locacionController.text =
|
|
profesional.realAddress;
|
|
|
|
LatLng professionalCoordinates = LatLng(
|
|
professionalLatitude!,
|
|
professionalLongitude!);
|
|
|
|
if (professionalLatitude != null &&
|
|
professionalLatitude != 0 &&
|
|
professionalLongitude != null &&
|
|
professionalLongitude != 0) {
|
|
_animateCameraToPosition(
|
|
LatLng(professionalLatitude!,
|
|
professionalLongitude!),
|
|
);
|
|
|
|
getPolylinePoints(
|
|
_currentP!, professionalCoordinates)
|
|
.then(
|
|
(coordinates) => {
|
|
generatePolyLineFromPoints(coordinates),
|
|
},
|
|
);
|
|
}
|
|
} else {
|
|
polylines.clear();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
decoration: const InputDecoration(
|
|
prefixIcon: Icon(Icons.assignment_ind_rounded),
|
|
suffixIcon: Icon(Icons.arrow_drop_down),
|
|
hintText: 'Seleccionar profesional',
|
|
),
|
|
),
|
|
const SizedBox(height: 15),
|
|
TextFormField(
|
|
controller: _locacionController,
|
|
readOnly: ubicacion == 'sitio' ? true : false,
|
|
decoration: const InputDecoration(
|
|
prefixIcon: Icon(Icons.near_me),
|
|
hintText: 'Dirección',
|
|
),
|
|
onChanged: (value) {
|
|
String modifiedValue = value.replaceAll(' ', '_');
|
|
placeAutoComplete(modifiedValue);
|
|
},
|
|
),
|
|
ubicacion == 'sitio'
|
|
? const SizedBox()
|
|
: SizedBox(
|
|
height: _placesList.isNotEmpty ? 200 : 0,
|
|
child: ListView.builder(
|
|
itemCount: _placesList.length,
|
|
itemBuilder: (context, index) {
|
|
return ListTile(
|
|
onTap: () {
|
|
final selectedAddress =
|
|
_placesList[index]['formatted_address'];
|
|
final selectedLatitude = _placesList[index]
|
|
['geometry']['location']['lat'];
|
|
final selectedLongitude = _placesList[index]
|
|
['geometry']['location']['lng'];
|
|
|
|
_locacionController.text = selectedAddress;
|
|
|
|
_animateCameraToPosition(LatLng(
|
|
selectedLatitude,
|
|
selectedLongitude,
|
|
));
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_placesList = [];
|
|
});
|
|
}
|
|
},
|
|
title: Text(
|
|
_placesList[index]['formatted_address']),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
fechaSeleccionada == null && horaSeleccionada == null
|
|
? const SizedBox()
|
|
: const SizedBox(height: 15),
|
|
fechaSeleccionada == null && horaSeleccionada == null
|
|
? const SizedBox()
|
|
: Row(
|
|
children: [
|
|
Expanded(
|
|
child: TextFormField(
|
|
readOnly: true,
|
|
decoration: const InputDecoration(
|
|
prefixIcon: Icon(Icons.calendar_month),
|
|
hintText: 'Fecha',
|
|
),
|
|
controller: TextEditingController(
|
|
text: fechaSeleccionada == null
|
|
? ''
|
|
: formatter.format(fechaSeleccionada!),
|
|
),
|
|
),
|
|
),
|
|
Expanded(
|
|
child: TextFormField(
|
|
readOnly: true,
|
|
decoration: const InputDecoration(
|
|
prefixIcon: Icon(Icons.watch_later_outlined),
|
|
hintText: 'Hora',
|
|
),
|
|
controller: TextEditingController(
|
|
text: horaSeleccionada == null
|
|
? ''
|
|
: ' ${horaSeleccionada!.format(context)}',
|
|
),
|
|
))
|
|
],
|
|
),
|
|
fechaSeleccionada == null && horaSeleccionada == null
|
|
? const SizedBox()
|
|
: const SizedBox(height: 15),
|
|
fechaSeleccionada == null && horaSeleccionada == null
|
|
? const SizedBox()
|
|
: TextFormField(
|
|
controller: _observacionController,
|
|
decoration: const InputDecoration(
|
|
prefixIcon: Icon(Icons.message_outlined),
|
|
hintText: 'Observaciones',
|
|
),
|
|
),
|
|
const SizedBox(height: 20),
|
|
Center(
|
|
child: PrimaryButton(
|
|
onPressed: () {
|
|
if (user?.name == null ||
|
|
user?.name == '' ||
|
|
user?.phoneNumber == null ||
|
|
user?.phoneNumber == '') {
|
|
WarningSnackbar.show(
|
|
title: 'Completa tu perfil',
|
|
message:
|
|
'Diligencia la información de tu perfil antes de solicitar un servicio.',
|
|
);
|
|
|
|
Navigator.push(
|
|
context,
|
|
CupertinoPageRoute(
|
|
builder: (BuildContext context) {
|
|
return const ProfileScreen();
|
|
},
|
|
),
|
|
);
|
|
} else {
|
|
try {
|
|
DateTime time1 = DateTime(
|
|
fechaSeleccionada!.year,
|
|
fechaSeleccionada!.month,
|
|
fechaSeleccionada!.day,
|
|
horaSeleccionada!.hour,
|
|
horaSeleccionada!.minute,
|
|
);
|
|
|
|
DateTime time2 =
|
|
time1.add(const Duration(hours: 2));
|
|
|
|
UserModel.getUser(uid.toString()).then((value) {
|
|
eventoService
|
|
.createEvent(
|
|
value.name,
|
|
_observacionController.text,
|
|
fechaSeleccionada.toString().substring(0,
|
|
fechaSeleccionada.toString().length - 1),
|
|
'$time1',
|
|
'$time2',
|
|
professionalId,
|
|
ubicacion,
|
|
_locacionController.text,
|
|
ubicacion != 'sitio'
|
|
? coordinates.latitude
|
|
: professionalLatitude,
|
|
ubicacion != 'sitio'
|
|
? coordinates.longitude
|
|
: professionalLongitude,
|
|
'pendiente',
|
|
professionalTarifa == 0
|
|
? 0
|
|
: professionalTarifa,
|
|
false,
|
|
false,
|
|
)
|
|
.then((value) {
|
|
if (professionalToken != '') {
|
|
sendPushNotification(professionalToken);
|
|
}
|
|
Navigator.pushReplacement(
|
|
context,
|
|
CupertinoPageRoute(
|
|
builder: (BuildContext context) {
|
|
return ServiceAfterScreen(
|
|
eventoId: value,
|
|
);
|
|
},
|
|
),
|
|
);
|
|
});
|
|
});
|
|
} catch (e) {
|
|
WarningSnackbar.show(
|
|
title: 'Llena todos los campos',
|
|
message:
|
|
'Asegurate de llenar todos los campos antes de solicitar un servicio. $e',
|
|
);
|
|
}
|
|
}
|
|
},
|
|
text: 'Solicitar servicio',
|
|
minWidth: 230,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> getLocationUpdates() async {
|
|
bool _serviceEnabled;
|
|
PermissionStatus _permissionGranted;
|
|
|
|
_serviceEnabled = await _locationController.serviceEnabled();
|
|
if (_serviceEnabled) {
|
|
_serviceEnabled = await _locationController.requestService();
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
_permissionGranted = await _locationController.hasPermission();
|
|
if (_permissionGranted == PermissionStatus.denied) {
|
|
_permissionGranted = await _locationController.requestPermission();
|
|
if (_permissionGranted != PermissionStatus.granted) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
_locationController.onLocationChanged
|
|
.listen((LocationData currentLocation) {
|
|
if (currentLocation.latitude != null &&
|
|
currentLocation.longitude != null) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_currentP = LatLng(
|
|
currentLocation.latitude!,
|
|
currentLocation.longitude!,
|
|
);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<List<LatLng>> getPolylinePoints(
|
|
LatLng originP, LatLng destinationP) async {
|
|
List<LatLng> polylineCoordinates = [];
|
|
PolylinePoints polylinePoints = PolylinePoints();
|
|
PolylineResult result = await polylinePoints.getRouteBetweenCoordinates(
|
|
GOOGLE_MAPS_API_KEY,
|
|
PointLatLng(originP.latitude, originP.longitude),
|
|
PointLatLng(destinationP.latitude, destinationP.longitude),
|
|
travelMode: TravelMode.driving,
|
|
);
|
|
if (result.points.isNotEmpty) {
|
|
result.points.forEach((PointLatLng point) {
|
|
polylineCoordinates.add(LatLng(point.latitude, point.longitude));
|
|
});
|
|
} else {
|
|
print(result.errorMessage);
|
|
}
|
|
return polylineCoordinates;
|
|
}
|
|
|
|
void generatePolyLineFromPoints(List<LatLng> polylineCoordinates) async {
|
|
PolylineId id = const PolylineId("poly");
|
|
Polyline polyline = Polyline(
|
|
polylineId: id,
|
|
color: Colors.blue,
|
|
points: polylineCoordinates,
|
|
width: 6,
|
|
);
|
|
if (mounted) {
|
|
setState(() {
|
|
polylines[id] = polyline;
|
|
});
|
|
}
|
|
}
|
|
|
|
void _applyMapStyle(GoogleMapController controller) {
|
|
controller.setMapStyle(MAP_STYLE);
|
|
}
|
|
|
|
Future<void> _animateCameraToPosition(LatLng position) async {
|
|
final GoogleMapController controller = await _mapController.future;
|
|
controller.animateCamera(
|
|
CameraUpdate.newCameraPosition(
|
|
CameraPosition(
|
|
target: position,
|
|
zoom: 17.5,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> _animateInitialCameraToPosition(String coordsOfCity) async {
|
|
List<String> coords = coordsOfCity.split(',');
|
|
double lat = double.parse(coords[0]);
|
|
double lng = double.parse(coords[1]);
|
|
|
|
_animateCameraToPosition(LatLng(lat, lng));
|
|
|
|
try {
|
|
Position position = await _determinePosition();
|
|
|
|
_animateCameraToPosition(LatLng(position.latitude, position.longitude));
|
|
|
|
if (mounted) {
|
|
setState(() {});
|
|
}
|
|
} catch (e) {
|
|
print('Error: $e');
|
|
}
|
|
}
|
|
|
|
void updateMarkersForServiceType(String serviceType) {
|
|
getUsersWithActiveStatus(serviceType).then((value) {
|
|
markers.clear();
|
|
for (var doc in value) {
|
|
final element = doc.data()!;
|
|
|
|
if (element['latitude'] != null && element['longitude'] != null) {
|
|
markers.add(
|
|
Marker(
|
|
icon: _markerIcon!,
|
|
markerId: MarkerId(doc.id),
|
|
position: LatLng(
|
|
element['latitude'],
|
|
element['longitude'],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}).catchError((e) {
|
|
print('Error al actualizar los marcadores: $e');
|
|
});
|
|
}
|
|
|
|
Future<Position> _determinePosition() async {
|
|
bool serviceEnabled;
|
|
LocationPermission permission;
|
|
|
|
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
|
|
|
if (!serviceEnabled) {
|
|
return Future.error('Location services are disabled');
|
|
}
|
|
|
|
permission = await Geolocator.checkPermission();
|
|
|
|
if (permission == LocationPermission.denied) {
|
|
permission = await Geolocator.requestPermission();
|
|
|
|
if (permission == LocationPermission.denied) {
|
|
return Future.error('Location permission denied');
|
|
}
|
|
}
|
|
|
|
if (permission == LocationPermission.deniedForever) {
|
|
return Future.error('Location permissions are permanently denied');
|
|
}
|
|
|
|
Position position = await Geolocator.getCurrentPosition();
|
|
|
|
return position;
|
|
}
|
|
|
|
Future<String> getLocationName(double latitude, double longitude) async {
|
|
String address;
|
|
try {
|
|
List<geocoding.Placemark> placemarks =
|
|
await geocoding.placemarkFromCoordinates(latitude, longitude);
|
|
geocoding.Placemark place = placemarks[0];
|
|
|
|
if (place.thoroughfare != '' || place.subThoroughfare != '') {
|
|
address =
|
|
"${place.thoroughfare} ${place.subThoroughfare} ${place.subLocality}, ${place.locality}, ${place.administrativeArea}";
|
|
} else {
|
|
address = '';
|
|
}
|
|
} catch (e) {
|
|
address = '';
|
|
}
|
|
|
|
return address;
|
|
}
|
|
|
|
void placeAutoComplete(String query) async {
|
|
Uri uri = Uri.https("admin.prosapp.co", "/autocomplete", {
|
|
"input": query,
|
|
"location": _coordsOfCity,
|
|
});
|
|
|
|
String? response = await NetworkUtility.fetchUrl(uri);
|
|
|
|
if (response != null) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_placesList = jsonDecode(response.toString())['results'];
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<List<DocumentSnapshot<Map<String, dynamic>>>> getUsersWithActiveStatus(
|
|
String serviceType) async {
|
|
dynamic querySnapshot;
|
|
|
|
if (serviceType != "Servicio") {
|
|
querySnapshot = await FirebaseFirestore.instance
|
|
.collection('users')
|
|
.where('estado', isEqualTo: 'activo')
|
|
.where('profesion', isEqualTo: serviceType)
|
|
.get();
|
|
} else {
|
|
querySnapshot = await FirebaseFirestore.instance
|
|
.collection('users')
|
|
.where('estado', isEqualTo: 'activo')
|
|
.get();
|
|
}
|
|
|
|
return querySnapshot.docs;
|
|
}
|
|
|
|
void _saveToken() async {
|
|
FirebaseMessaging messaging = FirebaseMessaging.instance;
|
|
|
|
final token = await messaging.getToken();
|
|
|
|
try {
|
|
await FirebaseFirestore.instance
|
|
.collection('users')
|
|
.doc(uid)
|
|
.update({'token': token});
|
|
} catch (e) {
|
|
print(e);
|
|
}
|
|
}
|
|
|
|
Future<void> _getAppVersion() async {
|
|
PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
|
String version = packageInfo.version;
|
|
|
|
print('App version: $version');
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
appVersion = version;
|
|
});
|
|
}
|
|
|
|
print('App version: $settings');
|
|
|
|
if (settings != null) {
|
|
_checkForUpdate(settings);
|
|
}
|
|
}
|
|
|
|
void _checkForUpdate(String? settings) {
|
|
if (appVersion == settings) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) {
|
|
return WillPopScope(
|
|
onWillPop: () async {
|
|
return false;
|
|
},
|
|
child: Center(
|
|
child: AlertDialog(
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16.0),
|
|
),
|
|
title: const Text(
|
|
"Debes Actualizar la Aplicación",
|
|
style: TextStyle(fontWeight: FontWeight.bold),
|
|
),
|
|
content: const Text(
|
|
"Tienes una versión desactualizada de nuestra aplicación. Te recomendamos actualizarla para disfrutar de las últimas características y mejoras.",
|
|
),
|
|
actions: [
|
|
TextButton(
|
|
child: const Text(
|
|
"Actualizar",
|
|
style:
|
|
TextStyle(fontWeight: FontWeight.w600, fontSize: 20),
|
|
),
|
|
onPressed: () {
|
|
if (Platform.isAndroid) {
|
|
launchUrl(_url);
|
|
} else {
|
|
launchUrl(_urlIos);
|
|
}
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
Future<void> _selectDateAndTime(BuildContext context) async {
|
|
final DateTime? pickedDate = await showDatePicker(
|
|
context: context,
|
|
initialDate: now,
|
|
firstDate: now,
|
|
lastDate: DateTime(now.year + 1),
|
|
);
|
|
|
|
if (pickedDate == null) {
|
|
return;
|
|
}
|
|
|
|
final TimeOfDay? pickedTime = await showTimePicker(
|
|
context: context,
|
|
initialTime: TimeOfDay.now(),
|
|
);
|
|
|
|
if (pickedTime != null) {
|
|
final DateTime selectedDateTime = DateTime(
|
|
pickedDate.year,
|
|
pickedDate.month,
|
|
pickedDate.day,
|
|
pickedTime.hour,
|
|
pickedTime.minute,
|
|
);
|
|
|
|
if (selectedDateTime.isBefore(DateTime.now())) {
|
|
WarningSnackbar.show(
|
|
title: 'Hora inválida',
|
|
message:
|
|
'La hora seleccionada no es valida porque es anterior a la hora actual.',
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
_selectedDate = pickedDate;
|
|
_selectedTime = pickedTime;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> sendPushNotification(String pro) async {
|
|
try {
|
|
http.Response response = await http.post(
|
|
Uri.parse('https://fcm.googleapis.com/fcm/send'),
|
|
headers: <String, String>{
|
|
'Content-Type': 'application/json; charset=UTF-8',
|
|
'Authorization':
|
|
'key=AAAAORdR-xU:APA91bF_wblg86jHAC-uexrXPHavYRlk5wge1Gf46m56V4J2D2L37Cp_hf46JZUzpvsWPSpqc5ewHelKI9LTifUG_s2mciMI6e5VLKo7E1R8btbNo7iaM9do2ctoyHKUm1atlZBdKaN2',
|
|
},
|
|
body: jsonEncode(
|
|
<String, dynamic>{
|
|
'notification': <String, dynamic>{
|
|
'body': 'alguien a solicitado tus servicios',
|
|
'title': 'Nueva solicitud',
|
|
},
|
|
'priority': 'high',
|
|
'data': <String, dynamic>{
|
|
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
|
|
'id': '1',
|
|
'status': 'done',
|
|
'screen': 'solicitud'
|
|
},
|
|
'to': pro
|
|
},
|
|
),
|
|
);
|
|
|
|
response;
|
|
} catch (e) {
|
|
print('error al enviar notificacion $e');
|
|
}
|
|
}
|
|
}
|