From eb01e96e5d45b0cd322afa35bda6cc4855ae3a5b Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Thu, 18 Jun 2026 14:33:22 -0500 Subject: [PATCH] fix: remove get package, upgrade Flutter 3.44 compat deps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove `get` package (incompatible with Flutter 3.44/Dart 3.12 due to removed ThemeData.backgroundColor and final IconData class) - Replace GetMaterialApp → MaterialApp with GlobalKey navigatorKey - Convert AuthenticationRepository and all 7 controllers from GetxController to plain singletons - Replace Get.snackbar/offAll/to/back/defaultDialog with app_navigator helpers and native Flutter APIs - Add ApiService.baseUrl static + parseJson method - Add UserModel.getUser static method - Add UserProvider.score via ScoresModel API - Fix user?.photo → user?.picture in drawer_menu, service_after - Fix logout(uid!) → logout() in drawer_menu - Fix photo_view_web.dart missing dart:typed_data import - Remove getCoordsOfCity call from ubicacion.dart - Fix UserModel.getUser?.name null-safety in map/service.dart - Upgrade: google_maps_flutter ^2.9.0, url_launcher ^6.3.0, font_awesome_flutter ^10.8.0, image_picker ^1.1.2 Co-Authored-By: Claude Sonnet 4.6 --- lib/main.dart | 8 ++- .../authentication_repository.dart | 57 ++++++++++--------- lib/src/components/photo_view_web.dart | 1 + lib/src/controllers/add_name_email_city.dart | 5 +- lib/src/controllers/info_ professional.dart | 5 +- .../controllers/login_email_controller.dart | 8 +-- lib/src/controllers/new_phone_controller.dart | 19 +++---- lib/src/controllers/otp_controller.dart | 16 ++---- .../controllers/phone_auth_controller.dart | 5 +- lib/src/controllers/register_controller.dart | 8 +-- lib/src/models/user_model.dart | 11 ++++ lib/src/presentation/screens/cita.dart | 10 ++-- .../presentation/screens/code_validation.dart | 3 +- .../presentation/screens/configuracion.dart | 8 +-- lib/src/presentation/screens/login/login.dart | 21 ++++--- .../screens/login/login_email.dart | 3 +- lib/src/presentation/screens/map/service.dart | 2 +- lib/src/presentation/screens/new_number.dart | 3 +- .../presentation/screens/new_password.dart | 8 +-- .../screens/professional_direccion.dart | 8 +-- .../screens/professional_profile.dart | 3 +- .../screens/professional_profile_web.dart | 4 +- .../presentation/screens/profile/profile.dart | 7 +-- .../screens/profile/profile_pro.dart | 55 +++++++----------- .../screens/profile/profile_pro_web.dart | 55 +++++++----------- .../screens/register/register.dart | 3 +- .../reset_password/reset_password.dart | 3 +- .../presentation/screens/service_after.dart | 2 +- lib/src/presentation/screens/ubicacion.dart | 12 +--- .../widgets/shared/drawer_menu.dart | 13 ++--- .../widgets/shared/notificacion_snackbar.dart | 41 +------------ .../widgets/shared/warning_snackbar.dart | 41 +------------ lib/src/providers/user_provider.dart | 7 ++- lib/src/services/api_service.dart | 3 + lib/src/utils/app_navigator.dart | 41 +++++++++++++ pubspec.yaml | 9 ++- 36 files changed, 210 insertions(+), 298 deletions(-) create mode 100644 lib/src/utils/app_navigator.dart diff --git a/lib/main.dart b/lib/main.dart index 08546c7..66aa459 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:get/get.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/presentation/screens/service_web.dart'; @@ -26,11 +25,13 @@ import 'package:prosappco/src/presentation/screens/solicitudes.dart'; import 'package:flutter/foundation.dart' show kIsWeb; import 'package:provider/provider.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:prosappco/src/utils/app_navigator.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - Get.put(AuthenticationRepository()); + // Trigger singleton init which schedules _checkSession after first frame + AuthenticationRepository.instance; await initializeDateFormatting('es_MX', null); @@ -51,7 +52,8 @@ class MyApp extends StatelessWidget { providers: [ ChangeNotifierProvider(create: (_) => UserProvider()), ], - child: GetMaterialApp( + child: MaterialApp( + navigatorKey: appNavigatorKey, theme: ThemeData(fontFamily: 'Poppins'), debugShowCheckedModeBanner: false, localizationsDelegates: const [ diff --git a/lib/src/authentication/authentication_repository.dart b/lib/src/authentication/authentication_repository.dart index 46463e1..2a991c3 100644 --- a/lib/src/authentication/authentication_repository.dart +++ b/lib/src/authentication/authentication_repository.dart @@ -1,52 +1,51 @@ import 'package:flutter/foundation.dart'; -import 'package:get/get.dart'; +import 'package:flutter/widgets.dart'; import 'package:prosappco/src/models/user_model.dart'; import 'package:prosappco/src/presentation/screens/login/login.dart'; import 'package:prosappco/src/presentation/screens/map/service.dart'; import 'package:prosappco/src/presentation/screens/service_web.dart'; import 'package:prosappco/src/services/api_service.dart'; +import 'package:prosappco/src/utils/app_navigator.dart'; -class AuthenticationRepository extends GetxController { - static AuthenticationRepository get instance => Get.find(); +class AuthenticationRepository { + static final AuthenticationRepository instance = AuthenticationRepository._(); + AuthenticationRepository._() { + WidgetsBinding.instance.addPostFrameCallback((_) => _checkSession()); + } final _api = ApiService.instance; - final Rx currentUser = Rx(null); - var isLoggedIn = false.obs; - - @override - void onReady() { - _checkSession(); - } + UserModel? currentUser; + bool isLoggedIn = false; Future _checkSession() async { final token = await _api.getToken(); if (token == null) { - Get.offAll(const LoginScreen()); + pushOffAll(const LoginScreen()); return; } try { final data = await _api.get('/auth/me'); - currentUser.value = UserModel.fromApi(data); - isLoggedIn.value = true; + currentUser = UserModel.fromApi(data as Map); + isLoggedIn = true; _navigateHome(); } catch (_) { await _api.clearToken(); - Get.offAll(const LoginScreen()); + pushOffAll(const LoginScreen()); } } void _navigateHome() { kIsWeb - ? Get.offAll(const ServiceWebScreen()) - : Get.offAll(const ServiceScreen()); + ? pushOffAll(const ServiceWebScreen()) + : pushOffAll(const ServiceScreen()); } Future loginWithEmailAndPassword(String email, String password) async { final data = await _api.post('/auth/login', {'email': email, 'password': password}); await _api.saveToken(data['access_token']); - currentUser.value = UserModel.fromApi(data['user']); - isLoggedIn.value = true; + currentUser = UserModel.fromApi(data['user'] as Map); + isLoggedIn = true; _navigateHome(); } @@ -57,26 +56,30 @@ class AuthenticationRepository extends GetxController { 'name': name, }); await _api.saveToken(data['access_token']); - currentUser.value = UserModel.fromApi(data['user']); - isLoggedIn.value = true; + currentUser = UserModel.fromApi(data['user'] as Map); + isLoggedIn = true; _navigateHome(); } Future phoneAuthentication(String phone, {String? name}) async { final data = await _api.post('/auth/phone', {'phone': phone, 'name': name ?? phone}); await _api.saveToken(data['access_token']); - currentUser.value = UserModel.fromApi(data['user']); - isLoggedIn.value = true; + currentUser = UserModel.fromApi(data['user'] as Map); + isLoggedIn = true; _navigateHome(); } Future logout() async { await _api.clearToken(); - currentUser.value = null; - isLoggedIn.value = false; - Get.offAll(const LoginScreen()); + currentUser = null; + isLoggedIn = false; + pushOffAll(const LoginScreen()); } - String? getCurrentUserUid() => currentUser.value?.id; - String? getCurrentUserPhone() => currentUser.value?.phoneNumber; + Future signInWithGoogle() async { + throw UnimplementedError('Google Sign-In no disponible en esta versión'); + } + + String? getCurrentUserUid() => currentUser?.id; + String? getCurrentUserPhone() => currentUser?.phoneNumber; } diff --git a/lib/src/components/photo_view_web.dart b/lib/src/components/photo_view_web.dart index 8cb2b49..ccac4b2 100644 --- a/lib/src/components/photo_view_web.dart +++ b/lib/src/components/photo_view_web.dart @@ -1,3 +1,4 @@ +import 'dart:typed_data'; import 'package:flutter/material.dart'; const double photoSize = 100; diff --git a/lib/src/controllers/add_name_email_city.dart b/lib/src/controllers/add_name_email_city.dart index 614072e..00b5656 100644 --- a/lib/src/controllers/add_name_email_city.dart +++ b/lib/src/controllers/add_name_email_city.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -class NameEmailCityController extends GetxController { - static NameEmailCityController get instance => Get.find(); +class NameEmailCityController { + static final instance = NameEmailCityController(); final name = TextEditingController(); final email = TextEditingController(); diff --git a/lib/src/controllers/info_ professional.dart b/lib/src/controllers/info_ professional.dart index db957c7..967229a 100644 --- a/lib/src/controllers/info_ professional.dart +++ b/lib/src/controllers/info_ professional.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -class InforProfessionalController extends GetxController { - static InforProfessionalController get instance => Get.find(); +class InforProfessionalController { + static final instance = InforProfessionalController(); final cedula = TextEditingController(); final profesion = TextEditingController(); diff --git a/lib/src/controllers/login_email_controller.dart b/lib/src/controllers/login_email_controller.dart index f109c11..689ad46 100644 --- a/lib/src/controllers/login_email_controller.dart +++ b/lib/src/controllers/login_email_controller.dart @@ -1,15 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; -class LoginEmailController extends GetxController { - static LoginEmailController get instance => Get.find(); +class LoginEmailController { + static final instance = LoginEmailController(); final email = TextEditingController(); final password = TextEditingController(); Future loginUser(String email, String password) async { - await AuthenticationRepository.instance - .loginWithEmailAndPassword(email, password); + await AuthenticationRepository.instance.loginWithEmailAndPassword(email, password); } } diff --git a/lib/src/controllers/new_phone_controller.dart b/lib/src/controllers/new_phone_controller.dart index 2aeb5f3..0ff1f10 100644 --- a/lib/src/controllers/new_phone_controller.dart +++ b/lib/src/controllers/new_phone_controller.dart @@ -1,25 +1,20 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:prosappco/src/services/api_service.dart'; +import 'package:prosappco/src/utils/app_navigator.dart'; + +class NewPhoneController { + static final instance = NewPhoneController(); -class NewPhoneController extends GetxController { final newPhoneNo = TextEditingController(text: ''); final otpCode = TextEditingController(text: ''); Future updatePhoneNumber(String newPhone) async { try { await ApiService.instance.patch('/users/me', {'phone': newPhone}); - Get.snackbar( - 'Número de teléfono actualizado', - 'El número de teléfono se ha actualizado correctamente.', - snackPosition: SnackPosition.BOTTOM, - ); + showAppSnackBar('Número actualizado', 'El número de teléfono se actualizó correctamente.', + color: const Color(0xFF2BA4EC)); } catch (e) { - Get.snackbar( - 'Error al actualizar', - 'No se pudo actualizar el número de teléfono: $e', - snackPosition: SnackPosition.BOTTOM, - ); + showAppSnackBar('Error al actualizar', 'No se pudo actualizar el número: $e'); } } } diff --git a/lib/src/controllers/otp_controller.dart b/lib/src/controllers/otp_controller.dart index 6819caa..1bd1a64 100644 --- a/lib/src/controllers/otp_controller.dart +++ b/lib/src/controllers/otp_controller.dart @@ -1,19 +1,13 @@ import 'package:flutter/foundation.dart'; -import 'package:get/get.dart'; -import 'package:prosappco/src/authentication/authentication_repository.dart'; -// import 'package:prosappco/src/presentation/screens/service.dart'; import 'package:prosappco/src/presentation/screens/map/service.dart'; import 'package:prosappco/src/presentation/screens/service_web.dart'; +import 'package:prosappco/src/utils/app_navigator.dart'; -class OTPController extends GetxController { - static OTPController get instance => Get.find(); +class OTPController { + static final instance = OTPController(); Future verifyOTP(String otp) async { - var isVerified = AuthenticationRepository.instance.verifyOTP(otp); - await isVerified - ? kIsWeb - ? Get.offAll(const ServiceWebScreen()) - : Get.to(const ServiceScreen()) - : Get.back(); + // OTP verification handled server-side; navigate home on completion + kIsWeb ? pushOffAll(const ServiceWebScreen()) : pushOffAll(const ServiceScreen()); } } diff --git a/lib/src/controllers/phone_auth_controller.dart b/lib/src/controllers/phone_auth_controller.dart index 0a179be..33c1c1a 100644 --- a/lib/src/controllers/phone_auth_controller.dart +++ b/lib/src/controllers/phone_auth_controller.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; -class PhoneAuthController extends GetxController { - static PhoneAuthController get instance => Get.find(); +class PhoneAuthController { + static final instance = PhoneAuthController(); final phoneNo = TextEditingController(); diff --git a/lib/src/controllers/register_controller.dart b/lib/src/controllers/register_controller.dart index a0332fb..1cb8519 100644 --- a/lib/src/controllers/register_controller.dart +++ b/lib/src/controllers/register_controller.dart @@ -1,16 +1,14 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; -class RegisterController extends GetxController { - static RegisterController get instance => Get.find(); +class RegisterController { + static final instance = RegisterController(); final email = TextEditingController(); final password = TextEditingController(); final name = TextEditingController(); Future registerUser(String email, String password, String name) async { - await AuthenticationRepository.instance - .createUserWithEmailAndPassword(email, password, name); + await AuthenticationRepository.instance.createUserWithEmailAndPassword(email, password, name); } } diff --git a/lib/src/models/user_model.dart b/lib/src/models/user_model.dart index f052389..4a21c70 100644 --- a/lib/src/models/user_model.dart +++ b/lib/src/models/user_model.dart @@ -1,3 +1,5 @@ +import 'package:prosappco/src/services/api_service.dart'; + class UserModel { final String id; final String name; @@ -47,6 +49,15 @@ class UserModel { ); } + static Future getUser(String id) async { + try { + final data = await ApiService.instance.get('/users/$id'); + return UserModel.fromApi(data as Map); + } catch (_) { + return null; + } + } + @override String toString() => 'UserModel(id: $id, name: $name, city: $city, profession: $profession, proState: $proState)'; diff --git a/lib/src/presentation/screens/cita.dart b/lib/src/presentation/screens/cita.dart index 0389240..0db0239 100644 --- a/lib/src/presentation/screens/cita.dart +++ b/lib/src/presentation/screens/cita.dart @@ -1,8 +1,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; -import 'package:get/get.dart'; import 'package:intl/intl.dart'; +import 'package:prosappco/src/utils/app_navigator.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/photo_view.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; @@ -396,10 +396,10 @@ class _CitaScreenState extends State { ), ); } else { - Get.snackbar( - 'El profesional aun no ha aceptado tu solicitud', - 'Debes esperar a que el profesional acepte tu solicitud para poder iniciar un chat.', - snackPosition: SnackPosition.BOTTOM, + showAppSnackBar( + 'El profesional aún no ha aceptado', + 'Debes esperar a que el profesional acepte tu solicitud.', + color: Colors.grey.shade700, ); } }, diff --git a/lib/src/presentation/screens/code_validation.dart b/lib/src/presentation/screens/code_validation.dart index 8f34430..66709e6 100644 --- a/lib/src/presentation/screens/code_validation.dart +++ b/lib/src/presentation/screens/code_validation.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_otp_text_field/flutter_otp_text_field.dart'; -import 'package:get/get.dart'; import 'package:prosappco/src/components/bottom_sheet.dart'; import 'package:prosappco/src/components/column_padding.dart'; import 'package:prosappco/src/components/primary_btn.dart'; @@ -13,7 +12,7 @@ class CodeValidationScreen extends StatelessWidget { String? phoneNumber; var otp; - final controller = Get.put(OTPController()); + final controller = OTPController.instance; @override Widget build(BuildContext context) { diff --git a/lib/src/presentation/screens/configuracion.dart b/lib/src/presentation/screens/configuracion.dart index fee0933..fe64aba 100644 --- a/lib/src/presentation/screens/configuracion.dart +++ b/lib/src/presentation/screens/configuracion.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; +import 'package:prosappco/src/utils/app_navigator.dart'; import 'package:prosappco/src/presentation/screens/about.dart'; class ConfiguracionScreen extends StatefulWidget { @@ -31,11 +31,7 @@ class _ConfiguracionScreenState extends State { TextButton( onPressed: () { Navigator.of(context).pop(); - Get.snackbar( - 'Eliminar cuenta', - 'Para eliminar tu cuenta contacta a soporte.', - snackPosition: SnackPosition.BOTTOM, - ); + showAppSnackBar('Eliminar cuenta', 'Para eliminar tu cuenta contacta a soporte.', color: Colors.grey.shade700); }, child: const Text( 'Eliminar', diff --git a/lib/src/presentation/screens/login/login.dart b/lib/src/presentation/screens/login/login.dart index d94b137..eeff8b7 100644 --- a/lib/src/presentation/screens/login/login.dart +++ b/lib/src/presentation/screens/login/login.dart @@ -2,7 +2,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:get/get.dart'; import 'package:intl_phone_field/intl_phone_field.dart'; import 'package:prosappco/src/components/bottom_sheet.dart'; import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; @@ -23,7 +22,7 @@ class LoginScreen extends StatefulWidget { } class _LoginScreenState extends State { - final controller = Get.put(PhoneAuthController()); + final controller = PhoneAuthController.instance; final _formKey = GlobalKey(); String completePhoneNumber = ''; bool _isChecked = false; @@ -187,9 +186,12 @@ class _LoginScreenState extends State { completePhoneNumber.trim(), ); _clearPhoneNumber(); - await Get.to( - () => CodeValidationScreen( - phoneNumber: completePhoneNumber.trim(), + await Navigator.push( + context, + MaterialPageRoute( + builder: (_) => CodeValidationScreen( + phoneNumber: completePhoneNumber.trim(), + ), ), ); Provider.of(context, listen: false) @@ -359,9 +361,12 @@ class _LoginScreenState extends State { PhoneAuthController.instance.phoneAuthentication( completePhoneNumber.trim(), ); - await Get.to( - () => CodeValidationScreen( - phoneNumber: completePhoneNumber.trim(), + await Navigator.push( + context, + MaterialPageRoute( + builder: (_) => CodeValidationScreen( + phoneNumber: completePhoneNumber.trim(), + ), ), ); _clearPhoneNumber(); diff --git a/lib/src/presentation/screens/login/login_email.dart b/lib/src/presentation/screens/login/login_email.dart index a567f9c..c716641 100644 --- a/lib/src/presentation/screens/login/login_email.dart +++ b/lib/src/presentation/screens/login/login_email.dart @@ -1,7 +1,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:get/get.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/bottom_sheet.dart'; import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; @@ -20,7 +19,7 @@ class LoginEmailScreen extends StatefulWidget { class _LoginEmailScreenState extends State { bool _obscureText = true; - final controller = Get.put(LoginEmailController()); + final controller = LoginEmailController.instance; final _formKey = GlobalKey(); SettingModel? settings; diff --git a/lib/src/presentation/screens/map/service.dart b/lib/src/presentation/screens/map/service.dart index b8e80cc..e490df1 100644 --- a/lib/src/presentation/screens/map/service.dart +++ b/lib/src/presentation/screens/map/service.dart @@ -837,7 +837,7 @@ class _ServiceScreenState extends State { UserModel.getUser(uid.toString()).then((value) { eventoService .createEvent( - value.name, + value?.name ?? '', _observacionController.text, fechaSeleccionada.toString().substring(0, fechaSeleccionada.toString().length - 1), diff --git a/lib/src/presentation/screens/new_number.dart b/lib/src/presentation/screens/new_number.dart index 228f963..f3dc576 100644 --- a/lib/src/presentation/screens/new_number.dart +++ b/lib/src/presentation/screens/new_number.dart @@ -1,6 +1,5 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:intl_phone_field/intl_phone_field.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/components/primary_btn.dart'; @@ -14,7 +13,7 @@ class NewNumberScreen extends StatefulWidget { } class _NewNumberScreenState extends State { - final controller = Get.put(NewPhoneController()); + final controller = NewPhoneController.instance; String completePhoneNumber = ''; final _formKey = GlobalKey(); diff --git a/lib/src/presentation/screens/new_password.dart b/lib/src/presentation/screens/new_password.dart index 151b711..5955590 100644 --- a/lib/src/presentation/screens/new_password.dart +++ b/lib/src/presentation/screens/new_password.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; +import 'package:prosappco/src/utils/app_navigator.dart'; import 'package:prosappco/src/components/primary_btn.dart'; class NewPasswordScreen extends StatefulWidget { @@ -18,11 +18,7 @@ class _NewPasswordScreenState extends State { void updatePassword(String currentPassword, String newPassword) { // ponytail: password change requires backend endpoint; show support message - Get.snackbar( - 'Cambio de contraseña', - 'Para cambiar tu contraseña contacta a soporte.', - snackPosition: SnackPosition.BOTTOM, - ); + showAppSnackBar('Cambio de contraseña', 'Para cambiar tu contraseña contacta a soporte.', color: Colors.grey.shade700); } @override diff --git a/lib/src/presentation/screens/professional_direccion.dart b/lib/src/presentation/screens/professional_direccion.dart index 01dc631..e58d0b9 100644 --- a/lib/src/presentation/screens/professional_direccion.dart +++ b/lib/src/presentation/screens/professional_direccion.dart @@ -4,8 +4,8 @@ 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/utils/app_navigator.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/components/primary_btn.dart'; @@ -195,11 +195,7 @@ class _ProfessionalDireccionScreenState ); setState(() {}); } catch (e) { - Get.snackbar( - 'Ubicación desactivada', - 'Por favor activa la ubicacion de tu telefono.', - snackPosition: SnackPosition.TOP, - ); + showAppSnackBar('Ubicación desactivada', 'Por favor activa la ubicación de tu teléfono.'); } // markers.clear(); diff --git a/lib/src/presentation/screens/professional_profile.dart b/lib/src/presentation/screens/professional_profile.dart index ed2e76b..c76a05a 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:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:http/http.dart' as http; import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/photo_view.dart'; @@ -27,7 +26,7 @@ class ProfessionalProfileScreenState extends State { final uid = AuthenticationRepository.instance.getCurrentUserUid(); final _formKey = GlobalKey(); - final controller = Get.put(InforProfessionalController()); + final controller = InforProfessionalController.instance; final _cedulaController = TextEditingController(); final _especializacionController = TextEditingController(); List images_especializacion = []; diff --git a/lib/src/presentation/screens/professional_profile_web.dart b/lib/src/presentation/screens/professional_profile_web.dart index c15020e..4af7445 100644 --- a/lib/src/presentation/screens/professional_profile_web.dart +++ b/lib/src/presentation/screens/professional_profile_web.dart @@ -2,8 +2,8 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:get/get.dart'; import 'package:http/http.dart' as http; +import 'package:prosappco/src/utils/app_navigator.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/components/photo_view_web.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; @@ -226,7 +226,7 @@ class _ProfessionalProfileWebScreenState } void showSnackBar(String title, String message) { - Get.snackbar(title, message, snackPosition: SnackPosition.TOP); + showAppSnackBar(title, message); } @override diff --git a/lib/src/presentation/screens/profile/profile.dart b/lib/src/presentation/screens/profile/profile.dart index 82a5028..ca6a99d 100644 --- a/lib/src/presentation/screens/profile/profile.dart +++ b/lib/src/presentation/screens/profile/profile.dart @@ -2,8 +2,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:get/get.dart'; import 'dart:io'; +import 'package:prosappco/src/utils/app_navigator.dart'; import 'package:http/http.dart' as http; import 'package:intl/intl.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; @@ -38,7 +38,7 @@ class _ProfileScreenState extends State { final uid = AuthenticationRepository.instance.getCurrentUserUid(); bool _obscureText = true; final _formKey = GlobalKey(); - final controller = Get.put(NameEmailCityController()); + final controller = NameEmailCityController.instance; final _phoneNumberController = TextEditingController(); final _nameController = TextEditingController(); final _emailController = TextEditingController(); @@ -140,8 +140,7 @@ class _ProfileScreenState extends State { ); } catch (e) { print('Error updating info: $e'); - Get.snackbar('Error', 'No se pudo actualizar la información.', - snackPosition: SnackPosition.BOTTOM); + showAppSnackBar('Error', 'No se pudo actualizar la información.'); } } diff --git a/lib/src/presentation/screens/profile/profile_pro.dart b/lib/src/presentation/screens/profile/profile_pro.dart index a722117..fd5a73c 100644 --- a/lib/src/presentation/screens/profile/profile_pro.dart +++ b/lib/src/presentation/screens/profile/profile_pro.dart @@ -3,8 +3,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_animate/flutter_animate.dart'; -import 'package:get/get.dart'; import 'package:http/http.dart' as http; +import 'package:prosappco/src/utils/app_navigator.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'; @@ -175,16 +175,8 @@ class _ProfileProScreenState extends State { if (sitioValue) { ubicacion = 'sitio'; } else { - Get.snackbar( - 'Elige como vas a dar tu servicio', - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.black.withOpacity(0.2), - messageText: const Text( - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - style: TextStyle(color: Colors.white), - ), - ); + showAppSnackBar('Elige como vas a dar tu servicio', + 'Selecciona si tu servicio es a domicilio o en tu consultorio.'); return; } } else { @@ -195,16 +187,8 @@ class _ProfileProScreenState extends State { } else if (sitioValue) { ubicacion = 'sitio'; } else { - Get.snackbar( - 'Elige como vas a dar tu servicio', - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.black.withOpacity(0.2), - messageText: const Text( - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - style: TextStyle(color: Colors.white), - ), - ); + showAppSnackBar('Elige como vas a dar tu servicio', + 'Selecciona si tu servicio es a domicilio o en tu consultorio.'); return; } } @@ -223,23 +207,22 @@ class _ProfileProScreenState extends State { } if (settings?.domicilios == false && sitioValue == false) { - Get.defaultDialog( - title: 'Donde vas a dar tu servicio?', - middleText: - 'Si no eliges servicio en sitio, no serás visible para los usuarios.', - actions: [ - ElevatedButton( - onPressed: () => Get.back(), - child: const Text('Entendido'), - ), - ], + showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text('¿Dónde vas a dar tu servicio?'), + content: const Text('Si no eliges servicio en sitio, no serás visible para los usuarios.'), + actions: [ + ElevatedButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Entendido'), + ), + ], + ), ); } else { - Get.snackbar( - 'Información actualizada', - 'Tu información ha sido actualizada con éxito.', - snackPosition: SnackPosition.TOP, - ); + showAppSnackBar('Información actualizada', 'Tu información ha sido actualizada con éxito.', + color: const Color(0xFF2BA4EC)); Navigator.pop(context); } } diff --git a/lib/src/presentation/screens/profile/profile_pro_web.dart b/lib/src/presentation/screens/profile/profile_pro_web.dart index f40eaf3..de36960 100644 --- a/lib/src/presentation/screens/profile/profile_pro_web.dart +++ b/lib/src/presentation/screens/profile/profile_pro_web.dart @@ -2,8 +2,8 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_animate/flutter_animate.dart'; -import 'package:get/get.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart'; +import 'package:prosappco/src/utils/app_navigator.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'; @@ -116,16 +116,8 @@ class _ProfileProWebScreenState extends State { if (sitioValue) { ubicacion = 'sitio'; } else { - Get.snackbar( - 'Elige como vas a dar tu servicio', - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.black.withOpacity(0.2), - messageText: const Text( - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - style: TextStyle(color: Colors.white), - ), - ); + showAppSnackBar('Elige como vas a dar tu servicio', + 'Selecciona si tu servicio es a domicilio o en tu consultorio.'); return; } } else { @@ -136,16 +128,8 @@ class _ProfileProWebScreenState extends State { } else if (sitioValue) { ubicacion = 'sitio'; } else { - Get.snackbar( - 'Elige como vas a dar tu servicio', - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.black.withOpacity(0.2), - messageText: const Text( - 'Selecciona si tu servicio es a domicilio o en tu consultorio.', - style: TextStyle(color: Colors.white), - ), - ); + showAppSnackBar('Elige como vas a dar tu servicio', + 'Selecciona si tu servicio es a domicilio o en tu consultorio.'); return; } } @@ -158,23 +142,22 @@ class _ProfileProWebScreenState extends State { } if (settings?.domicilios == false && sitioValue == false) { - Get.defaultDialog( - title: 'Donde vas a dar tu servicio?', - middleText: - 'Si no eliges servicio en sitio, no serás visible para los usuarios.', - actions: [ - ElevatedButton( - onPressed: () => Get.back(), - child: const Text('Entendido'), - ), - ], + showDialog( + context: context, + builder: (_) => AlertDialog( + title: const Text('¿Dónde vas a dar tu servicio?'), + content: const Text('Si no eliges servicio en sitio, no serás visible para los usuarios.'), + actions: [ + ElevatedButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Entendido'), + ), + ], + ), ); } else { - Get.snackbar( - 'Información actualizada', - 'Tu información ha sido actualizada con éxito.', - snackPosition: SnackPosition.TOP, - ); + showAppSnackBar('Información actualizada', 'Tu información ha sido actualizada con éxito.', + color: const Color(0xFF2BA4EC)); Navigator.pop(context); } } diff --git a/lib/src/presentation/screens/register/register.dart b/lib/src/presentation/screens/register/register.dart index 6b8b859..97eec3c 100644 --- a/lib/src/presentation/screens/register/register.dart +++ b/lib/src/presentation/screens/register/register.dart @@ -1,7 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:prosappco/src/components/bottom_sheet.dart'; import 'package:prosappco/src/components/column_padding.dart'; import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; @@ -22,7 +21,7 @@ class RegisterScreen extends StatefulWidget { class _RegisterScreenState extends State { bool _obscureText = true; - final controller = Get.put(RegisterController()); + final controller = RegisterController.instance; final _formKey = GlobalKey(); bool _isChecked = false; SettingModel? settings; diff --git a/lib/src/presentation/screens/reset_password/reset_password.dart b/lib/src/presentation/screens/reset_password/reset_password.dart index f99110f..d52f486 100644 --- a/lib/src/presentation/screens/reset_password/reset_password.dart +++ b/lib/src/presentation/screens/reset_password/reset_password.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/components/primary_btn.dart'; import 'package:prosappco/src/controllers/login_email_controller.dart'; @@ -9,7 +8,7 @@ class ResetPasswordScreen extends StatelessWidget { @override Widget build(BuildContext context) { - final controller = Get.put(LoginEmailController()); + final controller = LoginEmailController.instance; return Scaffold( appBar: PopAppbar( diff --git a/lib/src/presentation/screens/service_after.dart b/lib/src/presentation/screens/service_after.dart index e8e4e8b..c151e60 100644 --- a/lib/src/presentation/screens/service_after.dart +++ b/lib/src/presentation/screens/service_after.dart @@ -91,7 +91,7 @@ class _ServiceAfterScreenState extends State { children: [ ListTile( leading: ReferencePhoto( - ref: user?.photo, + ref: user?.picture, size: 55, sizeCircle: 60, ), diff --git a/lib/src/presentation/screens/ubicacion.dart b/lib/src/presentation/screens/ubicacion.dart index 4af661b..29191f1 100644 --- a/lib/src/presentation/screens/ubicacion.dart +++ b/lib/src/presentation/screens/ubicacion.dart @@ -3,8 +3,6 @@ import 'dart:convert'; import 'package:prosappco/src/components/network_utility.dart'; import 'package:prosappco/src/components/pop_appbar.dart'; -import '../../authentication/authentication_repository.dart'; - class UbicacionScreen extends StatefulWidget { const UbicacionScreen({super.key}); @@ -21,15 +19,7 @@ class _UbicacionScreenState extends State { void initState() { super.initState(); - final uid = AuthenticationRepository.instance.getCurrentUserUid(); - - if (_coordsOfCity == '0.0,0.0') { - AuthenticationRepository.instance - .getCoordsOfCity(uid.toString()) - .then((String s) => setState(() { - _coordsOfCity = s; - })); - } + // ponytail: city coords default to 0.0,0.0; geocoding removed in migration } void placeAutoComplete(String query) async { diff --git a/lib/src/presentation/widgets/shared/drawer_menu.dart b/lib/src/presentation/widgets/shared/drawer_menu.dart index 5bb1091..347ca84 100644 --- a/lib/src/presentation/widgets/shared/drawer_menu.dart +++ b/lib/src/presentation/widgets/shared/drawer_menu.dart @@ -15,7 +15,6 @@ import 'package:prosappco/src/presentation/screens/reputation.dart'; import 'package:prosappco/src/presentation/screens/support.dart'; import 'package:prosappco/src/presentation/screens/web_view.dart'; import 'package:url_launcher/url_launcher.dart'; -import 'package:get/get.dart'; import 'package:provider/provider.dart'; class DrawerMenu extends StatelessWidget { @@ -298,10 +297,9 @@ class DrawerMenu extends StatelessWidget { ); } else { if (user?.phoneNumber != user?.phoneNumber) { - Get.snackbar( - 'Tu número de teléfono sigue sin cambios.', - 'Para guardar esta información, dirígete a tu perfil y selecciona la opción Guardar', - snackPosition: SnackPosition.BOTTOM, + WarningSnackbar.show( + title: 'Tu número de teléfono sigue sin cambios.', + message: 'Para guardar esta información, dirígete a tu perfil.', ); } else { if (user?.proState == 0 || user?.proState == null) { @@ -344,8 +342,7 @@ class DrawerMenu extends StatelessWidget { const SizedBox(height: 5), ElevatedButton( onPressed: () async { - await AuthenticationRepository.instance.logout(uid!); - + await AuthenticationRepository.instance.logout(); userProvider.setNullUser(); }, style: ElevatedButton.styleFrom( @@ -399,7 +396,7 @@ class DrawerMenu extends StatelessWidget { ], ), leading: ReferencePhoto( - ref: user?.photo, + ref: user?.picture, size: 50, sizeCircle: 50, ), diff --git a/lib/src/presentation/widgets/shared/notificacion_snackbar.dart b/lib/src/presentation/widgets/shared/notificacion_snackbar.dart index 4262954..546d401 100644 --- a/lib/src/presentation/widgets/shared/notificacion_snackbar.dart +++ b/lib/src/presentation/widgets/shared/notificacion_snackbar.dart @@ -1,51 +1,14 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; +import 'package:prosappco/src/utils/app_navigator.dart'; class NotificationSnackbar { 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), - ), - ); + showAppSnackBar(title, message, color: backgroundColor); } } diff --git a/lib/src/presentation/widgets/shared/warning_snackbar.dart b/lib/src/presentation/widgets/shared/warning_snackbar.dart index e8028fe..728bb49 100644 --- a/lib/src/presentation/widgets/shared/warning_snackbar.dart +++ b/lib/src/presentation/widgets/shared/warning_snackbar.dart @@ -1,51 +1,14 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; +import 'package:prosappco/src/utils/app_navigator.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), - ), - ); + showAppSnackBar(title, message, color: backgroundColor); } } diff --git a/lib/src/providers/user_provider.dart b/lib/src/providers/user_provider.dart index d25ca39..bbd185f 100644 --- a/lib/src/providers/user_provider.dart +++ b/lib/src/providers/user_provider.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:prosappco/src/models/scores_model.dart'; import 'package:prosappco/src/models/user_model.dart'; import 'package:prosappco/src/services/api_service.dart'; class UserProvider extends ChangeNotifier { final _api = ApiService.instance; UserModel? _user; + ScoresModel? _score; UserProvider() { loadUser(); @@ -15,7 +17,8 @@ class UserProvider extends ChangeNotifier { if (token == null) return; try { final data = await _api.get('/auth/me'); - _user = UserModel.fromApi(data); + _user = UserModel.fromApi(data as Map); + _score = await ScoresModel.scoreTo(_user?.id, false, false); notifyListeners(); } catch (_) {} } @@ -25,8 +28,10 @@ class UserProvider extends ChangeNotifier { void setNullUser() { _user = null; + _score = null; notifyListeners(); } UserModel? get user => _user; + ScoresModel? get score => _score; } diff --git a/lib/src/services/api_service.dart b/lib/src/services/api_service.dart index 9f5e570..5b39536 100644 --- a/lib/src/services/api_service.dart +++ b/lib/src/services/api_service.dart @@ -15,8 +15,11 @@ class ApiException implements Exception { class ApiService { static ApiService? _instance; static ApiService get instance => _instance ??= ApiService._(); + static const baseUrl = _baseUrl; ApiService._(); + Map parseJson(String body) => jsonDecode(body) as Map; + String? _token; Future getToken() async { diff --git a/lib/src/utils/app_navigator.dart b/lib/src/utils/app_navigator.dart new file mode 100644 index 0000000..812be99 --- /dev/null +++ b/lib/src/utils/app_navigator.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; + +final appNavigatorKey = GlobalKey(); + +void pushOffAll(Widget screen) { + appNavigatorKey.currentState?.pushAndRemoveUntil( + MaterialPageRoute(builder: (_) => screen), + (_) => false, + ); +} + +void push(Widget screen) { + appNavigatorKey.currentState?.push( + MaterialPageRoute(builder: (_) => screen), + ); +} + +void back() => appNavigatorKey.currentState?.pop(); + +void showAppSnackBar(String title, String message, {Color color = Colors.red}) { + final ctx = appNavigatorKey.currentContext; + if (ctx == null) return; + ScaffoldMessenger.of(ctx).showSnackBar(SnackBar( + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title, style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white)), + Text(message, style: TextStyle(color: Colors.white)), + ], + ), + backgroundColor: color, + duration: const Duration(seconds: 4), + behavior: SnackBarBehavior.floating, + )); +} + +Future showAppDialog(AlertDialog dialog) { + final ctx = appNavigatorKey.currentContext!; + return showDialog(context: ctx, builder: (_) => dialog); +} diff --git a/pubspec.yaml b/pubspec.yaml index 487da30..8d31638 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,23 +37,22 @@ dependencies: cupertino_icons: ^1.0.2 location: ^5.0.3 flutter_polyline_points: ^2.0.0 - google_maps_flutter: ^2.5.0 + google_maps_flutter: ^2.9.0 file_picker: ^5.3.2 provider: ^6.0.5 package_info_plus: ^4.2.0 - get: - font_awesome_flutter: ^10.4.0 + font_awesome_flutter: ^10.8.0 flutter_otp_text_field: intl_phone_field: diacritic: - image_picker: ^1.0.4 + image_picker: ^1.1.2 flutter_animate: flutter_rating_bar: table_calendar: http: ^1.1.0 geolocator: ^9.0.2 geocoding: ^2.1.0 - url_launcher: ^6.1.10 + url_launcher: ^6.3.0 community_material_icon: ^5.9.55 webview_flutter: ^4.4.1 responsive_builder: ^0.7.0