This commit is contained in:
Juan Felipe Duarte
2023-05-10 08:43:40 -05:00
parent eeced3ac66
commit 40ee4f2ab2
30 changed files with 1175 additions and 147 deletions
+1
View File
@@ -70,4 +70,5 @@ flutter {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation platform('com.google.firebase:firebase-bom:31.3.0')
implementation 'com.google.firebase:firebase-analytics-ktx'
}
+9
View File
@@ -2,6 +2,12 @@
package="com.example.prosappco">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<queries>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto" />
</intent>
</queries>
<application
android:label="prosapp"
android:name="${applicationName}"
@@ -29,6 +35,9 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+3 -1
View File
@@ -1,3 +1,4 @@
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
@@ -19,6 +20,7 @@ import 'package:prosappco/src/screens/profile.dart';
import 'package:prosappco/src/screens/profile_pro.dart';
import 'package:prosappco/src/screens/register.dart';
import 'package:prosappco/src/screens/request_sent.dart';
import 'package:prosappco/src/screens/score.dart';
import 'package:prosappco/src/screens/service.dart';
import 'package:prosappco/src/screens/solicitudes.dart';
import 'firebase_options.dart';
@@ -64,7 +66,7 @@ class MyApp extends StatelessWidget {
'/profilePro': (context) => const ProfilePro(),
'/calendar': (context) => const CalendarScreen(),
'/solicitud': (context) => SolicitudScreen(),
'/misserviciospro': (context) => MyServicesProScreen(),
'/misserviciospro': (context) => const MyServicesProScreen(),
'/misservicios': (context) => MyServicesScreen(),
},
onGenerateRoute: (settings) {
+34 -4
View File
@@ -6,8 +6,11 @@ import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/photo_view.dart';
import 'package:prosappco/src/models/scores_model.dart';
import 'package:prosappco/src/models/user_model.dart';
import 'package:prosappco/src/screens/configuracion.dart';
import 'package:prosappco/src/screens/messages_user.dart';
import 'package:prosappco/src/screens/profile.dart';
import 'package:prosappco/src/screens/reputation.dart';
import 'package:prosappco/src/screens/support.dart';
class DrawerMenu extends StatefulWidget {
@override
@@ -144,7 +147,16 @@ class _DrawerMenuState extends State<DrawerMenu> {
),
),
ListTile(
onTap: () {},
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return const ConfiguracionScreen();
},
),
);
},
leading: const Icon(
Icons.construction_outlined,
color: Colors.black,
@@ -155,7 +167,16 @@ class _DrawerMenuState extends State<DrawerMenu> {
),
),
ListTile(
onTap: () {},
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return const SupportScreen();
},
),
);
},
leading: const Icon(
Icons.question_mark_rounded,
color: Colors.black,
@@ -227,7 +248,16 @@ class _DrawerMenuState extends State<DrawerMenu> {
color: const Color(0xFFD6F4FF),
child: ListTile(
tileColor: const Color(0xFFD6F4FF),
onTap: () {},
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return const ReputationScreen();
},
),
);
},
trailing: const Icon(Icons.keyboard_arrow_right,
color: Colors.black),
title: const Text(
@@ -255,7 +285,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
),
const SizedBox(width: 5),
Text(
'(${scoresModel?.total.toString()}) ${scoresModel?.average.toString()}'),
'(${scoresModel?.total.toString()}) ${scoresModel?.average.toStringAsFixed(1)}'),
],
),
),
+23 -3
View File
@@ -14,6 +14,8 @@ import 'package:prosappco/src/screens/profile_pro.dart';
import 'package:prosappco/src/screens/reputation.dart';
import '../models/scores_model.dart';
import '../screens/configuracion.dart';
import '../screens/support.dart';
class DrawerProfessional extends StatefulWidget {
@override
@@ -153,7 +155,16 @@ class _DrawerProfessionalState extends State<DrawerProfessional> {
),
),
ListTile(
onTap: () {},
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return const ConfiguracionScreen();
},
),
);
},
leading: const Icon(
Icons.construction_outlined,
color: Colors.black,
@@ -164,7 +175,16 @@ class _DrawerProfessionalState extends State<DrawerProfessional> {
),
),
ListTile(
onTap: () {},
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return const SupportScreen();
},
),
);
},
leading: const Icon(
Icons.question_mark_rounded,
color: Colors.black,
@@ -296,7 +316,7 @@ class _DrawerProfessionalState extends State<DrawerProfessional> {
),
const SizedBox(width: 5),
Text(
'(${scoresModel?.total.toString()}) ${scoresModel?.average.toString()}'),
'(${scoresModel?.total.toString()}) ${scoresModel?.average.toStringAsFixed(1)}'),
],
),
),
+6 -1
View File
@@ -17,6 +17,7 @@ class EventoService {
double latitude,
double longitude,
String status,
int? tarifa,
) async {
try {
DateTime ahora = DateTime.now();
@@ -35,7 +36,8 @@ class EventoService {
'latitude': latitude,
'longitude': longitude,
'status': status,
'Timestamp': ahora
'Timestamp': ahora,
'tarifa': tarifa ?? 0,
}).then((value) {
FirebaseFirestore.instance.collection('users').doc(uid).update({
'services': FieldValue.arrayUnion([value.id])
@@ -135,6 +137,7 @@ class Event {
double? latitud;
String status;
ScoresModel? scoresModel;
int? tarifa;
Event({
this.id,
@@ -150,6 +153,7 @@ class Event {
this.longitud,
this.latitud,
this.status = 'pendiente',
this.tarifa,
});
factory Event.fromJson(Map<String, dynamic> json) {
@@ -167,6 +171,7 @@ class Event {
longitud: json['longitude'] ?? 0,
latitud: json['latitude'] ?? 0,
status: json['status'],
tarifa: json['tarifa'] ?? 0,
);
}
+3 -3
View File
@@ -30,7 +30,7 @@ class ScoresModel {
String userReceived, bool isFromClient, bool userInfo) async {
final receivedScoresQuery = FirebaseFirestore.instance
.collection('scores')
.where('is_from_client', isEqualTo: isFromClient)
.where('is_from_professional', isEqualTo: isFromClient)
.where('to_user', isEqualTo: userReceived);
final receivedScoresSnapshot = await receivedScoresQuery.get();
@@ -48,7 +48,7 @@ class ScoresModel {
String userGiven, bool isFromClient, bool userInfo) async {
final givenScoresQuery = FirebaseFirestore.instance
.collection('scores')
.where('is_from_client', isEqualTo: isFromClient)
.where('is_from_professional', isEqualTo: isFromClient)
.where('from_user', isEqualTo: userGiven);
final givenScoresSnapshot = await givenScoresQuery.get();
@@ -101,7 +101,7 @@ class ScoreDetailModel {
fromUser: data['from_user'],
toUser: data['to_user'],
comment: data['comment'],
isFromClient: data['is_from_client'],
isFromClient: data['is_from_professional'],
name: user?.name ?? "...",
avatar: user?.photo);
} catch (e) {
+42 -5
View File
@@ -3,17 +3,51 @@ import 'package:cloud_firestore/cloud_firestore.dart';
class SettingModel {
final bool domicilios;
final bool tarifas;
final String titulo;
final String parrafo;
final String numero;
final String email;
final String dias;
final String horas;
final String version;
final String proliticasPrivacidad;
final String terminosCondiciones;
SettingModel(this.domicilios, this.tarifas);
SettingModel(
this.domicilios,
this.tarifas,
this.titulo,
this.parrafo,
this.numero,
this.email,
this.dias,
this.horas,
this.version,
this.proliticasPrivacidad,
this.terminosCondiciones,
);
static Future<SettingModel> fromJson(Map<String, dynamic> json) async {
try {
if (json == null) return SettingModel(false, false);
if (json == null)
return SettingModel(false, false, '', '', '', '', '', '', '', '', '');
return SettingModel(json['domicilio'], json['tarifa']);
return SettingModel(
json['domicilio'],
json['tarifa'],
json['titulo_soporte'],
json['parrafo_soporte'],
json['numero_soporte'],
json['email_soporte'],
json['dias_soporte'],
json['horas_soporte'],
json['version'],
json['politicas_privacidad'],
json['terminos_condiciones'],
);
} catch (e) {
print('Error settings: $e');
return SettingModel(false, false);
return SettingModel(false, false, '', '', '', '', '', '', '', '', '');
}
}
@@ -24,11 +58,14 @@ class SettingModel {
.collection('settings')
.doc('global')
.get();
final Map<String, dynamic>? data = snapshot.data();
print('data $data');
return fromJson(data!);
} catch (e) {
print('Error getting settings: $e');
return SettingModel(false, false);
return SettingModel(false, false, '', '', '', '', '', '', '', '', '');
}
}
}
+12 -10
View File
@@ -8,25 +8,27 @@ class UserModel {
final String? profession;
final String? state;
final Reference? photo;
final int? tarifa;
UserModel(this.name, this.city, this.profession, this.state, this.photo);
UserModel(this.name, this.city, this.profession, this.state, this.photo,
this.tarifa);
static Future<UserModel> fromJson(
Map<String, dynamic>? json, String uid) async {
try {
if (json == null) return UserModel('', '', '', null, null);
if (json == null) return UserModel('', '', '', null, null, 0);
String? avatar = json['photo'];
return UserModel(
json['name'],
json['city'],
json['profesion'],
json['estado'],
avatar != null ? storage.ref().child(avatar) : null,
);
json['name'],
json['city'],
json['profesion'],
json['estado'],
avatar != null ? storage.ref().child(avatar) : null,
json['tarifa'] ?? 0);
} catch (e) {
print('XD user $e');
return UserModel('', '', '', null, null);
return UserModel('', '', '', null, null, 0);
}
}
@@ -38,7 +40,7 @@ class UserModel {
return fromJson(data, uid);
} catch (e) {
print('Error getting user: $e');
return UserModel('', '', '', null, null);
return UserModel('', '', '', null, null, 0);
}
}
}
+83
View File
@@ -0,0 +1,83 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/models/setting_model.dart';
import 'package:prosappco/src/screens/web_view.dart';
class AboutScreen extends StatefulWidget {
const AboutScreen({super.key});
@override
State<AboutScreen> createState() => _AboutScreenState();
}
class _AboutScreenState extends State<AboutScreen> {
SettingModel? settings;
@override
void initState() {
super.initState();
if (settings == null) {
SettingModel.getSettings().then(
(SettingModel value) => setState(() {
settings = value;
}),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PopAppbar(
onPressed: () {
Navigator.pop(context);
},
label: 'Acerca de la aplicación'),
body: ListView(
children: [
ListTile(
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return WebViewScreen(
label: 'Políticas de privacidad',
link: settings?.proliticasPrivacidad ?? '',
);
},
),
);
},
title: const Text('Políticas de privacidad'),
trailing:
const Icon(Icons.keyboard_arrow_right, color: Colors.black),
),
ListTile(
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return WebViewScreen(
label: 'Términos y condiciones',
link: settings?.terminosCondiciones ?? '',
);
},
),
);
},
title: const Text('Términos y condiciones'),
trailing:
const Icon(Icons.keyboard_arrow_right, color: Colors.black),
),
ListTile(
title: const Text('Versión de la aplicación'),
subtitle: Text(settings?.version ?? ''),
),
],
),
);
}
}
+36 -16
View File
@@ -5,7 +5,6 @@ import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/components/primary_btn.dart';
import 'package:prosappco/src/models/event_model.dart';
import 'package:prosappco/src/models/scores_model.dart';
import 'package:prosappco/src/screens/cita.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:intl/intl.dart';
@@ -26,7 +25,10 @@ class _CalendarScreenState extends State<CalendarScreen> {
EventoService eventoService = EventoService();
CalendarFormat _calendarFormat = CalendarFormat.month;
DateTime today = DateTime.now();
DateTime now = DateTime.now();
TimeOfDay? _selectedTime1;
TimeOfDay? _selectedTime2;
@@ -283,19 +285,36 @@ class _CalendarScreenState extends State<CalendarScreen> {
),
PrimaryButtom(
onPressed: () async {
final DateTime combinedDate1 = DateTime(
today.year,
today.month,
today.day,
_selectedTime1!.hour,
_selectedTime1!.minute,
);
final DateTime combinedDate2 = DateTime(
today.year,
today.month,
today.day,
_selectedTime2!.hour,
_selectedTime2!.minute,
);
await eventoService
.createEvent(
_titleController.text,
_descriptionController.text,
today.toString(),
_selectedTime1!.format(context).toString(),
_selectedTime2!.format(context).toString(),
uid.toString(),
'',
'',
0,
0,
'aprobado')
_titleController.text,
_descriptionController.text,
DateFormat('yyyy-MM-dd HH:mm:ss.SSS').format(today),
'$combinedDate1',
'$combinedDate2',
uid.toString(),
'',
'',
0,
0,
'aprobado',
0,
)
.then((value) {
Navigator.pop(context);
_titleController.text = '';
@@ -323,8 +342,7 @@ class _CalendarScreenState extends State<CalendarScreen> {
Widget _eventList() {
return FutureBuilder(
future:
getByProId('${DateFormat("yyyy-MM-dd 00:00:00.000").format(today)}Z'),
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');
@@ -355,7 +373,9 @@ class _CalendarScreenState extends State<CalendarScreen> {
),
);
},
leading: Text(e.range1Hour1),
leading: Text(
TimeOfDay.fromDateTime(DateTime.parse(e.range1Hour1))
.format(context)),
title: RichText(
text: TextSpan(
children: [
@@ -402,7 +422,7 @@ class _CalendarScreenState extends State<CalendarScreen> {
),
const SizedBox(width: 5),
Text(
'(${e.scoresModel?.total.toString()}) ${e.scoresModel?.average.toString()}'),
'(${e.scoresModel?.total.toString()}) ${e.scoresModel?.average.toStringAsFixed(1)}'),
],
),
Text(
+1 -7
View File
@@ -37,12 +37,6 @@ class _ChatScreenState extends State<ChatScreen> {
}
}
});
// if (scoresModel == null) {
// ScoresModel.scoreTo(uid.toString(), false, false).then(
// (ScoresModel s) => setState(() => scoresModel = s),
// );
// }
}
@override
@@ -62,7 +56,7 @@ class _ChatScreenState extends State<ChatScreen> {
color: Colors.grey.withOpacity(0.3),
spreadRadius: 2,
blurRadius: 3,
offset: const Offset(0, 2), // changes position of shadow
offset: const Offset(0, 2),
),
],
),
+151 -6
View File
@@ -3,6 +3,7 @@ import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/photo_view.dart';
@@ -11,6 +12,7 @@ import 'package:prosappco/src/models/event_model.dart';
import 'package:prosappco/src/models/scores_model.dart';
import 'package:prosappco/src/models/user_model.dart';
import 'package:prosappco/src/screens/chat.dart';
import 'package:prosappco/src/screens/score.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:community_material_icon/community_material_icon.dart';
@@ -25,13 +27,20 @@ class CitaScreen extends StatefulWidget {
class _CitaScreenState extends State<CitaScreen> {
final uid = AuthenticationRepository.instance.getCurrentUserUid();
UserModel? user;
@override
DateTime today = DateTime.now();
String nombre = '';
int tarifa = 0;
Reference? ref_photo;
ScoresModel? scoresModel;
final phoneNumber = '+573022548060';
String formatCurrency(int number) {
final formatter =
NumberFormat.currency(locale: 'es_CO', decimalDigits: 0, symbol: '');
return '\$${formatter.format(number)}';
}
Future<void> _openMap(double lat, double lng) async {
final Uri _url =
Uri.parse('https://www.google.com/maps/search/?api=1&query=$lat,$lng');
@@ -73,17 +82,25 @@ class _CitaScreenState extends State<CitaScreen> {
@override
Widget build(BuildContext context) {
today.difference(DateTime.parse(widget.evento.range1Hour1));
// DateTime todays = DateFormat('hh:mm a').parse(today.timeZoneName);
final eventDate = DateFormat('yyyy-MM-dd').parse(widget.evento.day);
if (nombre == '') {
if (uid != widget.evento.userId) {
UserModel.getUser(widget.evento.userId).then((value) {
setState(() {
nombre = value.name;
ref_photo = value.photo;
UserModel.getUser(uid.toString()).then((me) {
setState(() {
tarifa = me.tarifa ?? 0;
nombre = value.name;
ref_photo = value.photo;
});
});
});
} else {
UserModel.getUser(widget.evento.professionalId).then((value) {
setState(() {
tarifa = value.tarifa ?? 0;
nombre = value.name;
ref_photo = value.photo;
});
@@ -122,7 +139,7 @@ class _CitaScreenState extends State<CitaScreen> {
),
TextSpan(
text:
'${DateFormat('dd MMM', 'es').format(DateTime.parse(widget.evento.day))} ${widget.evento.range1Hour1}',
'${DateFormat('dd MMM', 'es').format(DateTime.parse(widget.evento.day))} ${TimeOfDay.fromDateTime(DateTime.parse(widget.evento.range1Hour1)).format(context)}',
style: const TextStyle(
color: Colors.grey,
fontSize: 16,
@@ -151,7 +168,7 @@ class _CitaScreenState extends State<CitaScreen> {
),
const SizedBox(width: 5),
Text(
'(${scoresModel?.total.toString()}) ${scoresModel?.average.toString()}'),
'(${scoresModel?.total.toString()}) ${scoresModel?.average.toStringAsFixed(1)}'),
],
),
),
@@ -194,6 +211,18 @@ class _CitaScreenState extends State<CitaScreen> {
],
),
),
Column(
children: [
Text(
formatCurrency(tarifa),
style: const TextStyle(
fontWeight: FontWeight.w600, fontSize: 25),
),
Text('Tarifa consulta ${widget.evento.tarifa}',
style: const TextStyle(fontSize: 15)),
],
),
const SizedBox(height: 15),
Text(
textAlign: TextAlign.center,
'" ${widget.evento.description} "',
@@ -367,6 +396,80 @@ class _CitaScreenState extends State<CitaScreen> {
],
),
),
widget.evento.status == 'aprobado'
? eventDate.year == today.year &&
eventDate.month == today.month &&
eventDate.day == today.day
? (DateTime.now()
.difference(
DateTime.parse(widget.evento.range1Hour1))
.abs() <=
const Duration(minutes: 30))
? Padding(
padding: const EdgeInsets.only(bottom: 30),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 20),
child: ElevatedButton(
onPressed: () {
FirebaseFirestore.instance
.collection("services")
.doc('${widget.evento.id}')
.update({"status": "iniciado"});
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
elevation: 0,
minimumSize: const Size(230, 60),
),
child: const Text(
'Iniciar servicio',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
),
ElevatedButton(
onPressed: () {
FirebaseFirestore.instance
.collection("services")
.doc('${widget.evento.id}')
.update({"status": "denegado"}).then(
(value) {
Navigator.pushReplacementNamed(
context, '/solicitud');
});
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFEC2B2B),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
elevation: 0,
minimumSize: const Size(230, 60),
),
child: const Text(
'Cancelar servicio',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
],
),
)
: const SizedBox()
: const SizedBox()
: const SizedBox(),
widget.evento.status == 'pendiente'
? Padding(
padding: const EdgeInsets.only(bottom: 30),
@@ -433,6 +536,48 @@ class _CitaScreenState extends State<CitaScreen> {
),
)
: const SizedBox(),
widget.evento.status == 'iniciado'
? Padding(
padding: const EdgeInsets.only(bottom: 30),
child: Column(
children: [
ElevatedButton(
onPressed: () {
FirebaseFirestore.instance
.collection("services")
.doc('${widget.evento.id}')
.update({"status": "terminado"}).then((value) {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return ScoreScreen(evento: widget.evento);
},
),
);
});
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
elevation: 0,
minimumSize: const Size(230, 60),
),
child: const Text(
'Terminar servicio',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
],
),
)
: const SizedBox(),
],
),
);
+36
View File
@@ -0,0 +1,36 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/screens/about.dart';
class ConfiguracionScreen extends StatelessWidget {
const ConfiguracionScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PopAppbar(
onPressed: () {
Navigator.pop(context);
},
label: 'Configuración'),
body: ListTile(
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return const AboutScreen();
},
),
);
},
title: const Text('Acerca de la aplicación'),
trailing: const Icon(
Icons.keyboard_arrow_right,
color: Colors.black,
),
),
);
}
}
+52 -13
View File
@@ -1,10 +1,13 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:intl/intl.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/drawer_professional.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/models/event_model.dart';
import 'package:prosappco/src/models/scores_model.dart';
import 'package:prosappco/src/screens/cita.dart';
class MyServicesScreen extends StatefulWidget {
@@ -16,6 +19,7 @@ class MyServicesScreen extends StatefulWidget {
class _MyServicesScreenState extends State<MyServicesScreen> {
DateTime today = DateTime.now();
final uid = AuthenticationRepository.instance.getCurrentUserUid();
@override
void initState() {
@@ -44,10 +48,32 @@ class _MyServicesScreenState extends State<MyServicesScreen> {
}
Widget _eventList() {
return FutureBuilder(
future: getByUserIdAll('aprobado', 'denegado'),
return StreamBuilder<List<Event>>(
stream: FirebaseFirestore.instance
.collection('services')
.where('user_id', isEqualTo: uid)
.where('status',
whereIn: ['aprobado', 'denegado', 'iniciado', 'terminado'])
.snapshots()
.asyncMap((snapshot) async {
try {
List<Event> eventos = [];
for (var element in snapshot.docs) {
final event = Event.fromJson(element.data());
event.scoresModel =
await ScoresModel.scoreTo(event.userId, false, false);
event.id = element.id;
eventos.add(event);
}
print('eventos $eventos');
return eventos;
} catch (e) {
print('Error getByProId $e');
return [];
}
}),
builder: (BuildContext context, AsyncSnapshot<List<Event>> snapshot) {
if (!snapshot.hasData) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
@@ -67,13 +93,14 @@ class _MyServicesScreenState extends State<MyServicesScreen> {
child: Center(child: Text('No tienes citas')),
);
}
return Column(
children: [
...eventos.map(
(event) => ListTile(
tileColor: event.status == 'aprobado'
? Colors.blue[100]
: Colors.red[100],
tileColor: event.status == 'denegado'
? Colors.red[100]
: Colors.blue[100],
onTap: () {
Navigator.push(
context,
@@ -90,11 +117,23 @@ class _MyServicesScreenState extends State<MyServicesScreen> {
color: Colors.blue,
size: 40,
)
: const Icon(
Icons.close,
color: Colors.red,
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: [
@@ -108,7 +147,7 @@ class _MyServicesScreenState extends State<MyServicesScreen> {
),
TextSpan(
text:
'${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${event.range1Hour1}',
'${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,
@@ -141,7 +180,7 @@ class _MyServicesScreenState extends State<MyServicesScreen> {
),
const SizedBox(width: 5),
Text(
'(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toString()}'),
'(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'),
],
),
Text(
+56 -24
View File
@@ -1,13 +1,17 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:intl/intl.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/drawer_professional.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/models/event_model.dart';
import 'package:prosappco/src/models/scores_model.dart';
import 'package:prosappco/src/screens/cita.dart';
class MyServicesProScreen extends StatefulWidget {
MyServicesProScreen({super.key});
const MyServicesProScreen({super.key});
@override
State<MyServicesProScreen> createState() => _MyServicesProScreenState();
@@ -15,6 +19,7 @@ class MyServicesProScreen extends StatefulWidget {
class _MyServicesProScreenState extends State<MyServicesProScreen> {
DateTime today = DateTime.now();
final uid = AuthenticationRepository.instance.getCurrentUserUid();
@override
void initState() {
@@ -43,10 +48,32 @@ class _MyServicesProScreenState extends State<MyServicesProScreen> {
}
Widget _eventList() {
return FutureBuilder(
future: getByProIdAll('aprobado', 'denegado'),
return StreamBuilder<List<Event>>(
stream: FirebaseFirestore.instance
.collection('services')
.where('professional_id', isEqualTo: uid)
.where('status',
whereIn: ['aprobado', 'denegado', 'iniciado', 'terminado'])
.snapshots()
.asyncMap((snapshot) async {
try {
List<Event> eventos = [];
for (var element in snapshot.docs) {
final event = Event.fromJson(element.data());
event.scoresModel =
await ScoresModel.scoreTo(event.userId, false, false);
event.id = element.id;
eventos.add(event);
}
print('eventos $eventos');
return eventos;
} catch (e) {
print('Error getByProId $e');
return [];
}
}),
builder: (BuildContext context, AsyncSnapshot<List<Event>> snapshot) {
if (!snapshot.hasData) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
@@ -66,24 +93,23 @@ class _MyServicesProScreenState extends State<MyServicesProScreen> {
child: Center(child: Text('No tienes citas')),
);
}
return Column(
children: [
...eventos.map(
(event) => ListTile(
tileColor: event.status == 'aprobado'
? Colors.blue[100]
: Colors.red[100],
tileColor: event.status == 'denegado'
? Colors.red[100]
: Colors.blue[100],
onTap: () {
print(event.id);
// Navigator.push(
// context,
// CupertinoPageRoute(
// builder: (BuildContext context) {
// return CitaScreen(evento: event);
// },
// ),
// );
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return CitaScreen(evento: event);
},
),
);
},
leading: event.status == 'aprobado'
? const Icon(
@@ -91,11 +117,17 @@ class _MyServicesProScreenState extends State<MyServicesProScreen> {
color: Colors.blue,
size: 40,
)
: const Icon(
Icons.close,
color: Colors.red,
size: 40,
),
: event.status == 'iniciado'
? const Icon(
Icons.access_time,
color: Colors.blue,
size: 40,
)
: const Icon(
Icons.close,
color: Colors.red,
size: 40,
),
title: RichText(
text: TextSpan(
children: [
@@ -109,7 +141,7 @@ class _MyServicesProScreenState extends State<MyServicesProScreen> {
),
TextSpan(
text:
'${DateFormat('dd MMM', 'es').format(DateTime.parse(event.day))} - ${event.range1Hour1}',
'${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,
@@ -142,7 +174,7 @@ class _MyServicesProScreenState extends State<MyServicesProScreen> {
),
const SizedBox(width: 5),
Text(
'(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toString()}'),
'(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'),
],
),
Text(
+14 -12
View File
@@ -36,6 +36,7 @@ class Professional {
final double longitude;
final List<String> professionalEspecializado;
final ScoresModel scores;
final int? tarifa;
Professional({
required this.id,
@@ -49,6 +50,7 @@ class Professional {
required this.realAddress,
required this.latitude,
required this.longitude,
this.tarifa,
});
String getEspecializaciones() {
@@ -90,18 +92,18 @@ class Professional {
.toList();
Professional professional = Professional(
id: user.id,
name: data['name'],
professionName: data['profesion'],
cityName: data['city'],
professionalRef: storage.ref().child(photo),
professionalEspecializado: especializaciones,
ubicacion: data['ubicacion'] ?? '',
realAddress: data['address'] ?? '',
latitude: data['latitude'] ?? 0,
longitude: data['longitude'] ?? 0,
scores: await ScoresModel.scoreFrom(uid, true, true),
);
id: user.id,
name: data['name'],
professionName: data['profesion'],
cityName: data['city'],
professionalRef: storage.ref().child(photo),
professionalEspecializado: especializaciones,
ubicacion: data['ubicacion'] ?? '',
realAddress: data['address'] ?? '',
latitude: data['latitude'] ?? 0,
longitude: data['longitude'] ?? 0,
scores: await ScoresModel.scoreFrom(uid, true, true),
tarifa: data['tarifa'] ?? 0);
return professional;
}
} catch (e) {
+1 -1
View File
@@ -170,7 +170,7 @@ class _ProfessionalInfoScreenState extends State<ProfessionalInfoScreen> {
),
const SizedBox(width: 5),
Text(
'(${widget.professional.scores.total.toString()}) ${widget.professional.scores.average.toString()}'),
'(${widget.professional.scores.total.toString()}) ${widget.professional.scores.average.toStringAsFixed(1)}'),
],
),
),
+107
View File
@@ -0,0 +1,107 @@
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/photo_view.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/models/scores_model.dart';
class ReputationProScreen extends StatefulWidget {
const ReputationProScreen({
super.key,
});
@override
State<ReputationProScreen> createState() => _ReputationProScreenState();
}
class _ReputationProScreenState extends State<ReputationProScreen> {
ScoresModel? scoresModel;
@override
void initState() {
super.initState();
final uid = AuthenticationRepository.instance.getCurrentUserUid();
if (scoresModel == null) {
ScoresModel.scoreFrom(uid.toString(), false, true).then(
(ScoresModel s) => setState(() => scoresModel = s),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PopAppbar(
onPressed: () {
Navigator.pop(context);
},
label: 'Reputación'),
body: SingleChildScrollView(
child: Column(
children: [
..._scoresList(scoresModel?.details ?? []),
],
)),
);
}
List<Widget> _scoresList(List<ScoreDetailModel> list) {
return list.map((e) => _scoreItem(e)).toList();
}
Widget _scoreItem(ScoreDetailModel scoreDetails) {
return ListTile(
onTap: () {},
leading: ReferencePhoto(
ref: scoreDetails.avatar,
size: 50,
sizeCircle: 50,
sizeIcon: 35,
),
title: Row(
children: [
RatingBar.builder(
initialRating: scoreDetails.score,
minRating: 1,
direction: Axis.horizontal,
allowHalfRating: true,
itemCount: 5,
itemSize: 22,
maxRating: 5,
itemPadding: const EdgeInsets.symmetric(horizontal: 0),
itemBuilder: (context, _) => const Icon(
Icons.star,
color: Color(0xFF2BA4EC),
),
onRatingUpdate: (rating) {},
ignoreGestures: true,
),
Text(
' (${scoreDetails.score})',
style: const TextStyle(color: Colors.black54, fontSize: 13),
)
],
),
subtitle: Row(
children: [
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: '${scoreDetails.name}, ',
style: const TextStyle(fontSize: 15, color: Colors.black),
),
TextSpan(
text: '"${scoreDetails.comment}"',
style: const TextStyle(fontSize: 15, color: Colors.grey),
),
],
),
),
),
],
));
}
}
+2 -3
View File
@@ -4,10 +4,9 @@ import 'package:prosappco/src/authentication/authentication_repository.dart';
import 'package:prosappco/src/components/photo_view.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/models/scores_model.dart';
import 'package:prosappco/src/models/user_model.dart';
class ReputationScreen extends StatefulWidget {
ReputationScreen({
const ReputationScreen({
super.key,
});
@@ -24,7 +23,7 @@ class _ReputationScreenState extends State<ReputationScreen> {
final uid = AuthenticationRepository.instance.getCurrentUserUid();
if (scoresModel == null) {
ScoresModel.scoreTo(uid.toString(), true, true).then(
ScoresModel.scoreTo(uid.toString(), false, true).then(
(ScoresModel s) => setState(() => scoresModel = s),
);
}
+168
View File
@@ -0,0 +1,168 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:intl/intl.dart';
import 'package:prosappco/src/components/photo_view.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/models/event_model.dart';
import 'package:prosappco/src/models/user_model.dart';
class ScoreScreen extends StatefulWidget {
final Event evento;
const ScoreScreen({super.key, required this.evento});
@override
State<ScoreScreen> createState() => ScoreScreenState();
}
class ScoreScreenState extends State<ScoreScreen> {
TextEditingController commentController = TextEditingController();
Reference? ref_photo;
String nombre = '';
double _rating = 1.0;
@override
Widget build(BuildContext context) {
if (nombre == '') {
if (uid != widget.evento.userId) {
UserModel.getUser(widget.evento.userId).then((value) {
UserModel.getUser(uid.toString()).then((me) {
setState(() {
nombre = value.name;
ref_photo = value.photo;
});
});
});
} else {
UserModel.getUser(widget.evento.professionalId).then((value) {
setState(() {
nombre = value.name;
ref_photo = value.photo;
});
});
}
}
return Scaffold(
appBar: PopAppbar(onPressed: () {}, label: 'Puntuación'),
body: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: ListTile(
leading: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: ReferencePhoto(
ref: ref_photo,
size: 50,
sizeCircle: 50,
sizeIcon: 35,
),
),
title: Text(
nombre,
style: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 16),
),
subtitle: Text(
'${DateFormat('dd MMM', 'es').format(DateTime.parse(widget.evento.day))} ${TimeOfDay.fromDateTime(DateTime.parse(widget.evento.range1Hour1)).format(context)}'),
),
),
RatingBar.builder(
initialRating: _rating,
minRating: 1,
direction: Axis.horizontal,
allowHalfRating: true,
itemCount: 5,
itemSize: 40,
glow: false,
maxRating: 5,
itemPadding: const EdgeInsets.symmetric(horizontal: 5),
itemBuilder: (context, _) => const Icon(
Icons.star,
color: Color(0xFF2BA4EC),
),
onRatingUpdate: (rating) {
setState(() {
_rating = rating;
});
},
ignoreGestures: false,
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 40),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Comentario',
style: TextStyle(fontSize: 18),
),
TextFormField(
maxLines: null,
keyboardType: TextInputType
.multiline, // establece el tipo de teclado como "multilinea"
controller: commentController,
),
],
),
),
),
Padding(
padding: const EdgeInsets.only(bottom: 30),
child: Column(
children: [
ElevatedButton(
onPressed: () {
if (widget.evento.professionalId == uid) {
FirebaseFirestore.instance.collection("scores").add({
"comment": commentController.text,
"from_user": uid,
"is_from_professional": false,
"score": _rating,
"to_user": widget.evento.userId,
}).then((value) {
// Navigator.pushReplacementNamed(context, '/score');
});
} else {
FirebaseFirestore.instance.collection("scores").add({
"comment": commentController.text,
"from_user": uid,
"is_from_professional": true,
"score": _rating,
"to_user": widget.evento.professionalId,
}).then((value) {
// Navigator.pushReplacementNamed(context, '/score');
});
}
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2BA4EC),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
elevation: 0,
minimumSize: const Size(230, 60),
),
child: const Text(
'Enviar',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
],
),
),
],
),
);
}
}
+31 -25
View File
@@ -1,5 +1,5 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:community_material_icon/community_material_icon.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
@@ -35,6 +35,7 @@ class _ServiceScreenState extends State<ServiceScreen> {
final _serviceTypeController = TextEditingController();
final _observacionController = TextEditingController();
String professionalId = '';
int professionalTarifa = 0;
String professionalAddress = '';
String professionalUbicacion = '';
double? professionalLatitude;
@@ -130,6 +131,7 @@ class _ServiceScreenState extends State<ServiceScreen> {
final FirebaseStorage storage = FirebaseStorage.instance;
final uid = AuthenticationRepository.instance.getCurrentUserUid();
final fcmToken = FirebaseMessaging.instance.getToken();
BitmapDescriptor? _markerIcon;
@@ -411,6 +413,9 @@ class _ServiceScreenState extends State<ServiceScreen> {
professionalId = profesional.id;
professionalTarifa =
profesional.tarifa ?? 0;
professionalUbicacion =
profesional.ubicacion;
@@ -549,35 +554,36 @@ class _ServiceScreenState extends State<ServiceScreen> {
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
int tiempo2 = ((_selectedTime!.hour * 3600 +
_selectedTime!.minute * 60) +
7200) %
86400;
final DateTime combinedDate = DateTime(
_selectedDate!.year,
_selectedDate!.month,
_selectedDate!.day,
_selectedTime!.hour,
_selectedTime!.minute);
int hora2 = tiempo2 ~/ 3600;
int minutos2 = (tiempo2 % 3600) ~/ 60;
TimeOfDay time2 =
TimeOfDay(hour: hora2, minute: minutos2);
DateTime time2 =
combinedDate.add(const Duration(hours: 2));
UserModel.getUser(uid.toString()).then((value) {
eventoService
.createEvent(
value.name,
_observacionController.text,
'${_selectedDate}Z',
_selectedTime!.format(context),
time2.format(context),
professionalId,
ubicacion,
_locationController.text,
ubicacion != 'sitio'
? coordinates.latitude
: professionalLatitude,
ubicacion != 'sitio'
? coordinates.longitude
: professionalLongitude,
'pendiente')
value.name,
_observacionController.text,
'$_selectedDate',
'$combinedDate',
'$time2',
professionalId,
ubicacion,
_locationController.text,
ubicacion != 'sitio'
? coordinates.latitude
: professionalLatitude,
ubicacion != 'sitio'
? coordinates.longitude
: professionalLongitude,
'pendiente',
professionalTarifa,
)
.then((value) {
Navigator.pushReplacement(
context,
+30 -4
View File
@@ -1,9 +1,11 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:intl/intl.dart';
import 'package:prosappco/src/components/drawer_professional.dart';
import 'package:prosappco/src/models/event_model.dart';
import 'package:prosappco/src/models/scores_model.dart';
import 'package:prosappco/src/screens/cita.dart';
class SolicitudScreen extends StatefulWidget {
@@ -50,8 +52,29 @@ class _SolicitudScreenState extends State<SolicitudScreen> {
}
Widget _eventList() {
return FutureBuilder(
future: getByProIdAll('pendiente', ''),
return StreamBuilder<List<Event>>(
stream: FirebaseFirestore.instance
.collection('services')
.where('professional_id', isEqualTo: uid)
.where('status', whereIn: ['pendiente', ''])
.snapshots()
.asyncMap((snapshot) async {
try {
List<Event> eventos = [];
for (var element in snapshot.docs) {
final event = Event.fromJson(element.data());
event.scoresModel =
await ScoresModel.scoreTo(event.userId, false, false);
event.id = element.id;
eventos.add(event);
}
print('eventos $eventos');
return eventos;
} catch (e) {
print('Error getByProId $e');
return [];
}
}),
builder: (BuildContext context, AsyncSnapshot<List<Event>> snapshot) {
if (!snapshot.hasData) {
return const Center(
@@ -87,7 +110,10 @@ class _SolicitudScreenState extends State<SolicitudScreen> {
),
);
},
leading: Text(event.range1Hour1),
leading: Text(
TimeOfDay.fromDateTime(DateTime.parse(event.range1Hour1))
.format(context),
),
title: RichText(
text: TextSpan(
children: [
@@ -134,7 +160,7 @@ class _SolicitudScreenState extends State<SolicitudScreen> {
),
const SizedBox(width: 5),
Text(
'(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toString()}'),
'(${event.scoresModel?.total.toString()}) ${event.scoresModel?.average.toStringAsFixed(1)}'),
],
),
Text(
+145
View File
@@ -0,0 +1,145 @@
import 'package:community_material_icon/community_material_icon.dart';
import 'package:flutter/material.dart';
import 'package:flutter_email_sender/flutter_email_sender.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:prosappco/src/models/setting_model.dart';
import 'package:url_launcher/url_launcher.dart';
class SupportScreen extends StatefulWidget {
const SupportScreen({super.key});
@override
State<SupportScreen> createState() => SsupportStateScreen();
}
class SsupportStateScreen extends State<SupportScreen> {
SettingModel? settings;
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')}';
if (!await launch(_whatsappUrl)) {
throw Exception('Could not launch $_whatsappUrl');
}
}
Future<void> _sendEmail(String recipients) async {
final Email email = Email(
body: '',
subject: 'Soporte Prosapp',
recipients: [recipients],
isHTML: false,
);
await FlutterEmailSender.send(email);
}
@override
void initState() {
super.initState();
if (settings == null) {
SettingModel.getSettings().then(
(SettingModel value) => setState(() {
settings = value;
}),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PopAppbar(
onPressed: () {
Navigator.pop(context);
},
label: 'Soporte'),
body: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 30, left: 35, right: 35),
child: Text(
'${settings?.titulo}',
style: const TextStyle(fontWeight: FontWeight.w600),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 35),
child: Text('${settings?.parrafo}'),
),
Row(
children: [
const Expanded(child: SizedBox()),
ElevatedButton(
onPressed: () {
_sendWhatsapp(settings?.numero);
},
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: const Color(0xFF2BA4EC),
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: Colors.white,
),
),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () {
_sendEmail(settings?.email ?? '');
},
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: const Color(0xFF2BA4EC),
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.email_outlined,
size: 30,
color: Colors.white,
),
),
),
const Expanded(child: SizedBox()),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 50),
child: Column(
children: [
const Text(
'Horario de atención:',
style: TextStyle(fontWeight: FontWeight.w600),
),
const SizedBox(height: 20),
Text('${settings?.dias}'),
const SizedBox(height: 5),
Text('${settings?.horas}'),
],
),
),
],
),
);
}
}
+47
View File
@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:prosappco/src/components/pop_appbar.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebViewScreen extends StatefulWidget {
String label;
String link;
WebViewScreen({super.key, required this.label, required this.link});
@override
State<WebViewScreen> createState() => _WebViewScreenState();
}
class _WebViewScreenState extends State<WebViewScreen> {
@override
Widget build(BuildContext context) {
final controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
// Update loading bar.
},
onPageStarted: (String url) {},
onPageFinished: (String url) {},
onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
),
)
..loadRequest(Uri.parse('${widget.link}'));
return Scaffold(
appBar: PopAppbar(
onPressed: () {
Navigator.pop(context);
},
label: widget.label,
),
body: WebViewWidget(controller: controller),
);
}
}
@@ -8,6 +8,7 @@ import Foundation
import cloud_firestore
import firebase_auth
import firebase_core
import firebase_messaging
import firebase_storage
import geolocator_apple
import url_launcher_macos
@@ -16,6 +17,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin"))
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
FLTFirebaseStoragePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseStoragePlugin"))
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
+72 -8
View File
@@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: _flutterfire_internals
sha256: f175bc1414e4edf8c5b83372c98eeabecf8353f39c9da423c2cfdf1f1f508788
sha256: "867b77e2367bc502dcd4d5a66302615409f04eb20ed82ba1c0ba073f9107e018"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
version: "1.2.0"
async:
dependency: transitive
description:
@@ -141,26 +141,50 @@ packages:
dependency: "direct main"
description:
name: firebase_core
sha256: ed611fb8e67e43ecc7956f242cecca383d87cf71aace27287aa5dd4bdba4ac07
sha256: dcf54c170c5371ad0e79229d0fb372c58262ae0968b7de222bf28e51dd236be0
url: "https://pub.dev"
source: hosted
version: "2.9.0"
version: "2.11.0"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
sha256: "0df0a064ab0cad7f8836291ca6f3272edd7b83ad5b3540478ee46a0849d8022b"
sha256: ae79f335f6c7f2dadb00c98c429da2ca905d265e0225fb5e7dfa62ac3accad48
url: "https://pub.dev"
source: hosted
version: "4.6.0"
version: "4.7.0"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
sha256: "347351a8f0518f3343d79a9a0690fa67ad232fc32e2ea270677791949eac792b"
sha256: e57ef862257a0d977c1308d02e2fbb9b68525e6d85711b08f3df8cec836fb444
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.4.0"
firebase_messaging:
dependency: "direct main"
description:
name: firebase_messaging
sha256: "88a4bf569899344d863db26a73092681b590274f570f0a431051b92c4dd9bee3"
url: "https://pub.dev"
source: hosted
version: "14.5.0"
firebase_messaging_platform_interface:
dependency: transitive
description:
name: firebase_messaging_platform_interface
sha256: "482fa6998f6cb10b30324ca8bfa0e2fcc0defed9484ccccce118b1b347e39a24"
url: "https://pub.dev"
source: hosted
version: "4.4.0"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
sha256: "61f7d323085704fbcc7b056a49d75211b2eb698610b878fec6cda18dd04d754f"
url: "https://pub.dev"
source: hosted
version: "3.4.0"
firebase_storage:
dependency: "direct main"
description:
@@ -198,6 +222,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.1+1"
flutter_email_sender:
dependency: "direct main"
description:
name: flutter_email_sender
sha256: "9e253c69617f43d4cb5de672e93a7a19c12a21fb6a75e66c6ce7626336c4c1bc"
url: "https://pub.dev"
source: hosted
version: "5.2.0"
flutter_lints:
dependency: "direct dev"
description:
@@ -725,6 +757,38 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
webview_flutter:
dependency: "direct main"
description:
name: webview_flutter
sha256: "1a37bdbaaf5fbe09ad8579ab09ecfd473284ce482f900b5aea27cf834386a567"
url: "https://pub.dev"
source: hosted
version: "4.2.0"
webview_flutter_android:
dependency: transitive
description:
name: webview_flutter_android
sha256: "1acea8def62592123e2fbbca164ed8681a98a890bdcbb88f916d5b4a22687759"
url: "https://pub.dev"
source: hosted
version: "3.7.0"
webview_flutter_platform_interface:
dependency: transitive
description:
name: webview_flutter_platform_interface
sha256: "78715dc442b7849dbde74e92bb67de1cecf5addf95531c5fb474e72f5fe9a507"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
webview_flutter_wkwebview:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: "61f33512810bf1ee9ac89761a4b02663ff64e8227b7dc80654642acd660fd49d"
url: "https://pub.dev"
source: hosted
version: "3.4.2"
sdks:
dart: ">=2.19.3 <3.0.0"
flutter: ">=3.3.0"
+4 -1
View File
@@ -56,8 +56,11 @@ dependencies:
google_maps_flutter: ^2.2.5
geolocator: ^9.0.2
geocoding: ^2.1.0
url_launcher: ^6.0.3
url_launcher: ^6.1.10
community_material_icon: ^5.9.55
flutter_email_sender: ^5.2.0
webview_flutter: ^4.2.0
firebase_messaging: ^14.5.0
dev_dependencies:
flutter_test:
@@ -6,10 +6,13 @@
#include "generated_plugin_registrant.h"
#include <firebase_core/firebase_core_plugin_c_api.h>
#include <geolocator_windows/geolocator_windows.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FirebaseCorePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
GeolocatorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("GeolocatorWindows"));
UrlLauncherWindowsRegisterWithRegistrar(
+1
View File
@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
firebase_core
geolocator_windows
url_launcher_windows
)