diff --git a/lib/src/components/banner_photo.dart b/lib/src/components/banner_photo.dart new file mode 100644 index 0000000..b3b5fac --- /dev/null +++ b/lib/src/components/banner_photo.dart @@ -0,0 +1,155 @@ +import 'dart:io'; + +import 'package:firebase_storage/firebase_storage.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:prosappco/src/components/column_padding.dart'; + +const double photoSize = 150; + +class ReferenceBannerPhoto extends StatelessWidget { + Reference? ref; + double size; + double sizeCircle; + + ReferenceBannerPhoto({ + super.key, + required this.ref, + this.size = photoSize, + this.sizeCircle = photoSize, + }); + + Future downloadImage() async { + try { + if (ref != null) { + final imageData = await ref!.getData(); + if (imageData != null) { + return Image.memory( + imageData, + height: size, + fit: BoxFit.fill, + ); + } + } + // ignore: empty_catches + } catch (e) {} + + return DefaultPhoto( + sizeDefault: sizeCircle, + ); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: downloadImage(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + // mientras la llamada asíncrona está en proceso, muestra un mensaje de carga + return DefaultPhoto(); + } else if (snapshot.connectionState == ConnectionState.done && + snapshot.hasData) { + return snapshot.data!; + } else { + return DefaultPhoto(); + } + }); + } +} + +class DefaultPhoto extends StatelessWidget { + double sizeDefault; + DefaultPhoto({ + super.key, + this.sizeDefault = photoSize, + }); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.15), + blurRadius: 5, + offset: const Offset(0, 1), + ), + ], + ), + height: 150, + child: ColumnPadding( + alineacion: MainAxisAlignment.spaceAround, + padding: const EdgeInsets.symmetric(horizontal: 70), + children: [ + Container( + decoration: BoxDecoration( + color: const Color(0xFFD6F4FF), + borderRadius: BorderRadius.circular(50), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.3), + spreadRadius: 2, + blurRadius: 5, + offset: const Offset(0, 3), // changes position of shadow + ), + ], + ), + constraints: const BoxConstraints(minWidth: 250, minHeight: 50), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + Expanded( + child: Center( + child: Text( + 'Foto portada', + style: TextStyle( + color: Color(0xFF2BA4EC), + fontSize: 17, + ), + ), + ), + ), + Padding( + padding: EdgeInsets.only(right: 15), + child: Icon( + Icons.file_upload_outlined, + color: Color(0xFF2BA4EC), + size: 30, + ), + ), + ], + ), + ), + ], + ), + ); + } +} + +class LocalPhoto extends StatelessWidget { + File file; + LocalPhoto({super.key, required this.file}); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.15), + blurRadius: 8, + offset: const Offset(0, 2), + ), + ], + ), + child: Image.file( + file, + width: double.infinity, + height: photoSize, + fit: BoxFit.fill, + ), + ); + } +} diff --git a/lib/src/components/column_padding.dart b/lib/src/components/column_padding.dart index d0e7337..fffe675 100644 --- a/lib/src/components/column_padding.dart +++ b/lib/src/components/column_padding.dart @@ -3,15 +3,19 @@ import 'package:flutter/material.dart'; class ColumnPadding extends StatelessWidget { final List children; final EdgeInsetsGeometry padding; + final MainAxisAlignment alineacion; const ColumnPadding({ super.key, required this.children, required this.padding, + required this.alineacion, }); @override Widget build(BuildContext context) { - return Padding(padding: padding, child: Column(children: children)); + return Padding( + padding: padding, + child: Column(mainAxisAlignment: alineacion, children: children)); } } diff --git a/lib/src/screens/code_validation.dart b/lib/src/screens/code_validation.dart index 9fdd70c..df89178 100644 --- a/lib/src/screens/code_validation.dart +++ b/lib/src/screens/code_validation.dart @@ -42,6 +42,7 @@ class CodeValidationScreen extends StatelessWidget { ], ), ColumnPadding( + alineacion: MainAxisAlignment.start, padding: const EdgeInsets.symmetric(horizontal: 25), children: [ const SizedBox(height: 10), diff --git a/lib/src/screens/horario.dart b/lib/src/screens/horario.dart new file mode 100644 index 0000000..3846d33 --- /dev/null +++ b/lib/src/screens/horario.dart @@ -0,0 +1,238 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:prosappco/src/components/pop_appbar.dart'; + +class HorarioScreen extends StatefulWidget { + const HorarioScreen({super.key}); + + @override + State createState() => _HorarioScreenState(); +} + +class _HorarioScreenState extends State { + bool lunesValue = false; + bool martesValue = false; + bool miercolesValue = false; + bool juevesValue = false; + bool viernesValue = false; + bool sabadoValue = false; + bool domingoValue = false; + bool jornadaContinuaLunes = false; + + TimeOfDay? _selectedTime; + + Future _selectTime(BuildContext context) async { + final TimeOfDay? pickedTime = await showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + // builder: (context, child) { + // return Theme(data: ThemeData.dark(), child: child!); + // }, + ); + if (pickedTime != null) { + setState(() { + _selectedTime = pickedTime; + }); + } + } + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Scaffold( + appBar: PopAppbar( + onPressed: () { + Navigator.pop(context); + }, + label: 'Horario'), + body: SingleChildScrollView( + child: Column(children: [ + const Divider(color: Colors.white, height: 10), + customSwitch( + 'Lunes', + lunesValue, + (value) { + lunesValue = value; + }, + ), + lunesValue + ? Column( + children: [ + customSwitch( + 'Jornada continua', + jornadaContinuaLunes, + (value) { + jornadaContinuaLunes = value; + }, + ), + Padding( + padding: const EdgeInsets.only(left: 30, bottom: 15), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: TextFormField( + onTap: () { + _selectTime(context); + }, + readOnly: true, + decoration: const InputDecoration( + hintText: 'Hora', + ), + controller: TextEditingController( + text: _selectedTime == null + ? '' + : ' ${_selectedTime!.format(context)}', + ), + style: const TextStyle(fontSize: 15), + ), + ), + const Text(' - '), + SizedBox( + width: 80, + child: TextFormField( + onTap: () { + _selectTime(context); + }, + readOnly: true, + decoration: const InputDecoration( + hintText: 'Hora', + ), + controller: TextEditingController( + text: _selectedTime == null + ? '' + : ' ${_selectedTime!.format(context)}'), + ), + ), + const Text(' - '), + SizedBox( + width: 80, + child: TextFormField( + onTap: () { + _selectTime(context); + }, + readOnly: true, + decoration: const InputDecoration( + hintText: 'Hora', + ), + controller: TextEditingController( + text: _selectedTime == null + ? '' + : ' ${_selectedTime!.format(context)}'), + ), + ), + const Text(' - '), + SizedBox( + width: 80, + child: TextFormField( + onTap: () { + _selectTime(context); + }, + readOnly: true, + decoration: const InputDecoration( + hintText: 'Hora', + ), + controller: TextEditingController( + text: _selectedTime == null + ? '' + : ' ${_selectedTime!.format(context)}'), + ), + ), + ], + ).animate().moveY( + duration: const Duration(milliseconds: 100)), + ], + ) + .animate() + .moveY(duration: const Duration(milliseconds: 100)), + ), + ], + ) + : const SizedBox(), + const Divider(height: 10), + customSwitch( + 'Martes', + martesValue, + (value) { + martesValue = value; + }, + ), + const Divider(height: 10), + customSwitch( + 'Miercoles', + miercolesValue, + (value) { + miercolesValue = value; + }, + ), + const Divider(height: 10), + customSwitch( + 'Jueves', + juevesValue, + (value) { + juevesValue = value; + }, + ), + const Divider(height: 10), + customSwitch( + 'Viernes', + viernesValue, + (value) { + viernesValue = value; + }, + ), + const Divider(height: 10), + customSwitch( + 'Sabado', + sabadoValue, + (value) { + sabadoValue = value; + }, + ), + const Divider(height: 10), + customSwitch( + 'Domingo', + domingoValue, + (value) { + domingoValue = value; + }, + ), + ]), + ), + ), + ); + } + + customSwitch(String text, bool switchValue, ValueChanged onChanged) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 30), + child: SizedBox( + height: 40, + child: Row( + children: [ + Expanded( + child: Text( + text, + style: const TextStyle( + fontSize: 15, + color: Colors.black, + ), + )), + Transform.scale( + scale: 1.2, + child: Switch( + value: switchValue, + onChanged: (bool newValue) { + setState(() { + onChanged(newValue); + }); + }, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/screens/professional_profile.dart b/lib/src/screens/professional_profile.dart index 4f72e11..34dbe51 100644 --- a/lib/src/screens/professional_profile.dart +++ b/lib/src/screens/professional_profile.dart @@ -493,7 +493,7 @@ class ProfessionalProfileScreenState extends State { children: [ TextFormField( controller: _cedulaController, - decoration: InputDecoration( + decoration: const InputDecoration( prefixIcon: Icon(Icons.person_outline), hintText: 'Cedula (Obligatorio)'), ), @@ -502,34 +502,34 @@ class ProfessionalProfileScreenState extends State { onPressed: () { _showChoiceDialogCedula(context); }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFD6F4FF), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(50), + ), + elevation: 0, + minimumSize: const Size(250, 50), + ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( + const Text( 'Cedula', style: TextStyle( color: Color(0xFF2BA4EC), fontSize: 17, ), ), - SizedBox(width: 15), + const SizedBox(width: 15), Icon( image_cedula != null ? Icons.check : Icons.file_upload_outlined, - color: Color(0xFF2BA4EC), + color: const Color(0xFF2BA4EC), size: 30, ), ], ), - style: ElevatedButton.styleFrom( - backgroundColor: Color(0xFFD6F4FF), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(50), - ), - elevation: 0, - minimumSize: Size(250, 50), - ), ), SizedBox(height: _space), TextFormField( diff --git a/lib/src/screens/profile.dart b/lib/src/screens/profile.dart index 5b1f409..74591d0 100644 --- a/lib/src/screens/profile.dart +++ b/lib/src/screens/profile.dart @@ -382,9 +382,7 @@ class _ProfileScreenState extends State { prefixIcon: Icon(Icons.person_outline), hintText: 'Nombre (Obligatorio)'), ), - const SizedBox( - height: - 20.0), // Agrega espacio vertical entre los TextFormFields + const SizedBox(height: 20.0), _email != null ? TextFormField( onTap: () { diff --git a/lib/src/screens/profile_pro.dart b/lib/src/screens/profile_pro.dart index 4f08e31..a99e073 100644 --- a/lib/src/screens/profile_pro.dart +++ b/lib/src/screens/profile_pro.dart @@ -1,5 +1,17 @@ +import 'dart:io'; + +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:prosappco/src/authentication/authentication_repository.dart'; +import 'package:prosappco/src/components/banner_photo.dart'; +import 'package:prosappco/src/components/column_padding.dart'; import 'package:prosappco/src/components/drawer_professional.dart'; +import 'package:prosappco/src/components/primary_btn.dart'; +import 'package:prosappco/src/screens/horario.dart'; +import 'package:prosappco/src/screens/professional.dart'; +import 'package:prosappco/src/services/select_image_profile.dart'; class ProfilePro extends StatefulWidget { const ProfilePro({super.key}); @@ -9,14 +21,80 @@ class ProfilePro extends StatefulWidget { } class _ProfileProState extends State { + File? image_portada; + File? imagen_to_upload; + + bool domicilioValue = false; + bool sitioValue = false; + + var _photo = '...'; + @override void initState() { super.initState(); + + final uid = AuthenticationRepository.instance.getCurrentUserUid(); + late final FirebaseAuth _auth; + _auth = FirebaseAuth.instance; + + if (_photo == '...') { + AuthenticationRepository.instance.getPhoto(uid.toString()).then( + (String s) => setState( + () { + _photo = s; + }, + ), + ); + } + } + + Future _showChoiceDialog(BuildContext context) async { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + content: SingleChildScrollView( + child: ListBody( + children: [ + GestureDetector( + child: const Text( + textAlign: TextAlign.center, + "Tomar foto", + style: TextStyle(color: Color(0xFF2BA4EC)), + ), + onTap: () async { + final imagen = await getImage(1); + setState(() { + imagen_to_upload = File(imagen[0]!.path); + }); + Navigator.of(context).pop(); + }, + ), + const Divider(color: Colors.black54), + GestureDetector( + child: const Text( + textAlign: TextAlign.center, + "Abrir Galería", + style: TextStyle(color: Color(0xFF2BA4EC)), + ), + onTap: () async { + final imagen = await getImage(2); + setState(() { + imagen_to_upload = File(imagen[0]!.path); + }); + Navigator.of(context).pop(); + }, + ), + ], + ), + ), + ); + }, + ); } @override Widget build(BuildContext context) { - bool switchValue = false; return SafeArea( child: Scaffold( appBar: AppBar( @@ -32,17 +110,207 @@ class _ProfileProState extends State { ), ), drawer: DrawerProfessional(), - body: Center( - child: Switch( - value: switchValue, - onChanged: (bool newValue) { - setState(() { - switchValue = newValue; - }); - }, + body: SingleChildScrollView( + reverse: true, + child: Column( + children: [ + GestureDetector( + onTap: () { + _showChoiceDialog(context); + }, + child: Container( + child: (imagen_to_upload != null) + ? LocalPhoto( + file: imagen_to_upload!, + ) + : ReferenceBannerPhoto( + ref: storage.ref().child('_photo'), + ), + ), + ), + const Divider( + color: Colors.white, + height: 12, + ), + customSwitch( + 'Servicio a domicilio', + domicilioValue, + (value) { + domicilioValue = value; + }, + ), + const Divider(), + customSwitch( + 'Servicio en sitio', + sitioValue, + (value) { + sitioValue = value; + }, + ), + sitioValue + ? Padding( + padding: const EdgeInsets.only( + left: 40, right: 40, bottom: 15), + child: Column( + children: [ + TextFormField( + // controller: _emailController, + decoration: const InputDecoration( + prefixIcon: Icon(Icons.near_me), + hintText: 'Dirección'), + ), + const SizedBox(height: 10), + TextFormField( + // controller: _emailController, + decoration: const InputDecoration( + hintText: 'Oficina / Piso / Conjunto'), + ), + ], + ) + .animate() + .moveY(duration: const Duration(milliseconds: 100)), + ) + : const SizedBox(), + const Divider(), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 30), + child: SizedBox( + width: double.infinity, + child: Text( + 'Horario estandar', + style: TextStyle( + color: Colors.black, + fontSize: 15, + ), + ), + ), + ), + GestureDetector( + onTap: () { + Navigator.push( + context, + CupertinoPageRoute( + builder: (BuildContext context) { + return const HorarioScreen(); + }, + ), + ); + }, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Row( + children: const [ + Expanded( + child: Padding( + padding: EdgeInsets.only(left: 30), + child: Text( + 'Dia', + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.w500), + ), + ), + ), + Padding( + padding: EdgeInsets.only(right: 170), + child: Text( + 'Hora', + style: TextStyle( + color: Colors.black, + fontWeight: FontWeight.w500), + ), + ), + Padding( + padding: EdgeInsets.only(right: 35), + child: Icon(Icons.edit_outlined), + ), + ], + ), + ), + Padding( + padding: sitioValue + ? const EdgeInsets.only(bottom: 30) + : const EdgeInsets.only(bottom: 70), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(left: 30), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Text('Lunes'), + Text('Martes'), + Text('Miércoles'), + Text('Jueves'), + Text('Viernes'), + Text('Sábado'), + Text('Domingo'), + ], + ), + ), + Padding( + padding: const EdgeInsets.only(left: 35), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Text('N/A'), + Text('N/A'), + Text('N/A'), + Text('N/A'), + Text('N/A'), + Text('N/A'), + Text('N/A'), + ], + ), + ), + ], + ), + ), + ], + ), + ), + PrimaryButtom(onPressed: () {}, label: 'Guardar'), + const SizedBox( + height: 20, + ) + ], ), ), ), ); } + + customSwitch(String text, bool switchValue, ValueChanged onChanged) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 30), + child: SizedBox( + height: 40, + child: Row( + children: [ + Expanded( + child: Text( + text, + style: const TextStyle( + fontSize: 15, + color: Colors.black, + ), + )), + Transform.scale( + scale: 1.2, + child: Switch( + value: switchValue, + onChanged: (bool newValue) { + setState(() { + onChanged(newValue); + }); + }, + ), + ), + ], + ), + ), + ); + } } diff --git a/lib/src/screens/service.dart b/lib/src/screens/service.dart index b3afb96..dd6579b 100644 --- a/lib/src/screens/service.dart +++ b/lib/src/screens/service.dart @@ -57,49 +57,20 @@ class _ServiceScreenState extends State { final FirebaseStorage storage = FirebaseStorage.instance; final uid = AuthenticationRepository.instance.getCurrentUserUid(); - var _ciudad = ''; - var _photo = '...'; - var _estado = '...'; @override void initState() { super.initState(); - - if (_estado == '...') { - AuthenticationRepository.instance - .getState(uid.toString()) - .then((String s) => setState(() { - _estado = s; - })); - } - - if (_ciudad == '') { - AuthenticationRepository.instance - .getCity(uid.toString()) - .then((String s) => setState(() { - _ciudad = s; - })); - } - - if (_photo == '...') { - AuthenticationRepository.instance - .getPhoto(uid.toString()) - .then((String s) => setState(() { - _photo = s; - })); - } } @override Widget build(BuildContext context) { - final User? user = FirebaseAuth.instance.currentUser; - List opc = ["Servicio", "Opción 1", "Opción 2", "Opción 3"]; String defaultValue = opc[0]; return SafeArea( child: Scaffold( - backgroundColor: Color(0xFFD6F4FF), + backgroundColor: const Color(0xFFD6F4FF), drawer: DrawerMenu(), body: SingleChildScrollView( reverse: true,