Files
prosappweb/lib/src/screens/service.dart
T

827 lines
32 KiB
Dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:prosappco/constansts.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/drawer_menu.dart';
import 'package:prosappco/src/components/network_utility.dart';
import 'package:prosappco/src/models/event_model.dart';
import 'package:prosappco/src/models/user_model.dart';
import 'package:prosappco/src/screens/professional.dart';
import 'package:intl/intl.dart';
import 'package:prosappco/src/screens/profile.dart';
import 'package:prosappco/src/screens/service_after.dart';
import 'package:prosappco/src/screens/service_type.dart';
import 'package:flutter/foundation.dart';
class ServiceScreen extends StatefulWidget {
const ServiceScreen({super.key});
@override
State<ServiceScreen> createState() => _ServiceScreenState();
}
class _ServiceScreenState extends State<ServiceScreen> {
EventoService eventoService = EventoService();
final TextEditingController _locationController = TextEditingController();
String _locationPosition = '';
String ubicacion = '';
final _profesionalController = TextEditingController();
final _serviceTypeController = TextEditingController();
final _observacionController = TextEditingController();
String professionalId = '';
int? professionalTarifa;
String professionalAddress = '';
String professionalUbicacion = '';
double? professionalLatitude;
double? professionalLongitude;
String _serviceType = 'Servicio';
String _professional = '';
final DateFormat formatter = DateFormat('dd/MM/yyyy');
final DateTime now = DateTime.now();
DateTime? _selectedDate;
TimeOfDay? _selectedTime;
late GoogleMapController googleMapController;
static CameraPosition initialCameraPosition = const CameraPosition(
target: LatLng(7.8939100, -72.5078200),
zoom: 14.4746,
);
Set<Marker> markers = {};
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<void> _selectDate(BuildContext context) async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: now,
firstDate: now,
lastDate: DateTime(now.year + 1),
// builder: (context, child) {
// return Theme(data: ThemeData.dark(), child: child!);
// },
);
if (picked != null && picked != _selectedDate) {
setState(() {
_selectedDate = picked;
});
}
}
Future<void> _selectTime(BuildContext context) async {
final TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: TimeOfDay.now(),
// builder: (context, child) {
// return Theme(data: ThemeData.dark(), child: child!);
// },
);
if (pickedTime != null) {
setState(() {
_selectedTime = pickedTime;
});
}
}
Future<List<DocumentSnapshot<Map<String, dynamic>>>>
getUsersWithActiveStatus() async {
var querySnapshot = await FirebaseFirestore.instance
.collection('users')
.where('estado', isEqualTo: 'activo')
.get();
return querySnapshot.docs;
}
final FirebaseStorage storage = FirebaseStorage.instance;
final uid = AuthenticationRepository.instance.getCurrentUserUid();
UserModel? user;
final fcmToken = FirebaseMessaging.instance.getToken();
BitmapDescriptor? _markerIcon;
@override
void initState() {
super.initState();
final userRef = FirebaseFirestore.instance.collection('users').doc(uid);
if (user == null) {
UserModel.getUser(uid.toString()).then(
(UserModel s) => setState(() => user = s),
);
}
BitmapDescriptor.fromAssetImage(
const ImageConfiguration(size: Size(48, 48)),
'images/pro_marke.png')
.then((icon) {
setState(() {
_markerIcon = icon;
});
});
try {
getUsersWithActiveStatus().then((value) {
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'],
),
),
);
}
}
});
} catch (e) {
print('error coordenadas $e');
}
}
late String lat;
late String long;
var coordinates;
Future<String> getLocationName(double latitude, double longitude) async {
String address;
List<Placemark> placemarks =
await placemarkFromCoordinates(latitude, longitude);
Placemark place = placemarks[0];
if (place.thoroughfare != '' || place.subThoroughfare != '') {
address =
"${place.thoroughfare} ${place.subThoroughfare} ${place.subLocality}, ${place.locality}, ${place.administrativeArea}";
} else {
address = '';
}
return address;
}
void placeAutoComplete(String query) async {
Uri uri =
Uri.https("maps.googleapis.com", "maps/api/place/autocomplete/json", {
"input": query,
"key": apiKey,
});
String? response = await NetworkUtility.fetchUrl(uri);
if (response != null) {
print(response);
}
}
@override
Widget build(BuildContext context) {
if (kIsWeb) {
return Scaffold(
drawer: DrawerMenu(),
appBar: AppBar(
elevation: 0,
title: Row(
children: [
Container(
padding: const EdgeInsets.only(right: 10),
decoration: const BoxDecoration(
border: Border(
right: BorderSide(
color: Colors.white,
width: 1.0,
),
),
),
child: const Text(
'Prosapp',
),
),
const SizedBox(width: 8),
TextButton(
style: TextButton.styleFrom(
foregroundColor: Colors.white,
),
onPressed: () {
showDialog(
context: context,
builder: (context) {
return _EnterLocationDialog();
},
);
},
child: const Row(
children: [
Icon(Icons.location_on),
SizedBox(width: 8),
Text('Ingresar mi ubicación'),
SizedBox(width: 4),
Icon(Icons.keyboard_arrow_down_rounded, size: 16),
],
),
),
],
), // Texto del título
),
body: Container(
child: ElevatedButton(
onPressed: () {
placeAutoComplete("Dubai");
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
elevation: 0,
minimumSize: const Size(230, 50),
),
child: const Text(
'Aceptar',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
),
);
}
return SafeArea(
child: Scaffold(
backgroundColor: const Color(0xFFD6F4FF),
drawer: DrawerMenu(),
body: SingleChildScrollView(
reverse: true,
child: Column(
children: [
Stack(
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.6,
child: GoogleMap(
mapType: MapType.normal,
initialCameraPosition: initialCameraPosition,
markers: markers,
zoomControlsEnabled: false,
onMapCreated: (GoogleMapController controller) {
googleMapController = controller;
},
onCameraIdle: () {
if (professionalAddress == '') {
if (coordinates != null) {
getLocationName(
coordinates.latitude, coordinates.longitude)
.then((locationName) {
setState(() {
_locationController.text = locationName;
});
});
}
}
},
onCameraMove: (position) {
setState(() {
coordinates = position.target;
});
},
gestureRecognizers: <Factory<
OneSequenceGestureRecognizer>>{
Factory<OneSequenceGestureRecognizer>(
() => EagerGestureRecognizer(),
),
},
),
),
const Positioned(
bottom: 25,
right: 0,
left: 0,
top: 0,
child: Icon(
Icons.location_on,
size: 40,
color: Color(0xFFFF0000),
),
),
Positioned(
bottom: 10,
right: 20,
child: FloatingActionButton(
onPressed: () async {
try {
Position position = await _determinePosition();
googleMapController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(
position.latitude,
position.longitude,
),
zoom: 17),
),
);
setState(() {});
} catch (e) {
Get.snackbar(
'ubicación desactivada',
'Por favor activa la ubicacion de tu telefono.',
snackPosition: SnackPosition.TOP,
);
}
// markers.clear();
},
elevation: 0,
child: const Icon(
Icons.gps_fixed,
size: 30,
),
),
),
Padding(
padding: const EdgeInsets.all(20.0),
child: Builder(
builder: (BuildContext 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: 30,
),
onPressed: () {
Scaffold.of(context).openDrawer();
},
);
},
),
),
Padding(
padding:
const EdgeInsets.only(top: 22, left: 100, right: 25),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.4),
spreadRadius: 1,
blurRadius: 2,
offset: const Offset(1, 2),
),
],
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: TextFormField(
onTap: () async {
final String? serviceType = await Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return const ServiceTypeScreen();
},
),
) as String?;
if (serviceType != null) {
setState(() {
_serviceType = serviceType;
_serviceTypeController.text = _serviceType;
});
}
},
controller: _serviceTypeController,
style: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.w600,
),
readOnly: true,
decoration: InputDecoration(
hintText:
_serviceType == '' ? 'Servicio' : _serviceType,
hintStyle: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.w600),
border: InputBorder.none,
enabledBorder: InputBorder.none,
),
),
),
),
),
],
),
Container(
width: double.infinity,
decoration: const BoxDecoration(color: Colors.white),
child: Column(
children: [
const SizedBox(height: 5),
SizedBox(
width: 320,
child: Form(
child: Column(
children: [
Column(
children: [
TextFormField(
readOnly: true,
controller: _locationController,
decoration: const InputDecoration(
prefixIcon: Icon(Icons.near_me),
hintText: 'Dirección',
),
),
],
),
const SizedBox(height: 15),
TextFormField(
controller: _profesionalController,
readOnly: true,
onTap: () async {
final dynamic 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) {
setState(() {
_profesionalController.text =
profesional.name.toString();
professionalId = profesional.id;
professionalTarifa =
profesional.tarifa ?? 0;
professionalUbicacion =
profesional.ubicacion;
professionalLatitude =
profesional.latitude;
professionalLongitude =
profesional.longitude;
if (ubicacion == 'sitio') {
professionalAddress =
profesional.realAddress;
_locationController.text =
profesional.realAddress;
if (professionalLatitude != null &&
professionalLatitude != 0 &&
professionalLongitude != null &&
professionalLongitude != 0) {
googleMapController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(
professionalLatitude!,
professionalLongitude!,
),
zoom: 17),
),
);
}
}
});
}
}
},
decoration: const InputDecoration(
prefixIcon:
Icon(Icons.assignment_ind_rounded),
suffixIcon: Icon(Icons.arrow_drop_down),
hintText: 'Seleccionar profesional'),
),
const SizedBox(height: 15),
Row(
children: [
SizedBox(
width: 180,
child: TextFormField(
onTap: () {
if (_profesionalController
.text.isNotEmpty) {
_selectDate(context);
} else {
final snackBar = SnackBar(
backgroundColor: Colors.blue,
content: const Text(
'Selecciona un profesional',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
elevation: 8.0,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10.0),
),
);
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
}
},
readOnly: true,
decoration: InputDecoration(
prefixIcon:
const Icon(Icons.calendar_month),
suffixIcon: _selectedDate == null
? const Icon(Icons.arrow_drop_down)
: null,
hintText: 'Fecha',
),
controller: TextEditingController(
text: _selectedDate == null
? ''
: formatter.format(_selectedDate!)),
),
),
SizedBox(
width: 140,
child: TextFormField(
onTap: () {
if (_profesionalController
.text.isNotEmpty) {
_selectTime(context);
} else {
final snackBar = SnackBar(
backgroundColor: Colors.blue,
content: const Text(
'Selecciona un profesional',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
elevation: 8.0,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10.0),
),
);
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
}
},
readOnly: true,
decoration: const InputDecoration(
suffixIcon: Icon(Icons.arrow_drop_down),
hintText: 'Hora',
),
controller: TextEditingController(
text: _selectedTime == null
? ''
: ' ${_selectedTime!.format(context)}'),
),
),
],
),
const SizedBox(height: 15),
TextFormField(
controller: _observacionController,
decoration: const InputDecoration(
prefixIcon: Icon(Icons.message_outlined),
hintText: 'Observaciones'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
if (user?.name == null ||
user?.name == '' ||
user?.phoneNumber == null ||
user?.phoneNumber == '') {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return const ProfileScreen();
},
),
);
} else {
try {
final DateTime combinedDate = DateTime(
_selectedDate!.year,
_selectedDate!.month,
_selectedDate!.day,
_selectedTime!.hour,
_selectedTime!.minute);
DateTime time2 = combinedDate
.add(const Duration(hours: 2));
UserModel.getUser(uid.toString())
.then((value) {
eventoService
.createEvent(
value.name,
_observacionController.text,
'$_selectedDate',
'$combinedDate',
'$time2',
professionalId,
ubicacion,
_locationController.text,
ubicacion != 'sitio'
? coordinates.latitude
: professionalLatitude,
ubicacion != 'sitio'
? coordinates.longitude
: professionalLongitude,
'pendiente',
professionalTarifa == 0
? 0
: professionalTarifa,
false,
false,
)
.then((value) {
Navigator.pushReplacement(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return ServiceAfterScreen(
eventoId: value,
);
},
),
);
});
});
} catch (e) {
Get.snackbar(
'Llena todos los campos',
'Asegurate de llenar todos los campos.',
snackPosition: SnackPosition.BOTTOM,
);
}
}
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
elevation: 0,
minimumSize: const Size(230, 50),
),
child: const Text(
'Solicitar servicio',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
//Text('${_selectedDate?.weekday}'),
],
),
),
),
],
),
),
],
),
),
),
);
}
}
class _EnterLocationDialog extends StatelessWidget {
const _EnterLocationDialog({super.key});
void placeAutoComplete(String query) async {
Uri uri =
Uri.https("maps.googleapis.com", "/maps/api/place/autocomplete/json", {
"input": query,
"key": apiKey,
});
String? response = await NetworkUtility.fetchUrl(uri);
if (response != null) {
print(response);
}
}
@override
Widget build(BuildContext context) {
return AlertDialog(
content: SizedBox(
height: 200,
child: Column(
children: [
const Text(
'Ingresa la ubicación',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
const SizedBox(height: 20),
TextFormField(
decoration: const InputDecoration(
hintText: 'Escribe tu ubicación',
),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
placeAutoComplete("Dubai");
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
elevation: 0,
minimumSize: const Size(230, 50),
),
child: const Text(
'Aceptar',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
],
),
),
);
}
}