import 'dart:convert'; 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/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/drawer_menu.dart'; import 'package:prosappco/src/models/event_model.dart'; import 'package:prosappco/src/models/user_model.dart'; import 'package:prosappco/src/screens/my_services.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/profile_web.dart'; import 'package:prosappco/src/screens/service_after.dart'; import 'package:prosappco/src/screens/service_type.dart'; import 'package:prosappco/src/screens/solicitudes.dart'; import 'package:prosappco/src/screens/ubicacion.dart'; import 'package:http/http.dart' as http; import 'package:prosappco/src/components/network_utility.dart'; class ServiceScreen extends StatefulWidget { const ServiceScreen({super.key}); @override State createState() => _ServiceScreenState(); } class _ServiceScreenState extends State { 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 sendPushNotification(String pro) async { try { http.Response response = await http.post( Uri.parse('https://fcm.googleapis.com/fcm/send'), headers: { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': 'key=AAAAORdR-xU:APA91bF_wblg86jHAC-uexrXPHavYRlk5wge1Gf46m56V4J2D2L37Cp_hf46JZUzpvsWPSpqc5ewHelKI9LTifUG_s2mciMI6e5VLKo7E1R8btbNo7iaM9do2ctoyHKUm1atlZBdKaN2', }, body: jsonEncode( { 'notification': { 'body': 'alguien a solicitado tus servicios', 'title': 'Nueva solicitud', }, 'priority': 'high', 'data': { 'click_action': 'FLUTTER_NOTIFICATION_CLICK', 'id': '1', 'status': 'done', 'screen': 'solicitud' }, 'to': pro }, ), ); response; } catch (e) { print('error al enviar notificacion $e'); } } EventoService eventoService = EventoService(); String _locationPosition = ''; String ubicacion = ''; final TextEditingController _locationController = TextEditingController(); final TextEditingController _ubicationController = TextEditingController(); final TextEditingController _profesionalController = TextEditingController(); final TextEditingController _serviceTypeController = TextEditingController(); final TextEditingController _observacionController = TextEditingController(); String professionalId = ''; String professionalToken = ''; 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(); List _placesList = []; String selectedPlace = ''; String _coordsOfCity = '0.0,0.0'; DateTime? _selectedDate; TimeOfDay? _selectedTime; late GoogleMapController googleMapController; static CameraPosition initialCameraPosition = const CameraPosition( target: LatLng(7.8939100, -72.5078200), zoom: 14.4746, ); Set markers = {}; Future _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 _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 _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>>> 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; String _ciudad = '...'; @override void initState() { super.initState(); if (_ciudad == '...') { AuthenticationRepository.instance .getCity(uid.toString()) .then((String s) { if (s.isEmpty) { FirebaseFirestore.instance.collection('users').doc(uid).set({ 'city': 'Cúcuta', }).then((_) { setState(() { _ciudad = 'Cúcuta'; }); }); } else { setState(() { _ciudad = s; }); } }); } if (_coordsOfCity == '0.0,0.0') { AuthenticationRepository.instance .getCoordsOfCity(uid.toString()) .then((String s) { setState(() { _coordsOfCity = s; }); if (_coordsOfCity != '0.0,0.0') { List coords = _coordsOfCity.split(','); double lat = double.parse(coords[0]); double lng = double.parse(coords[1]); // Asegúrate de que googleMapController esté disponible y no sea nulo googleMapController.moveCamera( CameraUpdate.newLatLngZoom( LatLng(lat, lng), 14.4746, // Puedes ajustar el nivel de zoom según tus necesidades ), ); } }); } _saveToken(); 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; double latUser = 0.0; double lngUser = 0.0; var coordinates; Future getLocationName(double latitude, double longitude) async { String address; List 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("admin.prosapp.co", "/autocomplete", { "input": query, "location": _coordsOfCity, }); String? response = await NetworkUtility.fetchUrl(uri); if (response != null) { setState(() { _placesList = jsonDecode(response.toString())['results']; }); } } @override Widget build(BuildContext context) { if (kIsWeb) { return Scaffold( backgroundColor: const Color(0xFFD6F4FF), drawer: DrawerMenu(), appBar: AppBar( elevation: 0, title: const Text( 'Prosapp', style: TextStyle( color: Colors.white, fontSize: 20, ), )), body: Center( child: SizedBox( width: 300, height: 470, child: Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), color: Colors.white, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 20.0), child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ 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; professionalAddress = _ubicationController.text; if (profesional.token != '') { professionalToken = profesional.token!; print(professionalToken); } if (ubicacion == 'sitio') { professionalAddress = profesional.realAddress; _ubicationController.text = profesional.realAddress; latUser = profesional.latitude; lngUser = profesional.longitude; } }); } } }, decoration: const InputDecoration( prefixIcon: Icon(Icons.assignment_ind_rounded), suffixIcon: Icon(Icons.arrow_drop_down), hintText: 'Seleccionar profesional'), ), const SizedBox(height: 20.0), TextFormField( controller: _ubicationController, readOnly: true, onTap: () async { final List datos = await Navigator.push( context, CupertinoPageRoute( builder: (BuildContext context) { return const UbicacionScreen(); }, ), ); if (datos.length == 3) { final formattedAddress = datos[0]; final lat = datos[1]; final lng = datos[2]; setState(() { _ubicationController.text = formattedAddress; latUser = lat; lngUser = lng; }); } }, decoration: const InputDecoration( hintText: 'Escribe tu ubicación', prefixIcon: Icon(Icons.location_on), ), ), const SizedBox(height: 20.0), 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!)), ), const SizedBox(height: 20.0), 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( prefixIcon: Icon(Icons.access_time), suffixIcon: Icon(Icons.arrow_drop_down), hintText: 'Hora', ), controller: TextEditingController( text: _selectedTime == null ? '' : ' ${_selectedTime!.format(context)}'), ), const SizedBox(height: 20.0), TextFormField( controller: _observacionController, decoration: const InputDecoration( prefixIcon: Icon(Icons.message_outlined), hintText: 'Observaciones'), ), const SizedBox(height: 40.0), ElevatedButton( onPressed: () { if (user?.name == null || user?.name == '' || user?.phoneNumber == null || user?.phoneNumber == '') { Navigator.push( context, CupertinoPageRoute( builder: (BuildContext context) { return const ProfileWebScreen(); }, ), ); } 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, professionalAddress, latUser, lngUser, '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) { 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, ), ), ), ], ), ), ), ), ), ); } return SafeArea( child: Scaffold( backgroundColor: const Color(0xFFD6F4FF), drawer: DrawerMenu(), body: Column( children: [ Stack( children: [ SizedBox( height: MediaQuery.of(context).size.height * 0.5, 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( () => 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(); }, ); }, ), ), ], ), Expanded( child: SingleChildScrollView( child: Container( width: double.infinity, height: MediaQuery.of(context).size.height * 0.5, decoration: const BoxDecoration(color: Colors.white), child: Column( children: [ SizedBox( width: 320, child: Form( child: Column( children: [ TextFormField( controller: _serviceTypeController, readOnly: true, 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; }); } }, decoration: const InputDecoration( prefixIcon: Icon(Icons.room_service_rounded), suffixIcon: Icon(Icons.arrow_drop_down), hintText: 'Tipo de Servicio'), ), const SizedBox(height: 15), Column( children: [ 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; 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; _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), TextFormField( controller: _locationController, 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.shrink() : 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']; _locationController.text = selectedAddress; final newCoordinates = LatLng( selectedLatitude, selectedLongitude); googleMapController.animateCamera( CameraUpdate.newLatLng( newCoordinates), ); setState(() { _placesList = []; }); }, title: Text(_placesList[index] ['formatted_address']), ); }, ), ), 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) { if (professionalToken != '') { sendPushNotification( professionalToken); } 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, ), ), ), ], ), ), ), ], ), ), ), ), ], ), ), ); } }