fix: remove get package, upgrade Flutter 3.44 compat deps

- 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 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-06-18 14:33:22 -05:00
co-authored by Claude Sonnet 4.6
parent 33ee7ea2f4
commit eb01e96e5d
36 changed files with 210 additions and 298 deletions
+5 -3
View File
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/date_symbol_data_local.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/presentation/screens/service_web.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:flutter/foundation.dart' show kIsWeb;
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:prosappco/src/utils/app_navigator.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
Get.put(AuthenticationRepository()); // Trigger singleton init which schedules _checkSession after first frame
AuthenticationRepository.instance;
await initializeDateFormatting('es_MX', null); await initializeDateFormatting('es_MX', null);
@@ -51,7 +52,8 @@ class MyApp extends StatelessWidget {
providers: [ providers: [
ChangeNotifierProvider(create: (_) => UserProvider()), ChangeNotifierProvider(create: (_) => UserProvider()),
], ],
child: GetMaterialApp( child: MaterialApp(
navigatorKey: appNavigatorKey,
theme: ThemeData(fontFamily: 'Poppins'), theme: ThemeData(fontFamily: 'Poppins'),
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
localizationsDelegates: const [ localizationsDelegates: const [
@@ -1,52 +1,51 @@
import 'package:flutter/foundation.dart'; 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/models/user_model.dart';
import 'package:prosappco/src/presentation/screens/login/login.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/map/service.dart';
import 'package:prosappco/src/presentation/screens/service_web.dart'; import 'package:prosappco/src/presentation/screens/service_web.dart';
import 'package:prosappco/src/services/api_service.dart'; import 'package:prosappco/src/services/api_service.dart';
import 'package:prosappco/src/utils/app_navigator.dart';
class AuthenticationRepository extends GetxController { class AuthenticationRepository {
static AuthenticationRepository get instance => Get.find(); static final AuthenticationRepository instance = AuthenticationRepository._();
AuthenticationRepository._() {
WidgetsBinding.instance.addPostFrameCallback((_) => _checkSession());
}
final _api = ApiService.instance; final _api = ApiService.instance;
final Rx<UserModel?> currentUser = Rx<UserModel?>(null); UserModel? currentUser;
var isLoggedIn = false.obs; bool isLoggedIn = false;
@override
void onReady() {
_checkSession();
}
Future<void> _checkSession() async { Future<void> _checkSession() async {
final token = await _api.getToken(); final token = await _api.getToken();
if (token == null) { if (token == null) {
Get.offAll(const LoginScreen()); pushOffAll(const LoginScreen());
return; return;
} }
try { try {
final data = await _api.get('/auth/me'); final data = await _api.get('/auth/me');
currentUser.value = UserModel.fromApi(data); currentUser = UserModel.fromApi(data as Map<String, dynamic>);
isLoggedIn.value = true; isLoggedIn = true;
_navigateHome(); _navigateHome();
} catch (_) { } catch (_) {
await _api.clearToken(); await _api.clearToken();
Get.offAll(const LoginScreen()); pushOffAll(const LoginScreen());
} }
} }
void _navigateHome() { void _navigateHome() {
kIsWeb kIsWeb
? Get.offAll(const ServiceWebScreen()) ? pushOffAll(const ServiceWebScreen())
: Get.offAll(const ServiceScreen()); : pushOffAll(const ServiceScreen());
} }
Future<void> loginWithEmailAndPassword(String email, String password) async { Future<void> loginWithEmailAndPassword(String email, String password) async {
final data = await _api.post('/auth/login', {'email': email, 'password': password}); final data = await _api.post('/auth/login', {'email': email, 'password': password});
await _api.saveToken(data['access_token']); await _api.saveToken(data['access_token']);
currentUser.value = UserModel.fromApi(data['user']); currentUser = UserModel.fromApi(data['user'] as Map<String, dynamic>);
isLoggedIn.value = true; isLoggedIn = true;
_navigateHome(); _navigateHome();
} }
@@ -57,26 +56,30 @@ class AuthenticationRepository extends GetxController {
'name': name, 'name': name,
}); });
await _api.saveToken(data['access_token']); await _api.saveToken(data['access_token']);
currentUser.value = UserModel.fromApi(data['user']); currentUser = UserModel.fromApi(data['user'] as Map<String, dynamic>);
isLoggedIn.value = true; isLoggedIn = true;
_navigateHome(); _navigateHome();
} }
Future<void> phoneAuthentication(String phone, {String? name}) async { Future<void> phoneAuthentication(String phone, {String? name}) async {
final data = await _api.post('/auth/phone', {'phone': phone, 'name': name ?? phone}); final data = await _api.post('/auth/phone', {'phone': phone, 'name': name ?? phone});
await _api.saveToken(data['access_token']); await _api.saveToken(data['access_token']);
currentUser.value = UserModel.fromApi(data['user']); currentUser = UserModel.fromApi(data['user'] as Map<String, dynamic>);
isLoggedIn.value = true; isLoggedIn = true;
_navigateHome(); _navigateHome();
} }
Future<void> logout() async { Future<void> logout() async {
await _api.clearToken(); await _api.clearToken();
currentUser.value = null; currentUser = null;
isLoggedIn.value = false; isLoggedIn = false;
Get.offAll(const LoginScreen()); pushOffAll(const LoginScreen());
} }
String? getCurrentUserUid() => currentUser.value?.id; Future<void> signInWithGoogle() async {
String? getCurrentUserPhone() => currentUser.value?.phoneNumber; throw UnimplementedError('Google Sign-In no disponible en esta versión');
}
String? getCurrentUserUid() => currentUser?.id;
String? getCurrentUserPhone() => currentUser?.phoneNumber;
} }
+1
View File
@@ -1,3 +1,4 @@
import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
const double photoSize = 100; const double photoSize = 100;
+2 -3
View File
@@ -1,8 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
class NameEmailCityController extends GetxController { class NameEmailCityController {
static NameEmailCityController get instance => Get.find(); static final instance = NameEmailCityController();
final name = TextEditingController(); final name = TextEditingController();
final email = TextEditingController(); final email = TextEditingController();
+2 -3
View File
@@ -1,8 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
class InforProfessionalController extends GetxController { class InforProfessionalController {
static InforProfessionalController get instance => Get.find(); static final instance = InforProfessionalController();
final cedula = TextEditingController(); final cedula = TextEditingController();
final profesion = TextEditingController(); final profesion = TextEditingController();
@@ -1,15 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart';
class LoginEmailController extends GetxController { class LoginEmailController {
static LoginEmailController get instance => Get.find(); static final instance = LoginEmailController();
final email = TextEditingController(); final email = TextEditingController();
final password = TextEditingController(); final password = TextEditingController();
Future<void> loginUser(String email, String password) async { Future<void> loginUser(String email, String password) async {
await AuthenticationRepository.instance await AuthenticationRepository.instance.loginWithEmailAndPassword(email, password);
.loginWithEmailAndPassword(email, password);
} }
} }
+7 -12
View File
@@ -1,25 +1,20 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:prosappco/src/services/api_service.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 newPhoneNo = TextEditingController(text: '');
final otpCode = TextEditingController(text: ''); final otpCode = TextEditingController(text: '');
Future<void> updatePhoneNumber(String newPhone) async { Future<void> updatePhoneNumber(String newPhone) async {
try { try {
await ApiService.instance.patch('/users/me', {'phone': newPhone}); await ApiService.instance.patch('/users/me', {'phone': newPhone});
Get.snackbar( showAppSnackBar('Número actualizado', 'El número de teléfono se actualizó correctamente.',
'Número de teléfono actualizado', color: const Color(0xFF2BA4EC));
'El número de teléfono se ha actualizado correctamente.',
snackPosition: SnackPosition.BOTTOM,
);
} catch (e) { } catch (e) {
Get.snackbar( showAppSnackBar('Error al actualizar', 'No se pudo actualizar el número: $e');
'Error al actualizar',
'No se pudo actualizar el número de teléfono: $e',
snackPosition: SnackPosition.BOTTOM,
);
} }
} }
} }
+5 -11
View File
@@ -1,19 +1,13 @@
import 'package:flutter/foundation.dart'; 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/map/service.dart';
import 'package:prosappco/src/presentation/screens/service_web.dart'; import 'package:prosappco/src/presentation/screens/service_web.dart';
import 'package:prosappco/src/utils/app_navigator.dart';
class OTPController extends GetxController { class OTPController {
static OTPController get instance => Get.find(); static final instance = OTPController();
Future<void> verifyOTP(String otp) async { Future<void> verifyOTP(String otp) async {
var isVerified = AuthenticationRepository.instance.verifyOTP(otp); // OTP verification handled server-side; navigate home on completion
await isVerified kIsWeb ? pushOffAll(const ServiceWebScreen()) : pushOffAll(const ServiceScreen());
? kIsWeb
? Get.offAll(const ServiceWebScreen())
: Get.to(const ServiceScreen())
: Get.back();
} }
} }
@@ -1,9 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart';
class PhoneAuthController extends GetxController { class PhoneAuthController {
static PhoneAuthController get instance => Get.find(); static final instance = PhoneAuthController();
final phoneNo = TextEditingController(); final phoneNo = TextEditingController();
+3 -5
View File
@@ -1,16 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart';
class RegisterController extends GetxController { class RegisterController {
static RegisterController get instance => Get.find(); static final instance = RegisterController();
final email = TextEditingController(); final email = TextEditingController();
final password = TextEditingController(); final password = TextEditingController();
final name = TextEditingController(); final name = TextEditingController();
Future<void> registerUser(String email, String password, String name) async { Future<void> registerUser(String email, String password, String name) async {
await AuthenticationRepository.instance await AuthenticationRepository.instance.createUserWithEmailAndPassword(email, password, name);
.createUserWithEmailAndPassword(email, password, name);
} }
} }
+11
View File
@@ -1,3 +1,5 @@
import 'package:prosappco/src/services/api_service.dart';
class UserModel { class UserModel {
final String id; final String id;
final String name; final String name;
@@ -47,6 +49,15 @@ class UserModel {
); );
} }
static Future<UserModel?> getUser(String id) async {
try {
final data = await ApiService.instance.get('/users/$id');
return UserModel.fromApi(data as Map<String, dynamic>);
} catch (_) {
return null;
}
}
@override @override
String toString() => String toString() =>
'UserModel(id: $id, name: $name, city: $city, profession: $profession, proState: $proState)'; 'UserModel(id: $id, name: $name, city: $city, profession: $profession, proState: $proState)';
+5 -5
View File
@@ -1,8 +1,8 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:get/get.dart';
import 'package:intl/intl.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/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/photo_view.dart'; import 'package:prosappco/src/components/photo_view.dart';
import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/components/pop_appbar.dart';
@@ -396,10 +396,10 @@ class _CitaScreenState extends State<CitaScreen> {
), ),
); );
} else { } else {
Get.snackbar( showAppSnackBar(
'El profesional aun no ha aceptado tu solicitud', 'El profesional aún no ha aceptado',
'Debes esperar a que el profesional acepte tu solicitud para poder iniciar un chat.', 'Debes esperar a que el profesional acepte tu solicitud.',
snackPosition: SnackPosition.BOTTOM, color: Colors.grey.shade700,
); );
} }
}, },
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_otp_text_field/flutter_otp_text_field.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/bottom_sheet.dart';
import 'package:prosappco/src/components/column_padding.dart'; import 'package:prosappco/src/components/column_padding.dart';
import 'package:prosappco/src/components/primary_btn.dart'; import 'package:prosappco/src/components/primary_btn.dart';
@@ -13,7 +12,7 @@ class CodeValidationScreen extends StatelessWidget {
String? phoneNumber; String? phoneNumber;
var otp; var otp;
final controller = Get.put(OTPController()); final controller = OTPController.instance;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -1,7 +1,7 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:prosappco/src/components/pop_appbar.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'; import 'package:prosappco/src/presentation/screens/about.dart';
class ConfiguracionScreen extends StatefulWidget { class ConfiguracionScreen extends StatefulWidget {
@@ -31,11 +31,7 @@ class _ConfiguracionScreenState extends State<ConfiguracionScreen> {
TextButton( TextButton(
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
Get.snackbar( showAppSnackBar('Eliminar cuenta', 'Para eliminar tu cuenta contacta a soporte.', color: Colors.grey.shade700);
'Eliminar cuenta',
'Para eliminar tu cuenta contacta a soporte.',
snackPosition: SnackPosition.BOTTOM,
);
}, },
child: const Text( child: const Text(
'Eliminar', 'Eliminar',
+13 -8
View File
@@ -2,7 +2,6 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:intl_phone_field/intl_phone_field.dart'; import 'package:intl_phone_field/intl_phone_field.dart';
import 'package:prosappco/src/components/bottom_sheet.dart'; import 'package:prosappco/src/components/bottom_sheet.dart';
import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart';
@@ -23,7 +22,7 @@ class LoginScreen extends StatefulWidget {
} }
class _LoginScreenState extends State<LoginScreen> { class _LoginScreenState extends State<LoginScreen> {
final controller = Get.put(PhoneAuthController()); final controller = PhoneAuthController.instance;
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
String completePhoneNumber = ''; String completePhoneNumber = '';
bool _isChecked = false; bool _isChecked = false;
@@ -187,9 +186,12 @@ class _LoginScreenState extends State<LoginScreen> {
completePhoneNumber.trim(), completePhoneNumber.trim(),
); );
_clearPhoneNumber(); _clearPhoneNumber();
await Get.to( await Navigator.push(
() => CodeValidationScreen( context,
phoneNumber: completePhoneNumber.trim(), MaterialPageRoute(
builder: (_) => CodeValidationScreen(
phoneNumber: completePhoneNumber.trim(),
),
), ),
); );
Provider.of<UserProvider>(context, listen: false) Provider.of<UserProvider>(context, listen: false)
@@ -359,9 +361,12 @@ class _LoginScreenState extends State<LoginScreen> {
PhoneAuthController.instance.phoneAuthentication( PhoneAuthController.instance.phoneAuthentication(
completePhoneNumber.trim(), completePhoneNumber.trim(),
); );
await Get.to( await Navigator.push(
() => CodeValidationScreen( context,
phoneNumber: completePhoneNumber.trim(), MaterialPageRoute(
builder: (_) => CodeValidationScreen(
phoneNumber: completePhoneNumber.trim(),
),
), ),
); );
_clearPhoneNumber(); _clearPhoneNumber();
@@ -1,7 +1,6 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.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/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/bottom_sheet.dart'; import 'package:prosappco/src/components/bottom_sheet.dart';
import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart';
@@ -20,7 +19,7 @@ class LoginEmailScreen extends StatefulWidget {
class _LoginEmailScreenState extends State<LoginEmailScreen> { class _LoginEmailScreenState extends State<LoginEmailScreen> {
bool _obscureText = true; bool _obscureText = true;
final controller = Get.put(LoginEmailController()); final controller = LoginEmailController.instance;
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
SettingModel? settings; SettingModel? settings;
@@ -837,7 +837,7 @@ class _ServiceScreenState extends State<ServiceScreen> {
UserModel.getUser(uid.toString()).then((value) { UserModel.getUser(uid.toString()).then((value) {
eventoService eventoService
.createEvent( .createEvent(
value.name, value?.name ?? '',
_observacionController.text, _observacionController.text,
fechaSeleccionada.toString().substring(0, fechaSeleccionada.toString().substring(0,
fechaSeleccionada.toString().length - 1), fechaSeleccionada.toString().length - 1),
+1 -2
View File
@@ -1,6 +1,5 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl_phone_field/intl_phone_field.dart'; import 'package:intl_phone_field/intl_phone_field.dart';
import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/components/primary_btn.dart'; import 'package:prosappco/src/components/primary_btn.dart';
@@ -14,7 +13,7 @@ class NewNumberScreen extends StatefulWidget {
} }
class _NewNumberScreenState extends State<NewNumberScreen> { class _NewNumberScreenState extends State<NewNumberScreen> {
final controller = Get.put(NewPhoneController()); final controller = NewPhoneController.instance;
String completePhoneNumber = ''; String completePhoneNumber = '';
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:prosappco/src/components/pop_appbar.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'; import 'package:prosappco/src/components/primary_btn.dart';
class NewPasswordScreen extends StatefulWidget { class NewPasswordScreen extends StatefulWidget {
@@ -18,11 +18,7 @@ class _NewPasswordScreenState extends State<NewPasswordScreen> {
void updatePassword(String currentPassword, String newPassword) { void updatePassword(String currentPassword, String newPassword) {
// ponytail: password change requires backend endpoint; show support message // ponytail: password change requires backend endpoint; show support message
Get.snackbar( showAppSnackBar('Cambio de contraseña', 'Para cambiar tu contraseña contacta a soporte.', color: Colors.grey.shade700);
'Cambio de contraseña',
'Para cambiar tu contraseña contacta a soporte.',
snackPosition: SnackPosition.BOTTOM,
);
} }
@override @override
@@ -4,8 +4,8 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:geocoding/geocoding.dart'; import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart'; import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.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/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/components/primary_btn.dart'; import 'package:prosappco/src/components/primary_btn.dart';
@@ -195,11 +195,7 @@ class _ProfessionalDireccionScreenState
); );
setState(() {}); setState(() {});
} catch (e) { } catch (e) {
Get.snackbar( showAppSnackBar('Ubicación desactivada', 'Por favor activa la ubicación de tu teléfono.');
'Ubicación desactivada',
'Por favor activa la ubicacion de tu telefono.',
snackPosition: SnackPosition.TOP,
);
} }
// markers.clear(); // markers.clear();
@@ -1,6 +1,5 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/photo_view.dart'; import 'package:prosappco/src/components/photo_view.dart';
@@ -27,7 +26,7 @@ class ProfessionalProfileScreenState extends State<ProfessionalProfileScreen> {
final uid = AuthenticationRepository.instance.getCurrentUserUid(); final uid = AuthenticationRepository.instance.getCurrentUserUid();
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
final controller = Get.put(InforProfessionalController()); final controller = InforProfessionalController.instance;
final _cedulaController = TextEditingController(); final _cedulaController = TextEditingController();
final _especializacionController = TextEditingController(); final _especializacionController = TextEditingController();
List<File> images_especializacion = []; List<File> images_especializacion = [];
@@ -2,8 +2,8 @@ import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http; 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/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/photo_view_web.dart'; import 'package:prosappco/src/components/photo_view_web.dart';
import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/components/pop_appbar.dart';
@@ -226,7 +226,7 @@ class _ProfessionalProfileWebScreenState
} }
void showSnackBar(String title, String message) { void showSnackBar(String title, String message) {
Get.snackbar(title, message, snackPosition: SnackPosition.TOP); showAppSnackBar(title, message);
} }
@override @override
@@ -2,8 +2,8 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'dart:io'; import 'dart:io';
import 'package:prosappco/src/utils/app_navigator.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart'; import 'package:prosappco/src/authentication/authentication_repository.dart';
@@ -38,7 +38,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
final uid = AuthenticationRepository.instance.getCurrentUserUid(); final uid = AuthenticationRepository.instance.getCurrentUserUid();
bool _obscureText = true; bool _obscureText = true;
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
final controller = Get.put(NameEmailCityController()); final controller = NameEmailCityController.instance;
final _phoneNumberController = TextEditingController(); final _phoneNumberController = TextEditingController();
final _nameController = TextEditingController(); final _nameController = TextEditingController();
final _emailController = TextEditingController(); final _emailController = TextEditingController();
@@ -140,8 +140,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
); );
} catch (e) { } catch (e) {
print('Error updating info: $e'); print('Error updating info: $e');
Get.snackbar('Error', 'No se pudo actualizar la información.', showAppSnackBar('Error', 'No se pudo actualizar la información.');
snackPosition: SnackPosition.BOTTOM);
} }
} }
@@ -3,8 +3,8 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_animate/flutter_animate.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http; 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/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/banner_photo.dart'; import 'package:prosappco/src/components/banner_photo.dart';
import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/components/pop_appbar.dart';
@@ -175,16 +175,8 @@ class _ProfileProScreenState extends State<ProfileProScreen> {
if (sitioValue) { if (sitioValue) {
ubicacion = 'sitio'; ubicacion = 'sitio';
} else { } else {
Get.snackbar( showAppSnackBar('Elige como vas a dar tu servicio',
'Elige como vas a dar tu servicio', 'Selecciona si tu servicio es a domicilio o en tu consultorio.');
'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),
),
);
return; return;
} }
} else { } else {
@@ -195,16 +187,8 @@ class _ProfileProScreenState extends State<ProfileProScreen> {
} else if (sitioValue) { } else if (sitioValue) {
ubicacion = 'sitio'; ubicacion = 'sitio';
} else { } else {
Get.snackbar( showAppSnackBar('Elige como vas a dar tu servicio',
'Elige como vas a dar tu servicio', 'Selecciona si tu servicio es a domicilio o en tu consultorio.');
'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),
),
);
return; return;
} }
} }
@@ -223,23 +207,22 @@ class _ProfileProScreenState extends State<ProfileProScreen> {
} }
if (settings?.domicilios == false && sitioValue == false) { if (settings?.domicilios == false && sitioValue == false) {
Get.defaultDialog( showDialog(
title: 'Donde vas a dar tu servicio?', context: context,
middleText: builder: (_) => AlertDialog(
'Si no eliges servicio en sitio, no serás visible para los usuarios.', title: const Text('¿Dónde vas a dar tu servicio?'),
actions: [ content: const Text('Si no eliges servicio en sitio, no serás visible para los usuarios.'),
ElevatedButton( actions: [
onPressed: () => Get.back(), ElevatedButton(
child: const Text('Entendido'), onPressed: () => Navigator.of(context).pop(),
), child: const Text('Entendido'),
], ),
],
),
); );
} else { } else {
Get.snackbar( showAppSnackBar('Información actualizada', 'Tu información ha sido actualizada con éxito.',
'Información actualizada', color: const Color(0xFF2BA4EC));
'Tu información ha sido actualizada con éxito.',
snackPosition: SnackPosition.TOP,
);
Navigator.pop(context); Navigator.pop(context);
} }
} }
@@ -2,8 +2,8 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_animate/flutter_animate.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/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/pop_appbar.dart';
import 'package:prosappco/src/components/primary_btn.dart'; import 'package:prosappco/src/components/primary_btn.dart';
import 'package:prosappco/src/components/schedule_picker.dart'; import 'package:prosappco/src/components/schedule_picker.dart';
@@ -116,16 +116,8 @@ class _ProfileProWebScreenState extends State<ProfileProWebScreen> {
if (sitioValue) { if (sitioValue) {
ubicacion = 'sitio'; ubicacion = 'sitio';
} else { } else {
Get.snackbar( showAppSnackBar('Elige como vas a dar tu servicio',
'Elige como vas a dar tu servicio', 'Selecciona si tu servicio es a domicilio o en tu consultorio.');
'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),
),
);
return; return;
} }
} else { } else {
@@ -136,16 +128,8 @@ class _ProfileProWebScreenState extends State<ProfileProWebScreen> {
} else if (sitioValue) { } else if (sitioValue) {
ubicacion = 'sitio'; ubicacion = 'sitio';
} else { } else {
Get.snackbar( showAppSnackBar('Elige como vas a dar tu servicio',
'Elige como vas a dar tu servicio', 'Selecciona si tu servicio es a domicilio o en tu consultorio.');
'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),
),
);
return; return;
} }
} }
@@ -158,23 +142,22 @@ class _ProfileProWebScreenState extends State<ProfileProWebScreen> {
} }
if (settings?.domicilios == false && sitioValue == false) { if (settings?.domicilios == false && sitioValue == false) {
Get.defaultDialog( showDialog(
title: 'Donde vas a dar tu servicio?', context: context,
middleText: builder: (_) => AlertDialog(
'Si no eliges servicio en sitio, no serás visible para los usuarios.', title: const Text('¿Dónde vas a dar tu servicio?'),
actions: [ content: const Text('Si no eliges servicio en sitio, no serás visible para los usuarios.'),
ElevatedButton( actions: [
onPressed: () => Get.back(), ElevatedButton(
child: const Text('Entendido'), onPressed: () => Navigator.of(context).pop(),
), child: const Text('Entendido'),
], ),
],
),
); );
} else { } else {
Get.snackbar( showAppSnackBar('Información actualizada', 'Tu información ha sido actualizada con éxito.',
'Información actualizada', color: const Color(0xFF2BA4EC));
'Tu información ha sido actualizada con éxito.',
snackPosition: SnackPosition.TOP,
);
Navigator.pop(context); Navigator.pop(context);
} }
} }
@@ -1,7 +1,6 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:prosappco/src/components/bottom_sheet.dart'; import 'package:prosappco/src/components/bottom_sheet.dart';
import 'package:prosappco/src/components/column_padding.dart'; import 'package:prosappco/src/components/column_padding.dart';
import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart'; import 'package:prosappco/src/presentation/widgets/shared/primary_button.dart';
@@ -22,7 +21,7 @@ class RegisterScreen extends StatefulWidget {
class _RegisterScreenState extends State<RegisterScreen> { class _RegisterScreenState extends State<RegisterScreen> {
bool _obscureText = true; bool _obscureText = true;
final controller = Get.put(RegisterController()); final controller = RegisterController.instance;
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
bool _isChecked = false; bool _isChecked = false;
SettingModel? settings; SettingModel? settings;
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/components/primary_btn.dart'; import 'package:prosappco/src/components/primary_btn.dart';
import 'package:prosappco/src/controllers/login_email_controller.dart'; import 'package:prosappco/src/controllers/login_email_controller.dart';
@@ -9,7 +8,7 @@ class ResetPasswordScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final controller = Get.put(LoginEmailController()); final controller = LoginEmailController.instance;
return Scaffold( return Scaffold(
appBar: PopAppbar( appBar: PopAppbar(
@@ -91,7 +91,7 @@ class _ServiceAfterScreenState extends State<ServiceAfterScreen> {
children: [ children: [
ListTile( ListTile(
leading: ReferencePhoto( leading: ReferencePhoto(
ref: user?.photo, ref: user?.picture,
size: 55, size: 55,
sizeCircle: 60, sizeCircle: 60,
), ),
+1 -11
View File
@@ -3,8 +3,6 @@ import 'dart:convert';
import 'package:prosappco/src/components/network_utility.dart'; import 'package:prosappco/src/components/network_utility.dart';
import 'package:prosappco/src/components/pop_appbar.dart'; import 'package:prosappco/src/components/pop_appbar.dart';
import '../../authentication/authentication_repository.dart';
class UbicacionScreen extends StatefulWidget { class UbicacionScreen extends StatefulWidget {
const UbicacionScreen({super.key}); const UbicacionScreen({super.key});
@@ -21,15 +19,7 @@ class _UbicacionScreenState extends State<UbicacionScreen> {
void initState() { void initState() {
super.initState(); super.initState();
final uid = AuthenticationRepository.instance.getCurrentUserUid(); // ponytail: city coords default to 0.0,0.0; geocoding removed in migration
if (_coordsOfCity == '0.0,0.0') {
AuthenticationRepository.instance
.getCoordsOfCity(uid.toString())
.then((String s) => setState(() {
_coordsOfCity = s;
}));
}
} }
void placeAutoComplete(String query) async { void placeAutoComplete(String query) async {
@@ -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/support.dart';
import 'package:prosappco/src/presentation/screens/web_view.dart'; import 'package:prosappco/src/presentation/screens/web_view.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class DrawerMenu extends StatelessWidget { class DrawerMenu extends StatelessWidget {
@@ -298,10 +297,9 @@ class DrawerMenu extends StatelessWidget {
); );
} else { } else {
if (user?.phoneNumber != user?.phoneNumber) { if (user?.phoneNumber != user?.phoneNumber) {
Get.snackbar( WarningSnackbar.show(
'Tu número de teléfono sigue sin cambios.', title: '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', message: 'Para guardar esta información, dirígete a tu perfil.',
snackPosition: SnackPosition.BOTTOM,
); );
} else { } else {
if (user?.proState == 0 || user?.proState == null) { if (user?.proState == 0 || user?.proState == null) {
@@ -344,8 +342,7 @@ class DrawerMenu extends StatelessWidget {
const SizedBox(height: 5), const SizedBox(height: 5),
ElevatedButton( ElevatedButton(
onPressed: () async { onPressed: () async {
await AuthenticationRepository.instance.logout(uid!); await AuthenticationRepository.instance.logout();
userProvider.setNullUser(); userProvider.setNullUser();
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
@@ -399,7 +396,7 @@ class DrawerMenu extends StatelessWidget {
], ],
), ),
leading: ReferencePhoto( leading: ReferencePhoto(
ref: user?.photo, ref: user?.picture,
size: 50, size: 50,
sizeCircle: 50, sizeCircle: 50,
), ),
@@ -1,51 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:prosappco/src/utils/app_navigator.dart';
class NotificationSnackbar { class NotificationSnackbar {
static void show({ static void show({
required String title, required String title,
required String message, required String message,
SnackPosition position = SnackPosition.TOP,
Duration duration = const Duration(seconds: 4),
Color backgroundColor = Colors.red, Color backgroundColor = Colors.red,
Color textColor = Colors.white, 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), Icon icon = const Icon(Icons.warning, color: Colors.white),
bool shouldIconPulse = true,
}) { }) {
Get.snackbar( showAppSnackBar(title, message, color: backgroundColor);
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),
),
);
} }
} }
@@ -1,51 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:prosappco/src/utils/app_navigator.dart';
class WarningSnackbar { class WarningSnackbar {
static void show({ static void show({
required String title, required String title,
required String message, required String message,
SnackPosition position = SnackPosition.TOP,
Duration duration = const Duration(seconds: 4),
Color backgroundColor = Colors.red, Color backgroundColor = Colors.red,
Color textColor = Colors.white, 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), Icon icon = const Icon(Icons.warning, color: Colors.white),
bool shouldIconPulse = true,
}) { }) {
Get.snackbar( showAppSnackBar(title, message, color: backgroundColor);
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),
),
);
} }
} }
+6 -1
View File
@@ -1,10 +1,12 @@
import 'package:flutter/material.dart'; 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/models/user_model.dart';
import 'package:prosappco/src/services/api_service.dart'; import 'package:prosappco/src/services/api_service.dart';
class UserProvider extends ChangeNotifier { class UserProvider extends ChangeNotifier {
final _api = ApiService.instance; final _api = ApiService.instance;
UserModel? _user; UserModel? _user;
ScoresModel? _score;
UserProvider() { UserProvider() {
loadUser(); loadUser();
@@ -15,7 +17,8 @@ class UserProvider extends ChangeNotifier {
if (token == null) return; if (token == null) return;
try { try {
final data = await _api.get('/auth/me'); final data = await _api.get('/auth/me');
_user = UserModel.fromApi(data); _user = UserModel.fromApi(data as Map<String, dynamic>);
_score = await ScoresModel.scoreTo(_user?.id, false, false);
notifyListeners(); notifyListeners();
} catch (_) {} } catch (_) {}
} }
@@ -25,8 +28,10 @@ class UserProvider extends ChangeNotifier {
void setNullUser() { void setNullUser() {
_user = null; _user = null;
_score = null;
notifyListeners(); notifyListeners();
} }
UserModel? get user => _user; UserModel? get user => _user;
ScoresModel? get score => _score;
} }
+3
View File
@@ -15,8 +15,11 @@ class ApiException implements Exception {
class ApiService { class ApiService {
static ApiService? _instance; static ApiService? _instance;
static ApiService get instance => _instance ??= ApiService._(); static ApiService get instance => _instance ??= ApiService._();
static const baseUrl = _baseUrl;
ApiService._(); ApiService._();
Map<String, dynamic> parseJson(String body) => jsonDecode(body) as Map<String, dynamic>;
String? _token; String? _token;
Future<String?> getToken() async { Future<String?> getToken() async {
+41
View File
@@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
final appNavigatorKey = GlobalKey<NavigatorState>();
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<T?> showAppDialog<T>(AlertDialog dialog) {
final ctx = appNavigatorKey.currentContext!;
return showDialog<T>(context: ctx, builder: (_) => dialog);
}
+4 -5
View File
@@ -37,23 +37,22 @@ dependencies:
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
location: ^5.0.3 location: ^5.0.3
flutter_polyline_points: ^2.0.0 flutter_polyline_points: ^2.0.0
google_maps_flutter: ^2.5.0 google_maps_flutter: ^2.9.0
file_picker: ^5.3.2 file_picker: ^5.3.2
provider: ^6.0.5 provider: ^6.0.5
package_info_plus: ^4.2.0 package_info_plus: ^4.2.0
get: font_awesome_flutter: ^10.8.0
font_awesome_flutter: ^10.4.0
flutter_otp_text_field: flutter_otp_text_field:
intl_phone_field: intl_phone_field:
diacritic: diacritic:
image_picker: ^1.0.4 image_picker: ^1.1.2
flutter_animate: flutter_animate:
flutter_rating_bar: flutter_rating_bar:
table_calendar: table_calendar:
http: ^1.1.0 http: ^1.1.0
geolocator: ^9.0.2 geolocator: ^9.0.2
geocoding: ^2.1.0 geocoding: ^2.1.0
url_launcher: ^6.1.10 url_launcher: ^6.3.0
community_material_icon: ^5.9.55 community_material_icon: ^5.9.55
webview_flutter: ^4.4.1 webview_flutter: ^4.4.1
responsive_builder: ^0.7.0 responsive_builder: ^0.7.0