diff --git a/lib/src/authentication/authentication_repository.dart b/lib/src/authentication/authentication_repository.dart index e449174..aa29fc5 100644 --- a/lib/src/authentication/authentication_repository.dart +++ b/lib/src/authentication/authentication_repository.dart @@ -187,6 +187,34 @@ class AuthenticationRepository extends GetxController { return city; } + Future getGender(String uid) async { + String gender = ''; + try { + final snapshot = + await FirebaseFirestore.instance.collection('users').doc(uid).get(); + final Map? data = snapshot.data(); + gender = data?['gender'] ?? ''; + } catch (e) { + print('Error getting gender: $e'); + } + return gender; + } + + Future getBirthday(String uid) async { + String birthDate = ''; + try { + final snapshot = + await FirebaseFirestore.instance.collection('users').doc(uid).get(); + final Map? data = snapshot.data(); + birthDate = data?['birth_date'] ?? ''; + + print('nada $birthDate'); + } catch (e) { + print('Error getting birthDate: $e'); + } + return birthDate; + } + Future getCoordsOfCity(String uid) async { String coords = ''; try { diff --git a/lib/src/presentation/screens/login/login.dart b/lib/src/presentation/screens/login/login.dart index 382f713..177aa94 100644 --- a/lib/src/presentation/screens/login/login.dart +++ b/lib/src/presentation/screens/login/login.dart @@ -30,9 +30,11 @@ class _LoginScreenState extends State { SettingModel? settings; void _clearPhoneNumber() { - setState(() { - controller.phoneNo.text = ''; - }); + if (mounted) { + setState(() { + controller.phoneNo.text = ''; + }); + } } void _launchURL(String url) async { @@ -47,11 +49,13 @@ class _LoginScreenState extends State { void initState() { super.initState(); if (settings == null) { - SettingModel.getSettings().then( - (SettingModel value) => setState(() { - settings = value; - }), - ); + SettingModel.getSettings().then((SettingModel value) { + if (mounted) { + setState(() { + settings = value; + }); + } + }); } } @@ -182,12 +186,12 @@ class _LoginScreenState extends State { PhoneAuthController.instance.phoneAuthentication( completePhoneNumber.trim(), ); + _clearPhoneNumber(); await Get.to( () => CodeValidationScreen( phoneNumber: completePhoneNumber.trim(), ), ); - _clearPhoneNumber(); Provider.of(context, listen: false) .initUserProvider(); } diff --git a/lib/src/presentation/screens/my_services.dart b/lib/src/presentation/screens/my_services.dart index 6223396..9124b0d 100644 --- a/lib/src/presentation/screens/my_services.dart +++ b/lib/src/presentation/screens/my_services.dart @@ -9,6 +9,7 @@ import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/models/event_model.dart'; import 'package:prosappco/src/models/scores_model.dart'; import 'package:prosappco/src/presentation/screens/cita.dart'; +import 'package:prosappco/src/presentation/screens/score.dart'; class MyServicesScreen extends StatelessWidget { MyServicesScreen({super.key}); @@ -54,6 +55,7 @@ class MyServicesScreen extends StatelessWidget { .asyncMap((snapshot) async { try { List eventos = []; + for (var element in snapshot.docs) { final event = Event.fromJson(element.data()); event.scoresModel = @@ -79,8 +81,10 @@ class MyServicesScreen extends StatelessWidget { try { eventos.addAll(snapshot.data!); eventos.sort((a, b) => a.timeStamp!.compareTo(b.timeStamp!)); + + print('snapshot mi b ${eventos}'); } catch (e) { - print("Error" + e.toString()); + print("Error snapshot" + e.toString()); } if (eventos.isEmpty) { @@ -122,14 +126,39 @@ class MyServicesScreen extends StatelessWidget { ? Colors.red[100] : Colors.blue[100], onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return CitaScreen(evento: event); - }, - ), - ); + if (event.status == 'terminado') { + if (event.userId == uid) { + if (event.userScored) { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return CitaScreen(evento: event); + }, + ), + ); + } else { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return ScoreScreen( + evento: event, pro: false); + }, + ), + ); + } + } + } else { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return CitaScreen(evento: event); + }, + ), + ); + } }, leading: event.status == 'aprobado' ? const Column( diff --git a/lib/src/presentation/screens/my_services_pro.dart b/lib/src/presentation/screens/my_services_pro.dart index 72006e8..d593ae5 100644 --- a/lib/src/presentation/screens/my_services_pro.dart +++ b/lib/src/presentation/screens/my_services_pro.dart @@ -9,6 +9,7 @@ import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/models/event_model.dart'; import 'package:prosappco/src/models/scores_model.dart'; import 'package:prosappco/src/presentation/screens/cita.dart'; +import 'package:prosappco/src/presentation/screens/score.dart'; class MyServicesProScreen extends StatelessWidget { MyServicesProScreen({super.key}); @@ -98,14 +99,38 @@ class MyServicesProScreen extends StatelessWidget { ? Colors.red[100] : Colors.blue[100], onTap: () { - Navigator.push( - context, - CupertinoPageRoute( - builder: (BuildContext context) { - return CitaScreen(evento: event); - }, - ), - ); + if (event.status == 'terminado') { + if (event.professionalId == uid) { + if (event.professionalScored) { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return CitaScreen(evento: event); + }, + ), + ); + } else { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return ScoreScreen(evento: event, pro: true); + }, + ), + ); + } + } + } else { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return CitaScreen(evento: event); + }, + ), + ); + } }, leading: event.status == 'aprobado' ? const Column( diff --git a/lib/src/presentation/screens/profession.dart b/lib/src/presentation/screens/profession.dart index 0bdd330..7b5e21e 100644 --- a/lib/src/presentation/screens/profession.dart +++ b/lib/src/presentation/screens/profession.dart @@ -4,7 +4,6 @@ import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/presentation/screens/support.dart'; final CollectionReference professionsCollection = FirebaseFirestore.instance.collection('professions'); diff --git a/lib/src/presentation/screens/professional_info.dart b/lib/src/presentation/screens/professional_info.dart index 8dc8fe7..46b2972 100644 --- a/lib/src/presentation/screens/professional_info.dart +++ b/lib/src/presentation/screens/professional_info.dart @@ -1,3 +1,4 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; @@ -38,6 +39,33 @@ class _ProfessionalInfoScreenState extends State { }), ); } + + loadPaymentMethods(); + } + + Map paymentMethods = {}; + + String _formatPaymentMethods(Map paymentMethods) { + List enabledMethods = paymentMethods.entries + .where((entry) => entry.value) + .map((entry) => entry.key) + .toList(); + + return enabledMethods.join(', '); + } + + void loadPaymentMethods() { + FirebaseFirestore.instance + .collection('users') + .doc(widget.professional.id) + .get() + .then((doc) { + if (doc.exists) { + setState(() { + paymentMethods = Map.from(doc['paymentMethods'] ?? {}); + }); + } + }); } @override @@ -156,7 +184,38 @@ class _ProfessionalInfoScreenState extends State { ), ) : const SizedBox(), - const SizedBox(height: 10), + const SizedBox(height: 5), + paymentMethods.containsValue(true) + ? Container( + width: MediaQuery.of(context).size.width * 0.8, + padding: const EdgeInsets.symmetric( + horizontal: 16, vertical: 8), + decoration: BoxDecoration( + color: Colors.blue.withOpacity(0.1), + borderRadius: BorderRadius.circular(10), + ), + child: RichText( + text: TextSpan( + style: const TextStyle( + fontSize: 12, + color: Colors.blue, + ), + children: [ + const TextSpan( + text: 'Métodos de pago recibidos: ', + style: + TextStyle(fontWeight: FontWeight.normal), + ), + TextSpan( + text: _formatPaymentMethods(paymentMethods), + style: const TextStyle( + fontWeight: FontWeight.bold), + ), + ], + ), + )) + : const SizedBox(), + const SizedBox(height: 5), const Text( 'Se unió el 02 de abril del 2023', style: TextStyle(fontSize: 12), diff --git a/lib/src/presentation/screens/professional_profile.dart b/lib/src/presentation/screens/professional_profile.dart index 0799664..d7b487f 100644 --- a/lib/src/presentation/screens/professional_profile.dart +++ b/lib/src/presentation/screens/professional_profile.dart @@ -1,6 +1,5 @@ import 'dart:io'; import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -25,7 +24,6 @@ class ProfessionalProfileScreenState extends State { File? image_cedula; File? image_certificado; - late final FirebaseAuth _auth; final FirebaseStorage storage = FirebaseStorage.instance; final uid = AuthenticationRepository.instance.getCurrentUserUid(); @@ -44,7 +42,6 @@ class ProfessionalProfileScreenState extends State { @override void initState() { super.initState(); - _auth = FirebaseAuth.instance; if (_photo == '...') { AuthenticationRepository.instance .getPhoto(uid.toString()) diff --git a/lib/src/presentation/screens/profile/profile.dart b/lib/src/presentation/screens/profile/profile.dart index 46ba827..0c9098c 100644 --- a/lib/src/presentation/screens/profile/profile.dart +++ b/lib/src/presentation/screens/profile/profile.dart @@ -43,13 +43,14 @@ class _ProfileScreenState extends State { var _photo = '...'; String? _email = ''; String gender = ''; + String genderDb = ''; DateTime? birthDate; + String birthDateDb = ''; @override void initState() { super.initState(); - final uid = AuthenticationRepository.instance.getCurrentUserUid(); _auth = FirebaseAuth.instance; final currentUser = _auth.currentUser; @@ -65,22 +66,40 @@ class _ProfileScreenState extends State { _emailController.text = currentUser.email!; } + if (gender.isEmpty) { + AuthenticationRepository.instance.getGender(uid.toString()).then( + (String s) => setState(() { + genderDb = s; + }), + ); + } + + if (birthDate == null) { + AuthenticationRepository.instance.getBirthday(uid.toString()).then( + (String s) => setState(() { + if (s.isNotEmpty) { + birthDateDb = s; + } + }), + ); + } + _email = currentUser?.email; if (_ciudad == '...') { - AuthenticationRepository.instance - .getCity(uid.toString()) - .then((String s) => setState(() { - _ciudad = s; - })); + AuthenticationRepository.instance.getCity(uid.toString()).then( + (String s) => setState(() { + _ciudad = s; + }), + ); } if (_photo == '...') { - AuthenticationRepository.instance - .getPhoto(uid.toString()) - .then((String s) => setState(() { - _photo = s; - })); + AuthenticationRepository.instance.getPhoto(uid.toString()).then( + (String s) => setState(() { + _photo = s; + }), + ); } } @@ -221,6 +240,32 @@ class _ProfileScreenState extends State { String newEmail = _emailController.text.trim(); String newPassword = _passwordController.text.trim(); + if (gender.isNotEmpty) { + try { + await FirebaseFirestore.instance.collection('users').doc(uid).set({ + 'gender': gender, + }, SetOptions(merge: true)); + + genderDb = gender; + } catch (e) { + print('Error al actualizar el genero: $e'); + } + } + + if (birthDate != null) { + try { + await FirebaseFirestore.instance.collection('users').doc(uid).set({ + 'birth_date': birthDate.toString(), + }, SetOptions(merge: true)); + + birthDateDb = birthDate.toString(); + } catch (e) { + print('Error al actualizar la fecha de nacimiento: $e'); + } + } + + setState(() {}); + if (newName.isEmpty) { Get.snackbar( 'Nombre Invalido', @@ -281,7 +326,7 @@ class _ProfileScreenState extends State { .doc(uid) .update({'email': newEmail}); } catch (e) { - print('e'); + print('Error al actualizar la imagen de perfil $e'); } try { @@ -296,7 +341,11 @@ class _ProfileScreenState extends State { print('Error al actualizar la imagen de perfil $e'); } - // notifyListeners(); + Get.snackbar( + 'Informacion actualizada', + 'Tu informacion ha sido actualizada con exito.', + snackPosition: SnackPosition.BOTTOM, + ); } Future _updateEmailAndPassword( @@ -435,7 +484,7 @@ class _ProfileScreenState extends State { 'Inicia sesión para asegurarnos de que seas tú.', snackPosition: SnackPosition.BOTTOM, ); - AuthenticationRepository.instance.logout(uid!); + // AuthenticationRepository.instance.logout(uid!); } } } @@ -644,26 +693,32 @@ class _ProfileScreenState extends State { ), ), const SizedBox(height: 20.0), - GenderDropdown( - onChanged: (selectedGender) { - setState(() { - gender = selectedGender; - }); - }, - ), - const SizedBox(height: 20.0), - BirthDatePicker( - onDateSelected: (birthDay) { - setState(() { - birthDate = birthDay; - }); - }, - controller: TextEditingController( - text: birthDate == null - ? '' - : formatter.format(birthDate!), - ), - ), + genderDb == '' + ? GenderDropdown( + onChanged: (selectedGender) { + setState(() { + gender = selectedGender; + }); + }, + ) + : const SizedBox(), + genderDb == '' + ? const SizedBox(height: 20.0) + : const SizedBox(), + birthDateDb == '' + ? BirthDatePicker( + onDateSelected: (birthDay) { + setState(() { + birthDate = birthDay; + }); + }, + controller: TextEditingController( + text: birthDate == null + ? '' + : formatter.format(birthDate!), + ), + ) + : const SizedBox(), ], ), ), diff --git a/lib/src/presentation/screens/profile/profile_pro.dart b/lib/src/presentation/screens/profile/profile_pro.dart index 58b44e3..851b92f 100644 --- a/lib/src/presentation/screens/profile/profile_pro.dart +++ b/lib/src/presentation/screens/profile/profile_pro.dart @@ -9,12 +9,13 @@ import 'package:get/get.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/banner_photo.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; -import 'package:prosappco/src/components/primary_btn.dart'; import 'package:prosappco/src/components/schedule_picker.dart'; import 'package:prosappco/src/models/setting_model.dart'; import 'package:prosappco/src/presentation/screens/horario.dart'; import 'package:prosappco/src/presentation/screens/professional.dart'; import 'package:prosappco/src/presentation/screens/professional_direccion.dart'; +import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; +import 'package:prosappco/src/presentation/widgets/shared/primary_checkbox.dart'; import 'package:prosappco/src/services/select_image_profile.dart'; class ProfileProScreen extends StatefulWidget { @@ -47,8 +48,18 @@ class _ProfileProScreenState extends State { int _tarifa = 0; SettingModel? settings; + bool nequiValue = false; + bool banktransferValue = false; + bool datafoneValue = false; + Map? _horarios; + Map paymentMethods = { + 'Nequi': false, + 'Transferencia Bancaria': false, + 'Datafono': false, + }; + @override void initState() { super.initState(); @@ -131,6 +142,8 @@ class _ProfileProScreenState extends State { ), ); } + + loadPaymentMethods(); } void createSchedules() async { @@ -276,6 +289,8 @@ class _ProfileProScreenState extends State { print('Error al actualizar la imagen de perfil $e'); } + updatePaymentMethods(); + Get.snackbar( 'Información actualizada', 'Tu información ha sido actualizada con éxito.', @@ -405,6 +420,22 @@ class _ProfileProScreenState extends State { }); } + void updatePaymentMethods() { + FirebaseFirestore.instance.collection('users').doc(uid).update({ + 'paymentMethods': paymentMethods, + }); + } + + void loadPaymentMethods() { + FirebaseFirestore.instance.collection('users').doc(uid).get().then((doc) { + if (doc.exists) { + setState(() { + paymentMethods = Map.from(doc['paymentMethods'] ?? {}); + }); + } + }); + } + @override Widget build(BuildContext context) { String address = _direccion.toString(); @@ -522,6 +553,33 @@ class _ProfileProScreenState extends State { ) : const SizedBox(), const Divider(), + Padding( + padding: const EdgeInsets.only(left: 30, right: 30, bottom: 30), + child: SizedBox( + width: double.infinity, + child: Column( + children: [ + const Text( + 'Metodos de pago', + style: TextStyle( + color: Colors.black, + fontSize: 17, + ), + ), + for (var entry in paymentMethods.entries) + PrimaryCheckbox( + text: entry.key, + initialValue: entry.value, + onChanged: (value) { + setState(() { + paymentMethods[entry.key] = value; + }); + }, + ), + ], + ), + ), + ), const Padding( padding: EdgeInsets.symmetric(horizontal: 30), child: SizedBox( @@ -530,7 +588,7 @@ class _ProfileProScreenState extends State { 'Horario estandar', style: TextStyle( color: Colors.black, - fontSize: 15, + fontSize: 17, ), ), ), @@ -666,15 +724,14 @@ class _ProfileProScreenState extends State { ), ), ), - PrimaryButtom( - onPressed: () { - updateInfo(); - // name(); - }, - label: 'Guardar'), - const SizedBox( - height: 20, - ) + const SizedBox(height: 10), + PrimaryButton( + onPressed: () { + updateInfo(); + }, + text: 'Guardar', + ), + const SizedBox(height: 20), ], ), ), diff --git a/lib/src/presentation/screens/service.dart b/lib/src/presentation/screens/service.dart index f88ce80..f6ccd1a 100644 --- a/lib/src/presentation/screens/service.dart +++ b/lib/src/presentation/screens/service.dart @@ -26,6 +26,8 @@ import 'package:prosappco/src/presentation/screens/ubicacion.dart'; import 'package:http/http.dart' as http; import 'package:prosappco/src/components/network_utility.dart'; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; +import 'package:prosappco/src/presentation/widgets/shared/warning_snackbar.dart'; import 'package:url_launcher/url_launcher.dart'; class ServiceScreen extends StatefulWidget { @@ -178,9 +180,11 @@ class _ServiceScreenState extends State { ); if (picked != null && picked != _selectedDate) { - setState(() { - _selectedDate = picked; - }); + if (mounted) { + setState(() { + _selectedDate = picked; + }); + } } } @@ -191,9 +195,11 @@ class _ServiceScreenState extends State { ); if (pickedTime != null) { - setState(() { - _selectedTime = pickedTime; - }); + if (mounted) { + setState(() { + _selectedTime = pickedTime; + }); + } } } @@ -252,13 +258,25 @@ class _ServiceScreenState extends State { ), ), ); - - setState(() {}); + if (mounted) { + setState(() {}); + } } catch (e) { print('Error: $e'); } } + @override + void dispose() { + _locationController.dispose(); + _ubicationController.dispose(); + _profesionalController.dispose(); + _serviceTypeController.dispose(); + _observacionController.dispose(); + + super.dispose(); + } + void updateMarkersForServiceType(String serviceType) { getUsersWithActiveStatus(serviceType).then((value) { markers.clear(); @@ -287,9 +305,11 @@ class _ServiceScreenState extends State { PackageInfo packageInfo = await PackageInfo.fromPlatform(); String version = packageInfo.version; - setState(() { - appVersion = version; - }); + if (mounted) { + setState(() { + appVersion = version; + }); + } if (settings != null) { _checkForUpdate(settings); @@ -348,12 +368,14 @@ class _ServiceScreenState extends State { } if (settings == null) { - SettingModel.getSettings().then( - (SettingModel value) => setState(() { - settings = value.version; - _getAppVersion(); - }), - ); + SettingModel.getSettings().then((SettingModel value) => (value) { + if (mounted) { + setState(() { + settings = value.version; + _getAppVersion(); + }); + } + }); } if (_ciudad == '...') { @@ -364,14 +386,18 @@ class _ServiceScreenState extends State { FirebaseFirestore.instance.collection('users').doc(uid).set({ 'city': 'Cúcuta', }).then((_) { - setState(() { - _ciudad = 'Cúcuta'; - }); + if (mounted) { + setState(() { + _ciudad = 'Cúcuta'; + }); + } }); } else { - setState(() { - _ciudad = s; - }); + if (mounted) { + setState(() { + _ciudad = s; + }); + } } }); } @@ -380,27 +406,33 @@ class _ServiceScreenState extends State { AuthenticationRepository.instance .getCoordsOfCity(uid.toString()) .then((String s) { - setState(() { - _coordsOfCity = s; - _setInitialCameraPosition(_coordsOfCity); - }); + if (mounted) { + setState(() { + _coordsOfCity = s; + _setInitialCameraPosition(_coordsOfCity); + }); + } }); } _saveToken(); if (user == null) { - UserModel.getUser(uid.toString()).then( - (UserModel s) => setState(() => user = s), - ); + UserModel.getUser(uid.toString()).then((UserModel s) => (value) { + if (mounted) { + setState(() => user = s); + } + }); } BitmapDescriptor.fromAssetImage( const ImageConfiguration(size: Size(6, 6)), 'images/pro_marke.png') .then((icon) { - setState(() { - _markerIcon = icon; - }); + if (mounted) { + setState(() { + _markerIcon = icon; + }); + } }); updateMarkersForServiceType(_serviceType); @@ -456,9 +488,11 @@ class _ServiceScreenState extends State { String? response = await NetworkUtility.fetchUrl(uri); if (response != null) { - setState(() { - _placesList = jsonDecode(response.toString())['results']; - }); + if (mounted) { + setState(() { + _placesList = jsonDecode(response.toString())['results']; + }); + } } } @@ -511,26 +545,28 @@ class _ServiceScreenState extends State { 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 (mounted) { + 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; - } - }); + if (ubicacion == 'sitio') { + professionalAddress = profesional.realAddress; + _ubicationController.text = + profesional.realAddress; + latUser = profesional.latitude; + lngUser = profesional.longitude; + } + }); + } } } }, @@ -558,11 +594,13 @@ class _ServiceScreenState extends State { final lat = datos[1]; final lng = datos[2]; - setState(() { - _ubicationController.text = formattedAddress; - latUser = lat; - lngUser = lng; - }); + if (mounted) { + setState(() { + _ubicationController.text = formattedAddress; + latUser = lat; + lngUser = lng; + }); + } } }, decoration: const InputDecoration( @@ -826,17 +864,21 @@ class _ServiceScreenState extends State { getLocationName( coordinates.latitude, coordinates.longitude) .then((locationName) { - setState(() { - _locationController.text = locationName; - }); + if (mounted) { + setState(() { + _locationController.text = locationName; + }); + } }); } } }, onCameraMove: (position) { - setState(() { - coordinates = position.target; - }); + if (mounted) { + setState(() { + coordinates = position.target; + }); + } }, gestureRecognizers: >{ Factory( @@ -874,7 +916,9 @@ class _ServiceScreenState extends State { zoom: 17), ), ); - setState(() {}); + if (mounted) { + setState(() {}); + } } catch (e) { Get.snackbar( 'ubicación desactivada', @@ -945,13 +989,16 @@ class _ServiceScreenState extends State { ) as String?; if (serviceType != null) { - setState(() { - _serviceType = serviceType; - _serviceTypeController.text = - _serviceType; + if (mounted) { + setState(() { + _serviceType = serviceType; + _serviceTypeController.text = + _serviceType; - updateMarkersForServiceType(_serviceType); - }); + updateMarkersForServiceType( + _serviceType); + }); + } } }, decoration: const InputDecoration( @@ -984,56 +1031,59 @@ class _ServiceScreenState extends State { ubicacion = datos[1]; if (profesional != null) { - setState(() { - _profesionalController.text = - profesional.name.toString(); + if (mounted) { + setState(() { + _profesionalController.text = + profesional.name.toString(); - professionalId = profesional.id; + 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), - ), - ); + 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), + ), + ); + } + } + }); + } } } }, @@ -1090,9 +1140,11 @@ class _ServiceScreenState extends State { newCoordinates), ); - setState(() { - _placesList = []; - }); + if (mounted) { + setState(() { + _placesList = []; + }); + } }, title: Text(_placesList[index] ['formatted_address']), @@ -1170,12 +1222,18 @@ class _ServiceScreenState extends State { hintText: 'Observaciones'), ), const SizedBox(height: 20), - ElevatedButton( + 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( @@ -1247,23 +1305,9 @@ class _ServiceScreenState extends State { } } }, - 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: 'Solicitar servicio', + minWidth: 230, + ) ], ), ), diff --git a/lib/src/presentation/widgets/shared/primary_checkbox.dart b/lib/src/presentation/widgets/shared/primary_checkbox.dart new file mode 100644 index 0000000..fa448d7 --- /dev/null +++ b/lib/src/presentation/widgets/shared/primary_checkbox.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; + +class PrimaryCheckbox extends StatelessWidget { + final String text; + final bool initialValue; + final Function(bool) onChanged; + + const PrimaryCheckbox({ + Key? key, + required this.text, + required this.initialValue, + required this.onChanged, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return CheckboxListTile( + title: Text( + text, + style: const TextStyle(fontSize: 15), + ), + value: initialValue, + onChanged: (value) { + onChanged(value!); + }, + ); + } +} diff --git a/lib/src/presentation/widgets/shared/warning_snackbar.dart b/lib/src/presentation/widgets/shared/warning_snackbar.dart new file mode 100644 index 0000000..e8028fe --- /dev/null +++ b/lib/src/presentation/widgets/shared/warning_snackbar.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +class WarningSnackbar { + static void show({ + required String title, + required String message, + SnackPosition position = SnackPosition.TOP, + Duration duration = const Duration(seconds: 4), + Color backgroundColor = Colors.red, + Color textColor = Colors.white, + double borderRadius = 10.0, + EdgeInsets margin = const EdgeInsets.all(10.0), + SnackStyle snackStyle = SnackStyle.FLOATING, + Duration animationDuration = const Duration(milliseconds: 800), + bool isDismissible = true, + DismissDirection dismissDirection = DismissDirection.horizontal, + Curve forwardAnimationCurve = Curves.easeOutBack, + Curve reverseAnimationCurve = Curves.easeInBack, + Icon icon = const Icon(Icons.warning, color: Colors.white), + bool shouldIconPulse = true, + }) { + Get.snackbar( + title, + message, + snackPosition: position, + duration: duration, + backgroundColor: backgroundColor, + colorText: textColor, + borderRadius: borderRadius, + margin: margin, + snackStyle: snackStyle, + animationDuration: animationDuration, + isDismissible: isDismissible, + dismissDirection: dismissDirection, + forwardAnimationCurve: forwardAnimationCurve, + reverseAnimationCurve: reverseAnimationCurve, + icon: icon, + shouldIconPulse: shouldIconPulse, + titleText: Text( + title, + style: const TextStyle( + fontSize: 18.0, fontWeight: FontWeight.bold, color: Colors.white), + ), + messageText: Text( + message, + style: const TextStyle(fontSize: 16.0, color: Colors.white), + ), + ); + } +} diff --git a/lib/src/services/firebase_messaging.dart b/lib/src/services/firebase_messaging.dart index fe6d259..56e5610 100644 --- a/lib/src/services/firebase_messaging.dart +++ b/lib/src/services/firebase_messaging.dart @@ -1,7 +1,6 @@ import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:get/get.dart'; - class FirebaseMessagingService { FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; @@ -21,32 +20,35 @@ class FirebaseMessagingService { print('Token FCM: $token'); FirebaseMessaging.onMessage.listen((RemoteMessage message) { - print('Mensaje FCM recibido: ${message.notification?.title} - ${message.notification?.body}'); + print( + 'Mensaje FCM recibido: ${message.notification?.title} - ${message.notification?.body}'); - // Obtener el valor de la clave "screen" de los datos de la notificación - String? notificationScreen = message.data['screen']; - - // Navegar a la pantalla correspondiente según el valor de la clave "screen" - if (notificationScreen == "misservicios") { - // Navegar a MyServicesScreen - Get.offNamed('/misservicios'); - } else if (notificationScreen == "solicitud") { - // Navegar a SolicitudScreen - Get.offNamed('/solicitud'); - } -}); + // Obtener el valor de la clave "screen" de los datos de la notificación + String? notificationScreen = message.data['screen']; + // Navegar a la pantalla correspondiente según el valor de la clave "screen" + if (notificationScreen == "misservicios") { + // Navegar a MyServicesScreen + Get.offNamed('/misservicios'); + } else if (notificationScreen == "solicitud") { + // Navegar a SolicitudScreen + Get.offNamed('/solicitud'); + } + }); // Manejar la notificación cuando se toca y la aplicación está en primer plano (opcional) FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { - print('Mensaje FCM abierto desde la aplicación en primer plano: ${message.notification?.title} - ${message.notification?.body}'); + print( + 'Mensaje FCM abierto desde la aplicación en primer plano: ${message.notification?.title} - ${message.notification?.body}'); // Aquí puedes redirigir al usuario a una pantalla específica o realizar acciones según los datos recibidos }); // Manejar la notificación cuando se toca y la aplicación está cerrada (opcional) - RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage(); + RemoteMessage? initialMessage = + await FirebaseMessaging.instance.getInitialMessage(); if (initialMessage != null) { - print('Mensaje FCM abierto desde la aplicación cerrada: ${initialMessage.notification?.title} - ${initialMessage.notification?.body}'); + print( + 'Mensaje FCM abierto desde la aplicación cerrada: ${initialMessage.notification?.title} - ${initialMessage.notification?.body}'); // Aquí puedes redirigir al usuario a una pantalla específica o realizar acciones según los datos recibidos } } diff --git a/pubspec.yaml b/pubspec.yaml index ffd3068..a825cde 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -51,7 +51,7 @@ dependencies: flutter_animate: flutter_rating_bar: table_calendar: - google_maps_flutter: ^2.2.5 + google_maps_flutter: ^2.5.0 flutter_polyline_points: ^2.0.0 http: ^1.1.0 geolocator: ^9.0.2