This commit is contained in:
lizandrogd
2023-09-15 15:52:59 -05:00
43 changed files with 1692 additions and 1357 deletions
+1 -1
View File
@@ -46,7 +46,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.prosappco"
applicationId "com.prosapp.prosapp"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion "21".toInteger()
+5 -5
View File
@@ -8,18 +8,18 @@
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:245204384533:android:12920d0fe3ec9b999f871c",
"mobilesdk_app_id": "1:245204384533:android:e01772831d86f5c79f871c",
"android_client_info": {
"package_name": "com.example.prosappco"
"package_name": "com.prosapp.prosapp"
}
},
"oauth_client": [
{
"client_id": "245204384533-7p2j0go97da4gp062s7enop0ab8q7a7p.apps.googleusercontent.com",
"client_id": "245204384533-23bm14294ctm728nd9u83eiapsjfebhu.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.example.prosappco",
"certificate_hash": "e0e88a66931385d2626f8531e7cbbb5722df7b4b"
"package_name": "com.prosapp.prosapp",
"certificate_hash": "29ac5d7d1b88c755a084a0ad5d561dd62d795eb5"
}
},
{
+4
View File
@@ -14,6 +14,7 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
ADF3D9B31D73179ECCE7D3BB /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 47D29BFFEE062A72D9E6E12A /* GoogleService-Info.plist */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -33,6 +34,7 @@
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
47D29BFFEE062A72D9E6E12A /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
@@ -88,6 +90,7 @@
97C146EF1CF9000F007C117D /* Products */,
E5C876AEF0D01DE572D83FC4 /* Pods */,
5F3EBDC617D1ACB18B4906E0 /* Frameworks */,
47D29BFFEE062A72D9E6E12A /* GoogleService-Info.plist */,
);
sourceTree = "<group>";
};
@@ -192,6 +195,7 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
ADF3D9B31D73179ECCE7D3BB /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
+3 -3
View File
@@ -5,15 +5,15 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Prosappco</string>
<string>Prosapp</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<string>com.prosapp.prosapp</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>prosappco</string>
<string>prosapp</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
+1 -1
View File
@@ -1,7 +1,7 @@
{
"file_generated_by": "FlutterFire CLI",
"purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory",
"GOOGLE_APP_ID": "1:245204384533:ios:aeb233e55c5ddf7a9f871c",
"GOOGLE_APP_ID": "1:245204384533:ios:1d58f5624d0df4859f871c",
"FIREBASE_PROJECT_ID": "prosapp-5747a",
"GCM_SENDER_ID": "245204384533"
}
+10 -5
View File
@@ -49,35 +49,40 @@ class DefaultFirebaseOptions {
messagingSenderId: '245204384533',
projectId: 'prosapp-5747a',
authDomain: 'prosapp-5747a.firebaseapp.com',
databaseURL: 'https://prosapp-5747a-default-rtdb.firebaseio.com',
storageBucket: 'prosapp-5747a.appspot.com',
measurementId: 'G-EVBEQRZEDY',
);
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyBp7vsJvD6oXz0FORBLEeIELwId4a2onHQ',
appId: '1:245204384533:android:12920d0fe3ec9b999f871c',
appId: '1:245204384533:android:e01772831d86f5c79f871c',
messagingSenderId: '245204384533',
projectId: 'prosapp-5747a',
databaseURL: 'https://prosapp-5747a-default-rtdb.firebaseio.com',
storageBucket: 'prosapp-5747a.appspot.com',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyDUhnkNkeEPkqcwVo5EWr63q3oEX6bDGic',
appId: '1:245204384533:ios:aeb233e55c5ddf7a9f871c',
appId: '1:245204384533:ios:1d58f5624d0df4859f871c',
messagingSenderId: '245204384533',
projectId: 'prosapp-5747a',
databaseURL: 'https://prosapp-5747a-default-rtdb.firebaseio.com',
storageBucket: 'prosapp-5747a.appspot.com',
androidClientId: '245204384533-7p2j0go97da4gp062s7enop0ab8q7a7p.apps.googleusercontent.com',
androidClientId: '245204384533-04oabs744hp8fjbdreijp4h16nvfmeti.apps.googleusercontent.com',
iosClientId: '245204384533-nv7e2n7d4vj5kfkc55mlmiiju0ch2qhn.apps.googleusercontent.com',
iosBundleId: 'com.example.prosappco',
);
static const FirebaseOptions macos = FirebaseOptions(
apiKey: 'AIzaSyDUhnkNkeEPkqcwVo5EWr63q3oEX6bDGic',
appId: '1:245204384533:ios:aeb233e55c5ddf7a9f871c',
appId: '1:245204384533:ios:1d58f5624d0df4859f871c',
messagingSenderId: '245204384533',
projectId: 'prosapp-5747a',
databaseURL: 'https://prosapp-5747a-default-rtdb.firebaseio.com',
storageBucket: 'prosapp-5747a.appspot.com',
androidClientId: '245204384533-7p2j0go97da4gp062s7enop0ab8q7a7p.apps.googleusercontent.com',
androidClientId: '245204384533-04oabs744hp8fjbdreijp4h16nvfmeti.apps.googleusercontent.com',
iosClientId: '245204384533-nv7e2n7d4vj5kfkc55mlmiiju0ch2qhn.apps.googleusercontent.com',
iosBundleId: 'com.example.prosappco',
);
+23 -24
View File
@@ -1,4 +1,4 @@
import 'package:animated_splash_screen/animated_splash_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
@@ -25,8 +25,9 @@ import 'package:prosappco/src/screens/solicitudes.dart';
import 'package:prosappco/src/services/local_notifications.dart';
import 'firebase_options.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_web_plugins/url_strategy.dart';
//import 'package:flutter_web_plugins/url_strategy.dart';
import 'package:flutter/foundation.dart' show kIsWeb; // Importa kIsWeb
//import 'dart:html' as html;
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
@@ -40,30 +41,28 @@ void main() async {
options: DefaultFirebaseOptions.currentPlatform,
).then((value) => Get.put(AuthenticationRepository()));
FirebaseMessaging.onBackgroundMessage((_firebaseMessagingBackgroundHandler));
// Evitar la solicitud de permiso en la versión web
if (!kIsWeb) {
FirebaseMessaging messaging = FirebaseMessaging.instance;
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
print('User granted permission: ${settings.authorizationStatus}');
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
FirebaseMessaging.onBackgroundMessage((_firebaseMessagingBackgroundHandler));
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
print('User granted permission: ${settings.authorizationStatus}');
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
// Tu código para manejar las notificaciones aquí
});
}
await initNotifications();
@@ -73,7 +72,7 @@ void main() async {
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
usePathUrlStrategy();
runApp(const MyApp());
}
@@ -88,8 +87,8 @@ class MyApp extends StatelessWidget {
title: 'ProsApp',
initialRoute: '/',
routes: {
'/splash': (context) => const SplashScreen(),
'/': (context) => const LoginScreen(),
'/splash': (context) => const SplashScreen(),
'/login': (context) => const LoginEmailScreen(),
'/profile': (context) => const ProfileScreen(),
'/register': (context) => const RegisterScreen(),
+21 -5
View File
@@ -14,6 +14,7 @@ import 'package:prosappco/src/screens/profile.dart';
import 'package:prosappco/src/screens/profile_web.dart';
import 'package:prosappco/src/screens/reputation.dart';
import 'package:prosappco/src/screens/support.dart';
import 'package:prosappco/src/screens/web_view.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:get/get.dart';
@@ -46,7 +47,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
Future<void> _irSugerencias() async {
const url =
'https://admin.prosapp.co/sugerencias '; // Aquí debes poner la URL a la que quieres dirigirte
'https://admin.prosapp.co/sugerencias'; // Aquí debes poner la URL a la que quieres dirigirte
if (await canLaunch(url)) {
await launch(url);
} else {
@@ -224,7 +225,22 @@ class _DrawerMenuState extends State<DrawerMenu> {
),
),
ListTile(
onTap: _irSugerencias,
onTap: () {
if (kIsWeb) {
_irSugerencias();
} else {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return WebViewScreen(
label: 'Sugerencias',
link: 'https://admin.prosapp.co/sugerencias');
},
),
);
}
},
leading: const Icon(
Icons.campaign_outlined,
color: Colors.black,
@@ -390,14 +406,14 @@ class _DrawerMenuState extends State<DrawerMenu> {
user?.phoneNumber == null) {
Get.snackbar(
'Completa tu perfil',
'Para ser un profesional registrado, completa tu perfil. No olvides guardar tus cambios para que sean efectivos.',
'Para ser un profesional registrado, asegúrate de llenar todos los campos necesarios y no olvides guardar tus cambios para que surtan efecto.',
snackPosition: SnackPosition.BOTTOM,
);
} else {
if (currentUser?.phoneNumber != user?.phoneNumber) {
Get.snackbar(
'Número de teléfono sin cambios',
'Tu número de teléfono no se ha actualizado. Para guardar los cambios, ve al apartado de perfil y presiona en "Guardar".',
'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,
);
} else {
+17 -1
View File
@@ -14,6 +14,7 @@ import 'package:prosappco/src/screens/profile.dart';
import 'package:prosappco/src/screens/profile_pro.dart';
import 'package:prosappco/src/screens/profile_pro_web.dart';
import 'package:prosappco/src/screens/reputacion_pro.dart';
import 'package:prosappco/src/screens/web_view.dart';
import '../models/scores_model.dart';
import '../screens/configuracion.dart';
import '../screens/support.dart';
@@ -211,7 +212,22 @@ class _DrawerProfessionalState extends State<DrawerProfessional> {
),
),
ListTile(
onTap: _irSugerencias,
onTap: () {
if (kIsWeb) {
_irSugerencias();
} else {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return WebViewScreen(
label: 'Sugerencias',
link: 'https://admin.prosapp.co/sugerencias');
},
),
);
}
},
leading: const Icon(
Icons.campaign_outlined,
color: Colors.black,
+21 -7
View File
@@ -3,26 +3,40 @@ import 'package:flutter/material.dart';
class PrimaryButtom extends StatelessWidget {
final VoidCallback onPressed;
final String label;
final bool
isEnabled; // Nuevo parámetro para indicar si el botón está habilitado
const PrimaryButtom(
{super.key, required this.onPressed, required this.label});
const PrimaryButtom({
Key? key,
required this.onPressed,
required this.label,
this.isEnabled = true, // Valor predeterminado: habilitado
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
onPressed: isEnabled
? onPressed
: null, // Habilita/deshabilita el botón según isEnabled
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC),
backgroundColor: isEnabled
? const Color(0xFF2BA4EC)
: Colors.grey, // Cambia el color de fondo
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
elevation: 0,
elevation: isEnabled
? 0
: 0, // Cambia la elevación para dar una sensación de clickeabilidad
minimumSize: const Size(230, 60),
),
child: Text(
label,
style: const TextStyle(
color: Colors.white,
style: TextStyle(
color: isEnabled
? Colors.white
: Colors.black, // Cambia el color del texto
fontWeight: FontWeight.bold,
fontSize: 18,
),
+2 -2
View File
@@ -170,7 +170,7 @@ class _CalendarScreenState extends State<CalendarScreen> {
content: StatefulBuilder(
builder: (BuildContext context, StateSetter setStateDialog) {
return SizedBox(
height: 350,
height: 800,
child: Column(
children: [
Padding(
@@ -352,7 +352,7 @@ class _CalendarScreenState extends State<CalendarScreen> {
future: getByProId(DateFormat("yyyy-MM-dd 00:00:00.000").format(today)),
builder: (BuildContext context, AsyncSnapshot<List<Event>> snapshot) {
if (!snapshot.hasData) {
return const Text('No tiene citas');
return const Text('No tienes citas');
}
List<Event> eventos = [];
+285 -272
View File
@@ -71,7 +71,8 @@ class _CitaScreenState extends State<CitaScreen> {
'data': <String, dynamic>{
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
'id': '1',
'status': 'done'
'status': 'done',
'screen': 'misservicios',
},
'to': user
},
@@ -233,45 +234,47 @@ class _CitaScreenState extends State<CitaScreen> {
],
),
),
Container(
margin: const EdgeInsets.only(
left: 40, right: 40, top: 20, bottom: 20),
padding:
const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
decoration: BoxDecoration(
color: const Color(0xFFD6F4FF),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(1, 3),
),
],
),
child: Row(
children: [
const Icon(
Icons.error_outline,
size: 27,
color: Colors.black54,
),
const SizedBox(width: 15),
widget.evento.ubicacion != 'sitio'
? const Text(
'Servicio a domicilio.',
style:
TextStyle(color: Colors.black, fontSize: 14),
)
: const Text(
'Servicio en su sitio / consultorio',
style:
TextStyle(color: Colors.black, fontSize: 14),
widget.evento.userId == widget.evento.professionalId
? const SizedBox()
: Container(
margin: const EdgeInsets.only(
left: 40, right: 40, top: 20, bottom: 20),
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 15),
decoration: BoxDecoration(
color: const Color(0xFFD6F4FF),
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(1, 3),
),
],
),
),
],
),
child: Row(
children: [
const Icon(
Icons.error_outline,
size: 27,
color: Colors.black54,
),
const SizedBox(width: 15),
widget.evento.ubicacion != 'sitio'
? const Text(
'Servicio a domicilio.',
style: TextStyle(
color: Colors.black, fontSize: 14),
)
: const Text(
'Servicio en su sitio / consultorio',
style: TextStyle(
color: Colors.black, fontSize: 14),
),
],
),
),
settings?.tarifas == true && widget.evento.tarifa != 0
? Column(
children: [
@@ -292,249 +295,259 @@ class _CitaScreenState extends State<CitaScreen> {
style: const TextStyle(
color: Colors.grey, fontStyle: FontStyle.italic),
),
widget.evento.status == 'terminado'
? const SizedBox(height: 10)
: const Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: Text(
'Medios de comunicación con el usuario.',
style: TextStyle(color: Color(0xFF2BA4EC)),
),
),
widget.evento.status == 'terminado'
? pro == true
? widget.evento.professionalScored == true
? const SizedBox()
: Column(
children: [
const SizedBox(height: 120),
ElevatedButton(
onPressed: () {
Navigator.pushReplacement(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return ScoreScreen(
evento: widget.evento,
pro: pro!,
);
},
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
elevation: 0,
minimumSize: const Size(230, 60),
),
child: const Text(
'Puntuar servicio',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
],
)
: widget.evento.userScored == true
? const SizedBox()
: Column(
children: [
const SizedBox(height: 120),
ElevatedButton(
onPressed: () {
Navigator.pushReplacement(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return ScoreScreen(
evento: widget.evento,
pro: pro!,
);
},
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
elevation: 0,
minimumSize: const Size(230, 60),
),
child: const Text(
'Puntuar servicio',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
],
)
: Row(
children: [
const Expanded(child: SizedBox()),
ElevatedButton(
onPressed: () => launch("tel:$numberPhone"),
style: ElevatedButton.styleFrom(
foregroundColor: const Color(0xFF2BA4EC),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
side: const BorderSide(
color: Color(0xFF2BA4EC),
width: 2,
),
),
),
child: const Padding(
padding: EdgeInsets.symmetric(
vertical: 18, horizontal: 0),
child: Icon(
Icons.phone_android,
size: 30,
color: Color(0xFF2BA4EC),
),
widget.evento.userId == widget.evento.professionalId
? const SizedBox()
: widget.evento.status == 'terminado'
? const SizedBox(height: 10)
: const Padding(
padding: EdgeInsets.symmetric(vertical: 20),
child: Text(
'Medios de comunicación con el usuario.',
style: TextStyle(color: Color(0xFF2BA4EC)),
),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () async {
if (widget.evento.status != 'pendiente') {
final chatDoc = FirebaseFirestore.instance
.collection('chats')
.doc(widget.evento.id);
final chatSnapshot = await chatDoc.get();
if (!chatSnapshot.exists ||
chatSnapshot.data()!['message'] == null) {
await chatDoc.set(
{
'professional_id':
widget.evento.professionalId,
'user_id': widget.evento.userId,
'message': [],
},
SetOptions(merge: true),
).catchError((error) => print(
'Error al crear el documento: $error'));
}
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return ChatScreen(
eventoId: widget.evento.id);
},
),
);
}
// if (widget.evento.status != 'pendiente') {
// await FirebaseFirestore.instance
// .collection('chats')
// .doc(widget.evento.id)
// .set(
// {
// 'professional_id':
// widget.evento.professionalId,
// 'user_id': widget.evento.userId,
// 'message': [],
// },
// SetOptions(
// merge:
// true)).catchError((error) => print(
// 'Error al crear el documento: $error'));
// Navigator.push(
// context,
// CupertinoPageRoute(
// builder: (BuildContext context) {
// return ChatScreen(
// eventoId: widget.evento.id);
// },
// ),
// );
// }
else {
final snackBar = SnackBar(
backgroundColor: Colors.blue,
content: const Text(
'Primero acepta la solicitud',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
elevation: 8.0,
widget.evento.userId == widget.evento.professionalId
? const SizedBox()
: widget.evento.status == 'terminado'
? pro == true
? widget.evento.professionalScored == true
? const SizedBox()
: Column(
children: [
const SizedBox(height: 120),
ElevatedButton(
onPressed: () {
Navigator.pushReplacement(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return ScoreScreen(
evento: widget.evento,
pro: pro!,
);
},
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor:
const Color(0xFF2BA4EC),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(50),
),
elevation: 0,
minimumSize: const Size(230, 60),
),
child: const Text(
'Puntuar servicio',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
],
)
: widget.evento.userScored == true
? const SizedBox()
: Column(
children: [
const SizedBox(height: 120),
ElevatedButton(
onPressed: () {
Navigator.pushReplacement(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return ScoreScreen(
evento: widget.evento,
pro: pro!,
);
},
),
);
},
style: ElevatedButton.styleFrom(
backgroundColor:
const Color(0xFF2BA4EC),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(50),
),
elevation: 0,
minimumSize: const Size(230, 60),
),
child: const Text(
'Puntuar servicio',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
],
)
: Row(
children: [
const Expanded(child: SizedBox()),
ElevatedButton(
onPressed: () => launch("tel:$numberPhone"),
style: ElevatedButton.styleFrom(
foregroundColor: const Color(0xFF2BA4EC),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
borderRadius: BorderRadius.circular(50),
side: const BorderSide(
color: Color(0xFF2BA4EC),
width: 2,
),
),
);
),
child: const Padding(
padding: EdgeInsets.symmetric(
vertical: 18, horizontal: 0),
child: Icon(
Icons.phone_android,
size: 30,
color: Color(0xFF2BA4EC),
),
),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () async {
if (widget.evento.status != 'pendiente') {
final chatDoc = FirebaseFirestore.instance
.collection('chats')
.doc(widget.evento.id);
final chatSnapshot = await chatDoc.get();
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
}
},
style: ElevatedButton.styleFrom(
foregroundColor: const Color(0xFF2BA4EC),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
side: const BorderSide(
color: Color(0xFF2BA4EC),
width: 2,
if (!chatSnapshot.exists ||
chatSnapshot.data()!['message'] ==
null) {
await chatDoc.set(
{
'professional_id':
widget.evento.professionalId,
'user_id': widget.evento.userId,
'message': [],
},
SetOptions(merge: true),
).catchError((error) => print(
'Error al crear el documento: $error'));
}
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return ChatScreen(
eventoId: widget.evento.id);
},
),
);
}
// if (widget.evento.status != 'pendiente') {
// await FirebaseFirestore.instance
// .collection('chats')
// .doc(widget.evento.id)
// .set(
// {
// 'professional_id':
// widget.evento.professionalId,
// 'user_id': widget.evento.userId,
// 'message': [],
// },
// SetOptions(
// merge:
// true)).catchError((error) => print(
// 'Error al crear el documento: $error'));
// Navigator.push(
// context,
// CupertinoPageRoute(
// builder: (BuildContext context) {
// return ChatScreen(
// eventoId: widget.evento.id);
// },
// ),
// );
// }
else {
final snackBar = SnackBar(
backgroundColor: Colors.blue,
content: const Text(
'Primero acepta la solicitud',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
elevation: 8.0,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10.0),
),
);
ScaffoldMessenger.of(context)
.showSnackBar(snackBar);
}
},
style: ElevatedButton.styleFrom(
foregroundColor: const Color(0xFF2BA4EC),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
side: const BorderSide(
color: Color(0xFF2BA4EC),
width: 2,
),
),
),
child: const Padding(
padding: EdgeInsets.symmetric(
vertical: 18, horizontal: 0),
child: Icon(
Icons.message,
size: 30,
color: Color(0xFF2BA4EC),
),
),
),
),
child: const Padding(
padding: EdgeInsets.symmetric(
vertical: 18, horizontal: 0),
child: Icon(
Icons.message,
size: 30,
color: Color(0xFF2BA4EC),
),
),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () {
_sendWhatsapp(numberPhone);
},
style: ElevatedButton.styleFrom(
foregroundColor: const Color(0xFF2BA4EC),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
side: const BorderSide(
color: Color(0xFF2BA4EC),
width: 2,
const SizedBox(width: 20),
ElevatedButton(
onPressed: () {
_sendWhatsapp(numberPhone);
},
style: ElevatedButton.styleFrom(
foregroundColor: const Color(0xFF2BA4EC),
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
side: const BorderSide(
color: Color(0xFF2BA4EC),
width: 2,
),
),
),
child: const Padding(
padding: EdgeInsets.symmetric(
vertical: 18, horizontal: 0),
child: Icon(
CommunityMaterialIcons.whatsapp,
size: 30,
color: Color(0xFF2BA4EC),
),
),
),
),
child: const Padding(
padding: EdgeInsets.symmetric(
vertical: 18, horizontal: 0),
child: Icon(
CommunityMaterialIcons.whatsapp,
size: 30,
color: Color(0xFF2BA4EC),
),
),
const Expanded(child: SizedBox()),
],
),
const Expanded(child: SizedBox()),
],
),
widget.evento.ubicacion == 'sitio'
? const SizedBox()
: Padding(
+2 -2
View File
@@ -152,7 +152,7 @@ class _CityScreenState extends State<CityScreen> {
onPressed: () {
Navigator.pop(context);
},
label: 'Seleccione su ciudad'),
label: 'Selecciona tu ciudad'),
body: Column(
children: [
Padding(
@@ -160,7 +160,7 @@ class _CityScreenState extends State<CityScreen> {
child: TextField(
controller: searchController,
decoration: const InputDecoration(
hintText: 'Buscar ciudad',
hintText: 'Busca una ciudad',
prefixIcon: Icon(Icons.near_me),
),
),
+3 -3
View File
@@ -40,7 +40,7 @@ class CodeValidationScreen extends StatelessWidget {
},
),
const Text(
'Validar código',
'Valida el código',
style: TextStyle(
color: Color(0xFF262626),
fontSize: 38.0,
@@ -121,7 +121,7 @@ class CodeValidationScreen extends StatelessWidget {
onPressed: () {
OTPController.instance.verifyOTP(otp);
},
label: 'Validar código',
label: 'Valida el código',
),
const SizedBox(height: 30),
],
@@ -257,7 +257,7 @@ class CodeValidationScreen extends StatelessWidget {
onPressed: () {
OTPController.instance.verifyOTP(otp);
},
label: 'Validar código',
label: 'Valida el código',
),
const SizedBox(height: 30),
],
+121 -6
View File
@@ -1,3 +1,5 @@
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';
@@ -5,8 +7,11 @@ import 'package:intl_phone_field/intl_phone_field.dart';
import 'package:prosappco/src/components/bottom_sheet.dart';
import 'package:prosappco/src/components/primary_btn.dart';
import 'package:prosappco/src/controllers/phone_auth_controller.dart';
import 'package:prosappco/src/models/setting_model.dart';
import 'package:prosappco/src/screens/code_validation.dart';
import 'package:prosappco/src/screens/web_view.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:url_launcher/url_launcher.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@@ -19,6 +24,28 @@ class _LoginScreenState extends State<LoginScreen> {
final controller = Get.put(PhoneAuthController());
final _formKey = GlobalKey<FormState>();
String completePhoneNumber = '';
bool _isChecked = false;
SettingModel? settings;
void _launchURL(String url) async {
if (await canLaunch(url)) {
await launch(url, forceSafariVC: false, forceWebView: false);
} else {
throw 'No se pudo abrir el enlace $url';
}
}
@override
void initState() {
super.initState();
if (settings == null) {
SettingModel.getSettings().then(
(SettingModel value) => setState(() {
settings = value;
}),
);
}
}
@override
Widget build(BuildContext context) {
@@ -98,7 +125,49 @@ class _LoginScreenState extends State<LoginScreen> {
color: Color(0xFF65676B),
),
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Checkbox(
value: _isChecked,
onChanged: (value) {
setState(() {
_isChecked = value!;
});
},
),
GestureDetector(
onTap: () {
if (kIsWeb) {
_launchURL(settings?.terminosCondiciones ?? '');
} else {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return WebViewScreen(
label: 'Términos y condiciones',
link: settings?.terminosCondiciones ?? '',
);
},
),
);
}
},
child: const Text(
'Acepto los términos y condiciones.',
style: TextStyle(
fontSize: 13.0,
color: Color(0xFF65676B),
decoration: TextDecoration.underline,
),
),
),
],
),
),
PrimaryButtom(
onPressed: () {
if (_formKey.currentState!.validate()) {
@@ -112,6 +181,7 @@ class _LoginScreenState extends State<LoginScreen> {
);
}
},
isEnabled: _isChecked,
label: 'Enviar código',
),
const SizedBox(height: 20),
@@ -216,14 +286,58 @@ class _LoginScreenState extends State<LoginScreen> {
),
),
const Text(
'Un código será enviado a este numero de celular.',
'Se enviará un código a este número de celular.',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 13.0,
color: Color(0xFF65676B),
),
),
const SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Checkbox(
value: _isChecked,
onChanged: (value) {
setState(() {
_isChecked = value!;
});
},
),
GestureDetector(
onTap: () {
if (kIsWeb) {
_launchURL(settings?.terminosCondiciones ?? '');
} else {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return WebViewScreen(
label: 'Términos y condiciones',
link:
settings?.terminosCondiciones ?? '',
);
},
),
);
}
},
child: const Text(
'Acepto los términos y condiciones.',
style: TextStyle(
fontSize: 13.0,
color: Color(0xFF65676B),
decoration: TextDecoration
.underline, // Add underline style
),
),
),
],
),
),
PrimaryButtom(
onPressed: () {
if (_formKey.currentState!.validate()) {
@@ -237,6 +351,7 @@ class _LoginScreenState extends State<LoginScreen> {
);
}
},
isEnabled: _isChecked,
label: 'Enviar código',
),
const SizedBox(height: 20),
@@ -266,7 +381,7 @@ class GestureBottom extends StatelessWidget {
Navigator.pushNamed(context, '/login');
},
child: const Text(
'Entrar con correo electronico',
'Inicia sesión con tu correo electrónico',
style: TextStyle(
fontSize: 15.0, color: Color(0xFF65676B),
decoration: TextDecoration.underline, // Subrayado
@@ -291,14 +406,14 @@ class RichTxTBottom extends StatelessWidget {
fontFamily: 'Poppins',
),
children: [
const TextSpan(text: 'No estas registrado? '),
const TextSpan(text: '¿No estás registrado? '),
WidgetSpan(
child: GestureDetector(
onTap: () {
Navigator.pushNamed(context, '/register');
},
child: const Text(
'Registrarse',
'Regístrate',
style: TextStyle(
fontSize: 16.0,
color: Color(0xFF2BA4EC),
+6 -6
View File
@@ -114,7 +114,7 @@ class _LoginEmailScreenState extends State<LoginEmailScreen> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Entrar con Google ',
'Entra con Google ',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
@@ -179,7 +179,7 @@ class _LoginEmailScreenState extends State<LoginEmailScreen> {
obscureText: _obscureText,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Por favor ingrese una contraseña';
return 'Por favor, ingresa una contraseña';
}
return null;
},
@@ -383,7 +383,7 @@ class _LoginEmailScreenState extends State<LoginEmailScreen> {
obscureText: _obscureText,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Por favor ingrese una contraseña';
return 'Por favor, ingresa una contraseña';
}
return null;
},
@@ -466,12 +466,12 @@ class FormEmail extends StatelessWidget {
controller: controller.email,
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Por favor ingrese un email';
return 'Por favor, ingresa un Email';
}
final RegExp emailRegExp =
RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegExp.hasMatch(value)) {
return 'Por favor ingrese un email válido';
return 'Por favor, ingresa un Email válido';
}
return null;
},
@@ -551,7 +551,7 @@ class RichTxtBottom extends StatelessWidget {
fontFamily: 'Poppins',
),
children: [
const TextSpan(text: 'No estas registrado? '),
const TextSpan(text: '¿No estás registrado? '),
WidgetSpan(
child: GestureDetector(
onTap: () {
+1 -1
View File
@@ -44,7 +44,7 @@ class _MessagesScreenState extends State<MessagesScreen> {
child: TextField(
// controller: searchController,
decoration: InputDecoration(
hintText: 'Escriba un nombre',
hintText: 'Escribe un nombre',
prefixIcon: Icon(CommunityMaterialIcons.stethoscope),
),
),
+1 -1
View File
@@ -45,7 +45,7 @@ class _MessagesUserScreenState extends State<MessagesUserScreen> {
child: TextField(
// controller: searchController,
decoration: InputDecoration(
hintText: 'Escriba un nombre',
hintText: 'Escribe un nombre',
prefixIcon: Icon(CommunityMaterialIcons.stethoscope),
),
),
+120 -90
View File
@@ -87,6 +87,7 @@ class _MyServicesScreenState extends State<MyServicesScreen> {
try {
eventos.addAll(snapshot.data!);
eventos.sort((a, b) => a.range1Hour1.compareTo(b.range1Hour1));
} catch (e) {
print("Error" + e.toString());
}
@@ -100,108 +101,137 @@ class _MyServicesScreenState extends State<MyServicesScreen> {
return Column(
children: [
...eventos.map(
(event) => ListTile(
tileColor: event.status == 'denegado'
? Colors.red[100]
: Colors.blue[100],
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return CitaScreen(evento: event);
},
),
);
},
leading: event.status == 'aprobado'
? const Icon(
Icons.check,
color: Colors.blue,
size: 40,
)
: event.status == 'iniciado'
? const Icon(
Icons.access_time,
color: Colors.blue,
size: 40,
)
: event.status == 'pendiente'
...eventos
.where((event) => event.professionalId != event.userId)
.map(
(event) => FutureBuilder<DocumentSnapshot>(
future: FirebaseFirestore.instance
.collection('users')
.doc(event.professionalId)
.get(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> profSnapshot) {
if (profSnapshot.connectionState ==
ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (profSnapshot.hasError) {
return const Text(
'Error al obtener los datos del profesional',
// Aquí puedes agregar un estilo de texto apropiado
);
}
final professionalData = profSnapshot.data;
final professionalName =
professionalData?['name'] ?? 'N/D';
return ListTile(
tileColor: event.status == 'denegado'
? Colors.red[100]
: Colors.blue[100],
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return CitaScreen(evento: event);
},
),
);
},
leading: event.status == 'aprobado'
? const Icon(
Icons.access_time_outlined,
Icons.check,
color: Colors.blue,
size: 40,
)
: event.status == 'terminado'
: event.status == 'iniciado'
? const Icon(
Icons.rocket_launch,
Icons.access_time,
color: Colors.blue,
size: 40,
)
: const Icon(
Icons.close,
color: Colors.red,
size: 40,
: event.status == 'pendiente'
? const Icon(
Icons.access_time_outlined,
color: Colors.blue,
size: 40,
)
: event.status == 'terminado'
? const Icon(
Icons.rocket_launch,
color: Colors.blue,
size: 40,
)
: const Icon(
Icons.close,
color: Colors.red,
size: 40,
),
title: RichText(
text: TextSpan(
children: [
TextSpan(
text: '${professionalName}, ',
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
TextSpan(
text:
'${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${TimeOfDay.fromDateTime(DateTime.parse(event.range1Hour1)).format(context)}',
style: TextStyle(
color: Colors.grey[600],
fontSize: 16,
),
),
],
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
RatingBar.builder(
initialRating:
event.scoresModel?.average ?? 0,
minRating: 1,
direction: Axis.horizontal,
allowHalfRating: true,
itemCount: 5,
itemSize: 25,
maxRating: 5,
itemPadding:
const EdgeInsets.symmetric(horizontal: 0),
itemBuilder: (context, _) => const Icon(
Icons.star,
color: Color(0xFF2BA4EC),
),
title: RichText(
text: TextSpan(
children: [
TextSpan(
text: '${event.title}, ',
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 16,
onRatingUpdate: (rating) {},
ignoreGestures: true,
),
const SizedBox(width: 5),
Text(
'(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'),
],
),
Text(
'" ${event.description}"',
style:
const TextStyle(fontStyle: FontStyle.italic),
),
],
),
),
TextSpan(
text:
'${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${TimeOfDay.fromDateTime(DateTime.parse(event.range1Hour1)).format(context)}',
style: TextStyle(
color: Colors.grey[600],
fontSize: 16,
),
),
],
trailing: const Icon(Icons.keyboard_arrow_right),
);
},
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
RatingBar.builder(
initialRating: event.scoresModel?.average ?? 0,
minRating: 1,
direction: Axis.horizontal,
allowHalfRating: true,
itemCount: 5,
itemSize: 25,
maxRating: 5,
itemPadding:
const EdgeInsets.symmetric(horizontal: 0),
itemBuilder: (context, _) => const Icon(
Icons.star,
color: Color(0xFF2BA4EC),
),
onRatingUpdate: (rating) {},
ignoreGestures: true,
),
const SizedBox(width: 5),
Text(
'(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'),
],
),
Text(
'" ${event.description}"',
style: const TextStyle(fontStyle: FontStyle.italic),
),
],
),
trailing: const Icon(Icons.keyboard_arrow_right),
),
),
],
);
},
+94 -94
View File
@@ -31,10 +31,11 @@ class _MyServicesProScreenState extends State<MyServicesProScreen> {
return SafeArea(
child: Scaffold(
appBar: PopAppbar(
onPressed: () {
Navigator.pop(context);
},
label: 'Mis servicios'),
onPressed: () {
Navigator.pop(context);
},
label: 'Mis servicios',
),
drawer: DrawerProfessional(),
body: SingleChildScrollView(
child: Column(
@@ -82,6 +83,7 @@ class _MyServicesProScreenState extends State<MyServicesProScreen> {
try {
eventos.addAll(snapshot.data!);
eventos.sort((a, b) => a.range1Hour1.compareTo(b.range1Hour1));
} catch (e) {
print("Error" + e.toString());
}
@@ -96,102 +98,100 @@ class _MyServicesProScreenState extends State<MyServicesProScreen> {
return Column(
children: [
...eventos.map(
(event) => event.userId == event.professionalId
? const SizedBox()
: ListTile(
tileColor: event.status == 'denegado'
? Colors.red[100]
: Colors.blue[100],
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return CitaScreen(evento: event);
},
),
);
(event) => ListTile(
tileColor: event.status == 'denegado'
? Colors.red[100]
: Colors.blue[100],
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return CitaScreen(evento: event);
},
leading: event.status == 'aprobado'
? const Icon(
Icons.check,
color: Colors.blue,
size: 40,
)
: event.status == 'iniciado'
? const Icon(
Icons.access_time,
color: Colors.blue,
size: 40,
)
: event.status == 'terminado'
? const Icon(
Icons.rocket_launch,
color: Colors.blue,
size: 40,
)
: const Icon(
Icons.close,
color: Colors.red,
size: 40,
),
title: RichText(
text: TextSpan(
children: [
TextSpan(
text: '${event.title}, ',
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 16,
),
);
},
leading: event.status == 'aprobado'
? const Icon(
Icons.check,
color: Colors.blue,
size: 40,
)
: event.status == 'iniciado'
? const Icon(
Icons.access_time,
color: Colors.blue,
size: 40,
)
: event.status == 'terminado'
? const Icon(
Icons.rocket_launch,
color: Colors.blue,
size: 40,
)
: const Icon(
Icons.close,
color: Colors.red,
size: 40,
),
),
TextSpan(
text:
'${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${TimeOfDay.fromDateTime(DateTime.parse(event.range1Hour1)).format(context)}',
style: TextStyle(
color: Colors.grey[600],
fontSize: 16,
),
),
],
title: RichText(
text: TextSpan(
children: [
TextSpan(
text: '${event.title}, ',
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
RatingBar.builder(
initialRating: event.scoresModel?.average ?? 0,
minRating: 1,
direction: Axis.horizontal,
allowHalfRating: true,
itemCount: 5,
itemSize: 25,
maxRating: 5,
itemPadding:
const EdgeInsets.symmetric(horizontal: 0),
itemBuilder: (context, _) => const Icon(
Icons.star,
color: Color(0xFF2BA4EC),
),
onRatingUpdate: (rating) {},
ignoreGestures: true,
),
const SizedBox(width: 5),
Text(
'(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'),
],
),
Text(
'" ${event.description}"',
style: const TextStyle(fontStyle: FontStyle.italic),
),
],
TextSpan(
text:
'${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${TimeOfDay.fromDateTime(DateTime.parse(event.range1Hour1)).format(context)}',
style: TextStyle(
color: Colors.grey[600],
fontSize: 16,
),
),
trailing: const Icon(Icons.keyboard_arrow_right),
],
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
RatingBar.builder(
initialRating: event.scoresModel?.average ?? 0,
minRating: 1,
direction: Axis.horizontal,
allowHalfRating: true,
itemCount: 5,
itemSize: 25,
maxRating: 5,
itemPadding:
const EdgeInsets.symmetric(horizontal: 0),
itemBuilder: (context, _) => const Icon(
Icons.star,
color: Color(0xFF2BA4EC),
),
onRatingUpdate: (rating) {},
ignoreGestures: true,
),
const SizedBox(width: 5),
Text(
'(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'),
],
),
Text(
'" ${event.description}"',
style: const TextStyle(fontStyle: FontStyle.italic),
),
],
),
trailing: const Icon(Icons.keyboard_arrow_right),
),
),
],
);
+2 -2
View File
@@ -102,7 +102,7 @@ class _NewNumberScreenState extends State<NewNumberScreen> {
const Padding(
padding: EdgeInsets.only(bottom: 30),
child: Text(
'Un código será enviado a este numero de celular.',
'Se enviará un código a este número de celular',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 13.0, color: Color(0xFF65676B))),
@@ -215,7 +215,7 @@ class _NewNumberScreenState extends State<NewNumberScreen> {
const Padding(
padding: EdgeInsets.only(bottom: 30),
child: Text(
'Un código será enviado a este numero de celular.',
'Se enviará un código a este número de celular',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 13.0, color: Color(0xFF65676B))),
+3 -3
View File
@@ -49,7 +49,7 @@ class _NewNumberValidationScreenState extends State<NewNumberValidationScreen> {
},
),
const Text(
'Validar código',
'Valida el código',
style: TextStyle(
color: Color(0xFF262626),
fontSize: 38.0,
@@ -98,7 +98,7 @@ class _NewNumberValidationScreenState extends State<NewNumberValidationScreen> {
alignment: Alignment.topLeft,
child: Padding(
padding: EdgeInsets.only(bottom: 5),
child: Text('Codigo',
child: Text('Código',
textAlign: TextAlign.left,
style:
TextStyle(fontSize: 18.0, color: Color(0xFF65676B))),
@@ -135,7 +135,7 @@ class _NewNumberValidationScreenState extends State<NewNumberValidationScreen> {
minimumSize: const Size(230, 60), // Tamaño mínimo del botón
),
child: const Text(
'Validar código',
'Valida el código',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
+4 -4
View File
@@ -32,7 +32,7 @@ class _NewPasswordScreenState extends State<NewPasswordScreen> {
if (newPassword == currentPassword) {
Get.snackbar(
'Misma contraseña',
'La nueva contraseña debe ser diferente a la contraseña actual.',
'La nueva contraseña debe ser distinta a la contraseña actual.',
snackPosition: SnackPosition.BOTTOM,
);
return;
@@ -50,7 +50,7 @@ class _NewPasswordScreenState extends State<NewPasswordScreen> {
// Muestra un mensaje de éxito
Get.snackbar(
'Contraseña actualizada',
'La contraseña ha sido actualizada correctamente.',
'Tu contraseña ha sido cambiada con éxito.',
snackPosition: SnackPosition.BOTTOM,
);
} catch (e) {
@@ -60,7 +60,7 @@ class _NewPasswordScreenState extends State<NewPasswordScreen> {
} catch (e) {
Get.snackbar(
'Contraseña incorrecta',
'Error al actualizar la contraseña asegurese de ingresar bien la contraseña actual.',
'Ha ocurrido un error al actualizar la contraseña. Asegúrate de ingresar correctamente la contraseña actual.',
snackPosition: SnackPosition.BOTTOM,
);
return;
@@ -80,7 +80,7 @@ class _NewPasswordScreenState extends State<NewPasswordScreen> {
onPressed: () {
Navigator.pop(context);
},
label: 'Cambie su contraseña',
label: ' Cambia tu contraseña',
),
body: Center(
child: SizedBox(
+1 -1
View File
@@ -138,7 +138,7 @@ class _ProfessionScreenState extends State<ProfessionScreen> {
child: TextField(
controller: searchController,
decoration: const InputDecoration(
hintText: 'Buscar su profesión',
hintText: 'Busca tu profesión',
prefixIcon: Icon(Icons.assignment_ind_rounded),
),
),
+1 -1
View File
@@ -206,7 +206,7 @@ class _ProfessionalDireccionScreenState
setState(() {});
} catch (e) {
Get.snackbar(
'ubicación desactivada',
'Ubicación desactivada',
'Por favor activa la ubicacion de tu telefono.',
snackPosition: SnackPosition.TOP,
);
+4 -3
View File
@@ -422,7 +422,8 @@ class ProfessionalProfileScreenState extends State<ProfessionalProfileScreen> {
especializacion.split(',').map((e) => e.trim()).toList();
if (cedula.isEmpty) {
_showCustomSnackBar(context, 'Por favor, ingrese el número de cédula.');
_showCustomSnackBar(
context, 'Por favor, adjunta el documento PDF de tu cédula.');
return;
}
@@ -434,7 +435,7 @@ class ProfessionalProfileScreenState extends State<ProfessionalProfileScreen> {
if (image_certificado == null) {
_showCustomSnackBar(context,
'Por favor, adjunte el documento PDF de su certificado. ¡Gracias por su colaboración!');
'Por favor, adjunta el documento PDF de tu certificado. ¡Gracias por tu colaboración!');
return;
}
@@ -770,7 +771,7 @@ class ProfessionalProfileScreenState extends State<ProfessionalProfileScreen> {
SizedBox(width: 15),
Expanded(
child: Text(
'¡Importante! Solo aceptamos archivos en formato PDF. Si desea agregar más de una especialidad, envíe archivos adicionales separados por comas (,). ¡Gracias!',
'Si tienes más de una especialidad, por favor, adjunta un archivo con el diploma de respaldo para cada una de ellas y sepáralos por comas. ¡Gracias!',
style: TextStyle(color: Colors.black, fontSize: 14),
),
)
@@ -645,7 +645,7 @@ class _ProfessionalProfileWebScreenState
SizedBox(width: 15),
Expanded(
child: Text(
'Para añadir mas de una especialidad, envie mas fotos y separe por comas (,).',
'Si tienes más de una especialidad, por favor, adjunta un archivo con el diploma de respaldo para cada una de ellas y sepáralos por comas. ¡Gracias!',
style: TextStyle(
color: Colors.black, fontSize: 14),
),
+1 -1
View File
@@ -71,7 +71,7 @@ class _ProfessionalRevisionScreenState
children: [
Expanded(
child: Text(
'Gracias por proporcionar su información. Actualmente, estamos revisando sus datos y una vez aprobados, podrá acceder al perfil profesional sin problemas. Le notificaremos tan pronto como su cuenta esté lista. ¡Gracias por su paciencia!.',
'Gracias por proporcionar tu información. Actualmente, estamos revisando tus datos y una vez aprobados, podrás acceder al perfil profesional sin problemas. Te notificaremos tan pronto como tu cuenta esté lista. ¡Gracias por tu paciencia!',
style: TextStyle(
fontSize: 14,
),
+4 -5
View File
@@ -86,7 +86,6 @@ class _ProfileScreenState extends State<ProfileScreen> {
.collection('users')
.doc(uid)
.update({'photo': image});
} catch (e) {
try {
await FirebaseFirestore.instance
@@ -222,7 +221,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
// Si el nuevo nombre está vacío, no lo actualizamos y mostramos un mensaje al usuario
Get.snackbar(
'Nombre Invalido',
'Ingrese un nombre válido.',
'Ingresa un nombre válido.',
snackPosition: SnackPosition.BOTTOM,
);
return;
@@ -232,7 +231,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
// Si el nuevo email está vacío, no lo actualizamos y mostramos un mensaje al usuario
Get.snackbar(
'Correo Invalido',
'Ingrese un email válido.',
'Ingresa un email válido.',
snackPosition: SnackPosition.BOTTOM,
);
return;
@@ -259,7 +258,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
} else {
Get.snackbar(
'Contraseña Invalida',
'Porfavor ingrese una contraseña.',
'Porfavor ingresa una contraseña.',
snackPosition: SnackPosition.BOTTOM,
);
}
@@ -305,7 +304,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
} catch (e) {
Get.snackbar(
'Agregar correo',
'Inicie sesión para asegurarnos de que seas tú .',
'Inicia sesión para asegurarnos de que seas tú.',
snackPosition: SnackPosition.BOTTOM,
);
AuthenticationRepository.instance.logout(uid!);
+1 -1
View File
@@ -276,7 +276,7 @@ class _ProfileProScreenState extends State<ProfileProScreen> {
Get.snackbar(
'Información actualizada',
'Su información a sido actualizada con exito.',
'Tu información ha sido actualizada con éxito.',
snackPosition: SnackPosition.TOP,
);
+1 -1
View File
@@ -178,7 +178,7 @@ class _ProfileProWebScreenState extends State<ProfileProWebScreen> {
Get.snackbar(
'Información actualizada',
'Su información a sido actualizada con exito.',
'Tu información a sido actualizada con exito.',
snackPosition: SnackPosition.TOP,
);
+4 -4
View File
@@ -142,7 +142,7 @@ class _ProfileWebScreenState extends State<ProfileWebScreen> {
// Si el nuevo nombre está vacío, no lo actualizamos y mostramos un mensaje al usuario
Get.snackbar(
'Nombre Invalido',
'Ingrese un nombre válido.',
'Ingresa un nombre válido.',
snackPosition: SnackPosition.BOTTOM,
);
return;
@@ -161,7 +161,7 @@ class _ProfileWebScreenState extends State<ProfileWebScreen> {
// Si el nuevo nombre está vacío, no lo actualizamos y mostramos un mensaje al usuario
Get.snackbar(
'Correo Invalido',
'Ingrese un email válido.',
'Ingresa un email válido.',
snackPosition: SnackPosition.BOTTOM,
);
return;
@@ -186,7 +186,7 @@ class _ProfileWebScreenState extends State<ProfileWebScreen> {
} else {
Get.snackbar(
'Contraseña Invalida',
'Porfavor ingrese una contraseña.',
'Por favor, ingresa una contraseña.',
snackPosition: SnackPosition.BOTTOM,
);
}
@@ -262,7 +262,7 @@ class _ProfileWebScreenState extends State<ProfileWebScreen> {
} catch (e) {
Get.snackbar(
'Agregar correo',
'Inicie sesión para asegurarnos de que seas tú .',
'Inicia sesión para asegurarnos de que seas tú.',
snackPosition: SnackPosition.BOTTOM,
);
AuthenticationRepository.instance.logout(uid!);
+364 -298
View File
@@ -1,4 +1,5 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
@@ -7,8 +8,11 @@ import 'package:prosappco/main.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/primary_btn.dart';
import 'package:prosappco/src/controllers/register_controller.dart';
import 'package:prosappco/src/models/setting_model.dart';
import 'package:prosappco/src/screens/login.dart';
import 'package:prosappco/src/screens/web_view.dart';
import 'package:responsive_builder/responsive_builder.dart';
import 'package:url_launcher/url_launcher.dart';
class RegisterScreen extends StatefulWidget {
const RegisterScreen({super.key});
@@ -21,6 +25,28 @@ class _RegisterScreenState extends State<RegisterScreen> {
bool _obscureText = true;
final controller = Get.put(RegisterController());
final _formKey = GlobalKey<FormState>();
bool _isChecked = false;
SettingModel? settings;
void _launchURL(String url) async {
if (await canLaunch(url)) {
await launch(url, forceSafariVC: false, forceWebView: false);
} else {
throw 'No se pudo abrir el enlace $url';
}
}
@override
void initState() {
super.initState();
if (settings == null) {
SettingModel.getSettings().then(
(SettingModel value) => setState(() {
settings = value;
}),
);
}
}
@override
Widget build(BuildContext context) {
@@ -157,138 +183,184 @@ class _RegisterScreenState extends State<RegisterScreen> {
)
: const SizedBox(),
Form(
key: _formKey,
child: Column(
children: [
const Padding(
padding: EdgeInsets.only(bottom: 5),
child: Align(
alignment: Alignment.topLeft,
child: Text('Email',
style: TextStyle(
fontSize: 18.0,
color: Color(0xFF65676B))),
)),
Padding(
padding: const EdgeInsets.only(bottom: 18),
child: TextFormField(
controller: controller.email,
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Por favor ingrese un email';
}
final RegExp emailRegExp = RegExp(
r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegExp.hasMatch(value)) {
return 'Por favor ingrese un email válido';
}
return null;
},
decoration: const InputDecoration(
border: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color.fromARGB(
255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
hintText: 'Hello@gmail.com',
fillColor:
Color.fromARGB(255, 239, 239, 239),
filled: true,
prefixIcon: Icon(Icons.email_outlined)),
),
),
const Padding(
padding: EdgeInsets.only(bottom: 5),
child: Align(
alignment: Alignment.topLeft,
child: Text('Password',
style: TextStyle(
fontSize: 18.0,
color: Color(0xFF65676B))),
)),
Padding(
padding: const EdgeInsets.only(bottom: 25),
child: TextFormField(
controller: controller.password,
obscureText: _obscureText,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Por favor ingrese una contraseña';
}
if (value.length <= 6) {
return 'Contraseña muy corta';
}
return null;
},
decoration: InputDecoration(
errorBorder: const OutlineInputBorder(
key: _formKey,
child: Column(
children: [
const Padding(
padding: EdgeInsets.only(bottom: 5),
child: Align(
alignment: Alignment.topLeft,
child: Text('Email',
style: TextStyle(
fontSize: 18.0,
color: Color(0xFF65676B))),
)),
Padding(
padding: const EdgeInsets.only(bottom: 18),
child: TextFormField(
controller: controller.email,
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Por favor ingresa un email';
}
final RegExp emailRegExp = RegExp(
r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegExp.hasMatch(value)) {
return 'Por favor ingresa un email válido';
}
return null;
},
decoration: const InputDecoration(
border: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
Color.fromARGB(255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
border: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
hintText: 'Contraseña',
fillColor: const Color.fromARGB(
255, 239, 239, 239),
hintText: 'Hello@gmail.com',
fillColor:
Color.fromARGB(255, 239, 239, 239),
filled: true,
prefixIcon: const Icon(Icons.lock_outline),
suffixIcon: IconButton(
icon: Icon(
_obscureText
? Icons.visibility
: Icons.visibility_off,
color: Colors.grey,
),
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
prefixIcon: Icon(Icons.email_outlined)),
),
),
const Padding(
padding: EdgeInsets.only(bottom: 5),
child: Align(
alignment: Alignment.topLeft,
child: Text('Password',
style: TextStyle(
fontSize: 18.0,
color: Color(0xFF65676B))),
)),
Padding(
padding: const EdgeInsets.only(bottom: 25),
child: TextFormField(
controller: controller.password,
obscureText: _obscureText,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Por favor ingresa una contraseña';
}
if (value.length <= 6) {
return 'Contraseña muy corta';
}
return null;
},
decoration: InputDecoration(
errorBorder: const OutlineInputBorder(
borderSide: BorderSide(
color:
Color.fromARGB(255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: const OutlineInputBorder(
borderSide:
BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
border: const OutlineInputBorder(
borderSide:
BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
focusedBorder: const OutlineInputBorder(
borderSide:
BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
hintText: 'Contraseña',
fillColor:
const Color.fromARGB(255, 239, 239, 239),
filled: true,
prefixIcon: const Icon(Icons.lock_outline),
suffixIcon: IconButton(
icon: Icon(
_obscureText
? Icons.visibility
: Icons.visibility_off,
color: Colors.grey,
),
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
),
),
],
)),
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment
.center, // Centra horizontalmente
children: <Widget>[
Checkbox(
value: _isChecked,
onChanged: (value) {
setState(() {
_isChecked = value!;
});
},
),
GestureDetector(
onTap: () {
if (kIsWeb) {
_launchURL(
settings?.terminosCondiciones ?? '');
} else {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return WebViewScreen(
label: 'Políticas de privacidad',
link: settings?.terminosCondiciones ??
'',
);
},
),
);
}
},
child: const Text(
'Acepto los términos y condiciones.',
style: TextStyle(
color: Color(0xFF65676B),
decoration: TextDecoration.underline,
),
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(bottom: 25),
child: Center(
@@ -302,6 +374,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
}
},
label: 'Registrarme',
isEnabled: _isChecked,
),
),
),
@@ -407,195 +480,187 @@ class _RegisterScreenState extends State<RegisterScreen> {
horizontal: 0, vertical: 20),
child: Column(
children: [
// Padding(
// padding: const EdgeInsets.only(bottom: 20),
// child: ElevatedButton(
// onPressed: () async {
// await AuthenticationRepository.instance
// .signInWithGoogle();
// },
// style: ElevatedButton.styleFrom(
// backgroundColor: const Color(0xFF2BA4EC),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(50),
// ),
// elevation: 0,
// minimumSize: const Size(230, 60),
// ),
// child: const Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(
// 'Entrar con Google ',
// style: TextStyle(
// color: Colors.white,
// fontWeight: FontWeight.bold,
// fontSize: 18,
// ),
// ),
// SizedBox(width: 5),
// FaIcon(FontAwesomeIcons.google),
// ],
// )),
// ),
// const Padding(
// padding: EdgeInsets.symmetric(vertical: 0),
// child: Row(
// children: <Widget>[
// Expanded(
// child: Divider(
// color: Colors.black38,
// thickness: 1,
// ),
// ),
// Padding(
// padding: EdgeInsets.symmetric(horizontal: 10),
// child: Text("ó"),
// ),
// Expanded(
// child: Divider(
// color: Colors.black38,
// thickness: 1,
// ),
// ),
// ],
// ),
// ),
Form(
key: _formKey,
child: Column(
children: [
const Padding(
padding: EdgeInsets.only(bottom: 5),
child: Align(
alignment: Alignment.topLeft,
child: Text('Email',
style: TextStyle(
fontSize: 18.0,
color: Color(0xFF65676B))),
)),
Padding(
padding: const EdgeInsets.only(bottom: 18),
child: TextFormField(
controller: controller.email,
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Por favor ingrese un email';
}
final RegExp emailRegExp = RegExp(
r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegExp.hasMatch(value)) {
return 'Por favor ingrese un email válido';
}
return null;
},
decoration: const InputDecoration(
border: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color.fromARGB(
255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
hintText: 'Hello@gmail.com',
fillColor: Color.fromARGB(
255, 239, 239, 239),
filled: true,
prefixIcon:
Icon(Icons.email_outlined)),
),
),
const Padding(
padding: EdgeInsets.only(bottom: 5),
child: Align(
alignment: Alignment.topLeft,
child: Text('Password',
style: TextStyle(
fontSize: 18.0,
color: Color(0xFF65676B))),
)),
Padding(
padding: const EdgeInsets.only(bottom: 25),
child: TextFormField(
controller: controller.password,
obscureText: _obscureText,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Por favor ingrese una contraseña';
}
if (value.length <= 6) {
return 'Contraseña muy corta';
}
return null;
},
decoration: InputDecoration(
errorBorder: const OutlineInputBorder(
key: _formKey,
child: Column(
children: [
const Padding(
padding: EdgeInsets.only(bottom: 5),
child: Align(
alignment: Alignment.topLeft,
child: Text('Email',
style: TextStyle(
fontSize: 18.0,
color: Color(0xFF65676B))),
)),
Padding(
padding: const EdgeInsets.only(bottom: 18),
child: TextFormField(
controller: controller.email,
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Por favor ingresa un email';
}
final RegExp emailRegExp = RegExp(
r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegExp.hasMatch(value)) {
return 'Por favor ingresa un email válido';
}
return null;
},
decoration: const InputDecoration(
border: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color.fromARGB(
255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
border: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
hintText: 'Contraseña',
fillColor: const Color.fromARGB(
255, 239, 239, 239),
hintText: 'Hello@gmail.com',
fillColor:
Color.fromARGB(255, 239, 239, 239),
filled: true,
prefixIcon:
const Icon(Icons.lock_outline),
suffixIcon: IconButton(
icon: Icon(
_obscureText
? Icons.visibility
: Icons.visibility_off,
color: Colors.grey,
),
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
prefixIcon: Icon(Icons.email_outlined)),
),
),
const Padding(
padding: EdgeInsets.only(bottom: 5),
child: Align(
alignment: Alignment.topLeft,
child: Text('Password',
style: TextStyle(
fontSize: 18.0,
color: Color(0xFF65676B))),
)),
Padding(
padding: const EdgeInsets.only(bottom: 25),
child: TextFormField(
controller: controller.password,
obscureText: _obscureText,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Por favor ingresa una contraseña';
}
if (value.length <= 6) {
return 'Contraseña muy corta';
}
return null;
},
decoration: InputDecoration(
errorBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color.fromARGB(
255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
border: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
hintText: 'Contraseña',
fillColor: const Color.fromARGB(
255, 239, 239, 239),
filled: true,
prefixIcon:
const Icon(Icons.lock_outline),
suffixIcon: IconButton(
icon: Icon(
_obscureText
? Icons.visibility
: Icons.visibility_off,
color: Colors.grey,
),
onPressed: () {
setState(() {
_obscureText = !_obscureText;
});
},
),
),
),
],
)),
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment
.center, // Centra horizontalmente
children: <Widget>[
Checkbox(
value: _isChecked,
onChanged: (value) {
setState(() {
_isChecked = value!;
});
},
),
GestureDetector(
onTap: () {
if (kIsWeb) {
_launchURL(
settings?.terminosCondiciones ?? '');
} else {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return WebViewScreen(
label: 'Políticas de privacidad',
link: settings
?.terminosCondiciones ??
'',
);
},
),
);
}
},
child: const Text(
'Acepto los términos y condiciones.',
style: TextStyle(
color: Color(0xFF65676B),
decoration: TextDecoration.underline,
),
),
),
],
),
),
Padding(
padding: const EdgeInsets.only(bottom: 25),
child: Center(
@@ -608,6 +673,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
);
}
},
isEnabled: _isChecked,
label: 'Registrarme',
),
),
File diff suppressed because it is too large Load Diff
+1
View File
@@ -75,6 +75,7 @@ class SolicitudScreen extends StatelessWidget {
try {
eventos.addAll(snapshot.data!);
eventos.sort((a, b) => a.range1Hour1.compareTo(b.range1Hour1));
} catch (e) {
print("Error" + e.toString());
}
+3 -9
View File
@@ -19,9 +19,7 @@ class SsupportStateScreen extends State<SupportScreen> {
final String body = '';
Future<void> _sendWhatsapp(String? number) async {
final _whatsappUrl =
'https://wa.me/$number?text=${Uri.parse('Hola! soy usuario de Prosapp y quisiera conocer mas sobre esta app')}';
final _whatsappUrl = 'https://api.whatsapp.com/send?phone=$number&text=Hola%21+soy+usuario+de+Prosapp+y+quisiera+conocer+mas+sobre+esta+app+%F0%9F%98%81';
if (!await launch(_whatsappUrl)) {
throw Exception('Could not launch $_whatsappUrl');
}
@@ -39,14 +37,10 @@ class SsupportStateScreen extends State<SupportScreen> {
}
void _sendEmailWeb(String recipients) async {
final email =
'mailto:$recipients?subject=${Uri.encodeComponent(recipients)}&body=${Uri.encodeComponent(body)}';
final email = 'mailto:$recipients?subject=${Uri.encodeComponent(recipients)}&body=${Uri.encodeComponent(body)}';
if (await canLaunch(email)) {
await launch(email);
} else {
// No se pudo abrir el cliente de correo electrónico
// Puedes mostrar un diálogo o realizar otra acción en este caso
}
} else {}
}
@override
+17 -4
View File
@@ -1,4 +1,6 @@
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:get/get.dart';
class FirebaseMessagingService {
FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
@@ -18,11 +20,22 @@ class FirebaseMessagingService {
String? token = await _firebaseMessaging.getToken();
print('Token FCM: $token');
// Escuchar los mensajes entrantes en la aplicación
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Mensaje FCM recibido: ${message.notification?.title} - ${message.notification?.body}');
// Aquí puedes mostrar la notificación o procesar los datos recibidos según tus necesidades
});
print('Mensaje FCM recibido: ${message.notification?.title} - ${message.notification?.body}');
// Obtener el valor de la clave "screen" de los datos de la notificación
String? notificationScreen = message.data['screen'];
// Navegar a la pantalla correspondiente según el valor de la clave "screen"
if (notificationScreen == "misservicios") {
// Navegar a MyServicesScreen
Get.offNamed('/misservicios');
} else if (notificationScreen == "solicitud") {
// Navegar a SolicitudScreen
Get.offNamed('/solicitud');
}
});
// Manejar la notificación cuando se toca y la aplicación está en primer plano (opcional)
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
@@ -16,6 +16,7 @@ import firebase_storage
import flutter_local_notifications
import geolocator_apple
import path_provider_foundation
import shared_preferences_foundation
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
@@ -30,5 +31,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}
+5 -1
View File
@@ -26,6 +26,7 @@
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
DFAE116AA0A5B93B39BF14BF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = AE1007FC9B2143762CB9DF6D /* GoogleService-Info.plist */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -54,7 +55,7 @@
/* Begin PBXFileReference section */
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* prosappco.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "prosappco.app"; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10ED2044A3C60003C045 /* prosappco.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = prosappco.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
@@ -68,6 +69,7 @@
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
AE1007FC9B2143762CB9DF6D /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -99,6 +101,7 @@
33CEB47122A05771004F2AC0 /* Flutter */,
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
AE1007FC9B2143762CB9DF6D /* GoogleService-Info.plist */,
);
sourceTree = "<group>";
};
@@ -227,6 +230,7 @@
files = (
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
DFAE116AA0A5B93B39BF14BF /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
+1 -1
View File
@@ -1,7 +1,7 @@
{
"file_generated_by": "FlutterFire CLI",
"purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory",
"GOOGLE_APP_ID": "1:245204384533:ios:aeb233e55c5ddf7a9f871c",
"GOOGLE_APP_ID": "1:245204384533:ios:1d58f5624d0df4859f871c",
"FIREBASE_PROJECT_ID": "prosapp-5747a",
"GCM_SENDER_ID": "245204384533"
}
+56
View File
@@ -960,6 +960,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076
url: "https://pub.dev"
source: hosted
version: "2.2.0"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: d29753996d8eb8f7619a1f13df6ce65e34bc107bef6330739ed76f18b22310ef
url: "https://pub.dev"
source: hosted
version: "2.3.3"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d
url: "https://pub.dev"
source: hosted
version: "2.3.0"
simple_gesture_detector:
dependency: transitive
description:
+3 -2
View File
@@ -1,5 +1,5 @@
name: prosappco
description: A new Flutter project.
description: Encuentra personal médico certificado.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
version: 1.1.0+1
environment:
sdk: '>=2.19.3 <3.0.0'
@@ -30,6 +30,7 @@ environment:
dependencies:
flutter:
sdk: flutter
shared_preferences: ^2.0.10
# The following adds the Cupertino Icons font to your application.
+3 -1
View File
@@ -23,7 +23,9 @@
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="prosappco">
<meta name="apple-mobile-web-app-title" content="prosapp">
<meta name="package" content="prosapp"> <!-- Debe coincidir con el nuevo ID de paquete en Firebase -->
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->