This commit is contained in:
Felipe
2024-02-20 17:11:43 -05:00
parent 29d18e93e0
commit e00772f58a
21 changed files with 386 additions and 279 deletions
+43
View File
@@ -0,0 +1,43 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:user_repository/user_repository.dart';
part 'auth_event.dart';
part 'auth_state.dart';
class AuthBloc extends Bloc<AuthEvent, AuthState> {
final UserRepository _userRepository;
AuthBloc({required UserRepository userRepository})
: _userRepository = userRepository,
super(AuthStateInitial()) {
on<AuthEventLoginOAuth>(_onAuthEventLoginOAuth);
on<AuthEventVerifyOAuth>(_onAuthEventVerifyOAuth);
}
void _onAuthEventLoginOAuth(
AuthEventLoginOAuth event, Emitter<AuthState> emit) async {
emit(AuthStateProcess());
try {
await _userRepository.signInWithPhoneNumber(event.phone);
emit(const AuthStateVerifyOAuth(false));
} catch (e) {
emit(const AuthStateFailure());
}
}
void _onAuthEventVerifyOAuth(
AuthEventVerifyOAuth event, Emitter<AuthState> emit) async {
emit(AuthStateProcess());
try {
final bool isVerified = await _userRepository.verifyOTP(event.code);
(isVerified)
? emit(AuthStateSuccess())
: emit(const AuthStateVerifyOAuth(true));
} catch (e) {
emit(const AuthStateFailure());
}
}
}
+20
View File
@@ -0,0 +1,20 @@
part of 'auth_bloc.dart';
abstract class AuthEvent extends Equatable {
const AuthEvent();
@override
List<Object> get props => [];
}
class AuthEventLoginOAuth extends AuthEvent {
final String phone;
const AuthEventLoginOAuth({required this.phone});
}
class AuthEventVerifyOAuth extends AuthEvent {
final String code;
const AuthEventVerifyOAuth({required this.code});
}
+26
View File
@@ -0,0 +1,26 @@
part of 'auth_bloc.dart';
abstract class AuthState extends Equatable {
const AuthState();
@override
List<Object> get props => [];
}
class AuthStateInitial extends AuthState {}
class AuthStateProcess extends AuthState {}
class AuthStateVerifyOAuth extends AuthState {
final bool isWrongCode;
const AuthStateVerifyOAuth(this.isWrongCode);
}
class AuthStateSuccess extends AuthState {}
class AuthStateFailure extends AuthState {
final String? message;
const AuthStateFailure({this.message});
}
+1 -1
View File
@@ -18,7 +18,7 @@ class SignUpBloc extends Bloc<SignUpEvent, SignUpState> {
SignUpRequired event, Emitter<SignUpState> emit) async { SignUpRequired event, Emitter<SignUpState> emit) async {
emit(SignUpProcess()); emit(SignUpProcess());
try { try {
MyUser user = await _userRepository.signUp(event.user, event.password); MyUser user = await _userRepository.signUp(event.email, event.password);
await _userRepository.setUserData(user); await _userRepository.setUserData(user);
emit(SignUpSuccess()); emit(SignUpSuccess());
} catch (e) { } catch (e) {
+3 -2
View File
@@ -8,8 +8,9 @@ abstract class SignUpEvent extends Equatable {
} }
class SignUpRequired extends SignUpEvent { class SignUpRequired extends SignUpEvent {
final MyUser user; final String email;
final String password; final String password;
const SignUpRequired(this.user, this.password); const SignUpRequired({required this.email, required this.password});
// const SignUpRequired(this.email, this.password);
} }
+2 -2
View File
@@ -22,11 +22,11 @@ class GeneralDrawerHeader extends StatelessWidget {
); );
}, },
title: Text( title: Text(
context.read<MyUserBloc>().state.user!.name, context.read<MyUserBloc>().state.user!.name ?? '',
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: FontWeight.bold),
), ),
subtitle: Text( subtitle: Text(
context.read<MyUserBloc>().state.user!.email, context.read<MyUserBloc>().state.user!.email ?? '',
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
), ),
leading: context.read<MyUserBloc>().state.user!.picture == "" leading: context.read<MyUserBloc>().state.user!.picture == ""
@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
class GeneralInputDecoration {
static InputDecoration getCustomDecoration({
required BuildContext context,
required String hintText,
String? labelText,
Widget? suffixIcon,
Widget? prefixIcon,
String? errorMsg,
}) {
Color? borderColor = errorMsg != null ? Colors.red : Colors.grey;
return InputDecoration(
labelText: labelText,
suffixIcon: suffixIcon,
prefixIcon: prefixIcon,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: borderColor),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: BorderSide(color: Theme.of(context).colorScheme.primary),
),
errorBorder: OutlineInputBorder(
// Border when there's an error
borderRadius: BorderRadius.circular(20),
borderSide: BorderSide(color: Theme.of(context).colorScheme.error),
),
focusedErrorBorder: OutlineInputBorder(
// Border when focused with error
borderRadius: BorderRadius.circular(20),
borderSide: BorderSide(color: Theme.of(context).colorScheme.error),
),
fillColor: Colors.grey.shade200,
filled: true,
hintText: hintText,
hintStyle: TextStyle(color: Colors.grey[500]),
errorText: errorMsg,
);
}
}
+9 -16
View File
@@ -1,7 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:prosappco/components/general_input_decoration.dart';
class MyTextField extends StatelessWidget { class MyTextField extends StatelessWidget {
final TextEditingController controller; final TextEditingController controller;
final String? labelText;
final String hintText; final String hintText;
final bool obscureText; final bool obscureText;
final TextInputType keyboardType; final TextInputType keyboardType;
@@ -15,6 +17,7 @@ class MyTextField extends StatelessWidget {
const MyTextField( const MyTextField(
{super.key, {super.key,
this.labelText,
required this.controller, required this.controller,
required this.hintText, required this.hintText,
required this.obscureText, required this.obscureText,
@@ -38,23 +41,13 @@ class MyTextField extends StatelessWidget {
onTap: onTap, onTap: onTap,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
onChanged: onChanged, onChanged: onChanged,
decoration: InputDecoration( decoration: GeneralInputDecoration.getCustomDecoration(
suffixIcon: suffixIcon, labelText: labelText,
prefixIcon: prefixIcon, context: context,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Colors.transparent),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(20),
borderSide:
BorderSide(color: Theme.of(context).colorScheme.secondary),
),
fillColor: Colors.grey.shade200,
filled: true,
hintText: hintText, hintText: hintText,
hintStyle: TextStyle(color: Colors.grey[500]), prefixIcon: prefixIcon,
errorText: errorMsg, errorMsg: errorMsg,
suffixIcon: suffixIcon,
), ),
); );
} }
+4
View File
@@ -1,5 +1,6 @@
import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_auth/firebase_auth.dart';
import 'package:injector/injector.dart'; import 'package:injector/injector.dart';
import 'package:prosappco/blocs/auth_bloc/auth_bloc.dart';
import 'package:prosappco/blocs/authentication_bloc/authentication_bloc.dart'; import 'package:prosappco/blocs/authentication_bloc/authentication_bloc.dart';
import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart'; import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart';
import 'package:prosappco/blocs/profile_bloc/profile_bloc.dart'; import 'package:prosappco/blocs/profile_bloc/profile_bloc.dart';
@@ -32,5 +33,8 @@ class UserDI {
injector.registerDependency<SettingBloc>( injector.registerDependency<SettingBloc>(
(() => SettingBloc(userRepository: injector.get<UserRepository>()))); (() => SettingBloc(userRepository: injector.get<UserRepository>())));
injector.registerDependency<AuthBloc>(
(() => AuthBloc(userRepository: injector.get<UserRepository>())));
} }
} }
@@ -0,0 +1,102 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_otp_text_field/flutter_otp_text_field.dart';
import 'package:injector/injector.dart';
import 'package:prosappco/blocs/auth_bloc/auth_bloc.dart';
class OtpAuthScreen extends StatefulWidget {
final String phoneNumber;
const OtpAuthScreen({super.key, required this.phoneNumber});
@override
State<OtpAuthScreen> createState() => _OtpAuthScreenState();
}
class _OtpAuthScreenState extends State<OtpAuthScreen> {
bool _isRequestSent = false;
@override
Widget build(BuildContext context) {
final authBloc = Injector.appInstance.get<AuthBloc>();
if (!_isRequestSent) {
authBloc.add(AuthEventLoginOAuth(phone: widget.phoneNumber));
_isRequestSent = true;
}
return BlocProvider<AuthBloc>(
create: (context) => authBloc,
child: BlocConsumer<AuthBloc, AuthState>(
listener: (context, state) {
if (state is AuthStateSuccess) {
Navigator.of(context).pop();
}
if (state is AuthStateVerifyOAuth) {
if (state.isWrongCode) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('El Código es incorrecto'),
));
}
}
},
builder: (context, state) {
return Scaffold(
body: Column(children: [
getContent(state, authBloc: authBloc),
]),
);
},
),
);
}
Widget getContent(AuthState state, {required AuthBloc authBloc}) {
if (state is AuthStateInitial) {
return const Text('Inicio');
} else if (state is AuthStateProcess) {
return Center(
child: Column(
children: [
Text('${widget.phoneNumber} - $_isRequestSent'),
OtpTextField(
numberOfFields: 6,
borderColor: const Color(0xFF512DA8),
showFieldAsBox: true,
onCodeChanged: (String code) {},
onSubmit: (String verificationCode) {
authBloc.add(AuthEventVerifyOAuth(code: verificationCode));
}, // end onSubmit
),
],
),
);
} else if (state is AuthStateVerifyOAuth) {
return Center(
child: Column(
children: [
Text('${widget.phoneNumber} - $_isRequestSent'),
state.isWrongCode ? const Text('') : const Text(''),
OtpTextField(
numberOfFields: 6,
borderColor: const Color(0xFF512DA8),
showFieldAsBox: true,
onCodeChanged: (String code) {},
onSubmit: (String verificationCode) {
authBloc.add(AuthEventVerifyOAuth(code: verificationCode));
}, // end onSubmit
),
],
),
);
} else if (state is AuthStateSuccess) {
return const CircularProgressIndicator();
} else if (state is AuthStateFailure) {
return const Text('');
} else {
return const CircularProgressIndicator();
}
}
}
@@ -53,7 +53,10 @@ class _SignInScreenState extends State<SignInScreen> {
hintText: 'Email', hintText: 'Email',
obscureText: false, obscureText: false,
keyboardType: TextInputType.emailAddress, keyboardType: TextInputType.emailAddress,
prefixIcon: const Icon(CupertinoIcons.mail_solid), prefixIcon: Icon(
CupertinoIcons.mail_solid,
color: Colors.grey[600],
),
errorMsg: _errorMsg, errorMsg: _errorMsg,
validator: (val) { validator: (val) {
if (val!.isEmpty) { if (val!.isEmpty) {
@@ -72,7 +75,8 @@ class _SignInScreenState extends State<SignInScreen> {
hintText: 'Password', hintText: 'Password',
obscureText: obscurePassword, obscureText: obscurePassword,
keyboardType: TextInputType.visiblePassword, keyboardType: TextInputType.visiblePassword,
prefixIcon: const Icon(CupertinoIcons.lock_fill), prefixIcon:
Icon(CupertinoIcons.lock_fill, color: Colors.grey[600]),
errorMsg: _errorMsg, errorMsg: _errorMsg,
validator: (val) { validator: (val) {
if (val!.isEmpty) { if (val!.isEmpty) {
@@ -93,7 +97,7 @@ class _SignInScreenState extends State<SignInScreen> {
} }
}); });
}, },
icon: Icon(iconPassword), icon: Icon(iconPassword, color: Colors.grey[600]),
), ),
), ),
), ),
+26 -26
View File
@@ -58,32 +58,32 @@ class _SignScreenState extends State<SignScreen> with TickerProviderStateMixin {
), ),
), ),
), ),
TabBar( // TabBar(
controller: tabController, // controller: tabController,
unselectedLabelColor: // unselectedLabelColor:
Theme.of(context).colorScheme.onBackground, // Theme.of(context).colorScheme.onBackground,
labelColor: Theme.of(context).colorScheme.onBackground, // labelColor: Theme.of(context).colorScheme.onBackground,
tabs: const [ // tabs: const [
Padding( // Padding(
padding: EdgeInsets.all(12.0), // padding: EdgeInsets.all(12.0),
child: Text( // child: Text(
'Inicia sesión', // 'Inicia sesión',
style: TextStyle( // style: TextStyle(
fontSize: 18, // fontSize: 18,
), // ),
), // ),
), // ),
Padding( // Padding(
padding: EdgeInsets.all(12.0), // padding: EdgeInsets.all(12.0),
child: Text( // child: Text(
'Registrate', // 'Registrate',
style: TextStyle( // style: TextStyle(
fontSize: 18, // fontSize: 18,
), // ),
), // ),
), // ),
], // ],
), // ),
], ],
), ),
), ),
@@ -197,8 +197,9 @@ class _SignUpScreenState extends State<SignUpScreen> {
SizedBox( SizedBox(
width: MediaQuery.of(context).size.width * 0.9, width: MediaQuery.of(context).size.width * 0.9,
child: MyTextField( child: MyTextField(
labelText: 'Nombre',
controller: nameController, controller: nameController,
hintText: 'Name', hintText: 'Ingresa tu nombre',
obscureText: false, obscureText: false,
keyboardType: TextInputType.name, keyboardType: TextInputType.name,
prefixIcon: const Icon(CupertinoIcons.person_fill), prefixIcon: const Icon(CupertinoIcons.person_fill),
@@ -226,7 +227,9 @@ class _SignUpScreenState extends State<SignUpScreen> {
setState(() { setState(() {
context.read<SignUpBloc>().add(SignUpRequired( context.read<SignUpBloc>().add(SignUpRequired(
myUser, passwordController.text)); email: emailController.text,
password: passwordController.text,
));
}); });
} }
}, },
+33 -174
View File
@@ -3,14 +3,18 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:intl_phone_field/intl_phone_field.dart'; import 'package:intl_phone_field/intl_phone_field.dart';
import 'package:prosappco/components/general_input_decoration.dart';
import 'package:prosappco/screens/authentication/otp_auth_screen.dart';
import 'package:prosappco/screens/authentication/sign_screen.dart'; import 'package:prosappco/screens/authentication/sign_screen.dart';
class WelcomeScreen extends StatelessWidget { class WelcomeScreen extends StatelessWidget {
const WelcomeScreen({Key? key}); const WelcomeScreen({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width; double width = MediaQuery.of(context).size.width;
String? _phoneNumber;
String? _errorMsg;
return Scaffold( return Scaffold(
backgroundColor: Theme.of(context).colorScheme.tertiary, backgroundColor: Theme.of(context).colorScheme.tertiary,
@@ -70,6 +74,22 @@ class WelcomeScreen extends StatelessWidget {
inputFormatters: [ inputFormatters: [
FilteringTextInputFormatter.digitsOnly FilteringTextInputFormatter.digitsOnly
], ],
validator: (value) {
if (value == null) {
return 'Ingresa un numero';
} else {
return null;
}
},
onChanged: (phone) {
_phoneNumber = phone.completeNumber;
},
decoration:
GeneralInputDecoration.getCustomDecoration(
context: context,
hintText: 'Ingresa tu numero',
errorMsg: _errorMsg,
),
), ),
), ),
Text( Text(
@@ -82,15 +102,19 @@ class WelcomeScreen extends StatelessWidget {
), ),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
Navigator.push( final phoneNumber = _phoneNumber;
context,
CupertinoPageRoute( if (phoneNumber != null && phoneNumber.isNotEmpty) {
builder: (context) => Navigator.push(
const SignScreen(initialIndex: 1), context,
), CupertinoPageRoute(
); builder: (context) =>
OtpAuthScreen(phoneNumber: phoneNumber),
),
);
} else {}
}, },
child: const Text('Iniciar Sesión'), child: const Text('Enviar código'),
), ),
TextButton( TextButton(
onPressed: () { onPressed: () {
@@ -148,170 +172,5 @@ class WelcomeScreen extends StatelessWidget {
], ],
), ),
); );
// Scaffold(
// backgroundColor: Theme.of(context).colorScheme.surface,
// body: Column(
// children: [
// Container(
// color: Theme.of(context).colorScheme.tertiary,
// child: Column(
// children: [
// const SizedBox(height: 20),
// Center(
// child: Image(
// width: width * 0.7,
// image: const AssetImage('images/logo_prosapp.png'),
// ),
// ),
// const SizedBox(height: 20),
// ],
// ),
// ),
// SizedBox(
// width: double.infinity,
// child: Text(
// 'Iniciar sesión',
// style: TextStyle(
// // fontSize: 30,
// fontSize: width * 0.08,
// fontWeight: FontWeight.bold,
// ),
// ),
// ),
// Expanded(
// child: SingleChildScrollView(
// child: Column(
// children: [
// const SizedBox(height: 10),
// SizedBox(
// width: double.infinity,
// child: Text(
// 'Numero de celular',
// style: TextStyle(
// fontSize: width * 0.045,
// color: Theme.of(context).colorScheme.onBackground,
// ),
// ),
// ),
// Form(
// child: IntlPhoneField(
// initialCountryCode: 'CO',
// keyboardType: TextInputType.number,
// inputFormatters: [FilteringTextInputFormatter.digitsOnly],
// ),
// ),
// Text(
// 'Un código será enviado a este numero de celular.',
// textAlign: TextAlign.center,
// style: TextStyle(
// fontSize: 13.0,
// color: Theme.of(context).colorScheme.onBackground,
// ),
// ),
// TextButton(
// onPressed: () {
// Navigator.push(
// context,
// CupertinoPageRoute(
// builder: (context) => SignScreen(initialIndex: 0),
// ),
// );
// },
// child: Text(
// 'Inicia sesión con tu correo electrónico',
// style: TextStyle(
// fontSize: width * 0.04,
// ),
// ),
// ),
// RichText(
// text: TextSpan(
// style: const TextStyle(
// fontSize: 16.0,
// color: Color(0xFF65676B),
// fontFamily: 'Poppins',
// ),
// children: [
// const TextSpan(text: '¿No estás registrado? '),
// TextSpan(
// text: 'Regístrate',
// style: TextStyle(
// fontSize: width * 0.04,
// color: Colors.blue,
// fontWeight: FontWeight.w600,
// ),
// recognizer: TapGestureRecognizer()
// ..onTap = () {
// Navigator.push(
// context,
// CupertinoPageRoute(
// builder: (context) =>
// SignScreen(initialIndex: 1),
// ),
// );
// },
// ),
// ],
// ),
// ),
// ],
// ),
// ),
// ),
// ],
// ),
// );
// Scaffold(
// backgroundColor: Theme.of(context).colorScheme.surface,
// body: ListView(
// children: [
// Container(
// color: Theme.of(context).colorScheme.tertiary,
// padding: const EdgeInsets.symmetric(vertical: 20.0),
// child: Center(
// child: Image(
// width: width * 0.7,
// image: const AssetImage('images/logo_prosapp.png'),
// ),
// ),
// ),
// Container(
// decoration: const BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.only(
// topLeft: Radius.circular(60),
// topRight: Radius.circular(60),
// ),
// ),
// child: Padding(
// padding: const EdgeInsets.all(20.0),
// child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// const TextField(
// decoration: InputDecoration(
// labelText: 'Usuario',
// border: OutlineInputBorder(),
// ),
// ),
// const SizedBox(height: 20.0),
// const TextField(
// decoration: InputDecoration(
// labelText: 'Contraseña',
// border: OutlineInputBorder(),
// ),
// obscureText: true,
// ),
// const SizedBox(height: 20.0),
//
// ],
// ),
// ),
// ),
// ],
// ),
// );
} }
} }
+1 -1
View File
@@ -39,7 +39,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
), ),
body: BlocBuilder<MyUserBloc, MyUserState>(builder: (context, state) { body: BlocBuilder<MyUserBloc, MyUserState>(builder: (context, state) {
if (state.status == MyUserStatus.success) { if (state.status == MyUserStatus.success) {
_nameController.text = state.user!.name; _nameController.text = state.user!.name ?? '';
return Padding( return Padding(
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
@@ -58,6 +58,13 @@ class AuthenticationRepository extends GetxController {
); );
} }
Future<bool> verifyOTP(String otp) async {
var credentials = await _auth.signInWithCredential(
PhoneAuthProvider.credential(
verificationId: verificationId.value, smsCode: otp));
return credentials.user != null ? true : false;
}
Future<void> updatePhoneNumber(String verificationId, String smsCode) async { Future<void> updatePhoneNumber(String verificationId, String smsCode) async {
try { try {
PhoneAuthCredential credential = PhoneAuthProvider.credential( PhoneAuthCredential credential = PhoneAuthProvider.credential(
@@ -69,13 +76,6 @@ class AuthenticationRepository extends GetxController {
} }
} }
Future<bool> verifyOTP(String otp) async {
var credentials = await _auth.signInWithCredential(
PhoneAuthProvider.credential(
verificationId: verificationId.value, smsCode: otp));
return credentials.user != null ? true : false;
}
Future<void> createUserWithEmailAndPassword( Future<void> createUserWithEmailAndPassword(
String email, String password) async { String email, String password) async {
try { try {
@@ -2,14 +2,14 @@ import 'package:equatable/equatable.dart';
class MyUserEntity extends Equatable { class MyUserEntity extends Equatable {
final String id; final String id;
final String email; final String? email;
final String name; final String? name;
final String? picture; final String? picture;
const MyUserEntity({ const MyUserEntity({
required this.id, required this.id,
required this.email, this.email,
required this.name, this.name,
this.picture, this.picture,
}); });
@@ -25,8 +25,8 @@ class MyUserEntity extends Equatable {
static MyUserEntity fromDocument(Map<String, dynamic> doc) { static MyUserEntity fromDocument(Map<String, dynamic> doc) {
return MyUserEntity( return MyUserEntity(
id: doc['id'] as String, id: doc['id'] as String,
email: doc['email'] as String, email: doc['email'] as String?,
name: doc['name'] as String, name: doc['name'] as String?,
picture: doc['picture'] as String?, picture: doc['picture'] as String?,
); );
} }
@@ -4,14 +4,14 @@ import '../entities/entities.dart';
class MyUser extends Equatable { class MyUser extends Equatable {
final String id; final String id;
final String email; final String? email;
final String name; final String? name;
String? picture; final String? picture;
MyUser({ MyUser({
required this.id, required this.id,
required this.email, this.email,
required this.name, this.name,
this.picture, this.picture,
}); });
@@ -14,6 +14,7 @@ class FirebaseUserRepository implements UserRepository {
final usersCollection = FirebaseFirestore.instance.collection('users'); final usersCollection = FirebaseFirestore.instance.collection('users');
final StreamController<MyUser?> _userStreamController = final StreamController<MyUser?> _userStreamController =
StreamController<MyUser?>.broadcast(); StreamController<MyUser?>.broadcast();
String verificationId = '';
FirebaseUserRepository(this._firebaseAuth) { FirebaseUserRepository(this._firebaseAuth) {
_firebaseAuth.userChanges().listen((user) async { _firebaseAuth.userChanges().listen((user) async {
@@ -49,18 +50,14 @@ class FirebaseUserRepository implements UserRepository {
// Sign up // Sign up
@override @override
Future<MyUser> signUp(MyUser myUser, String password) async { Future<MyUser> signUp(String email, String password) async {
try { try {
UserCredential user = await _firebaseAuth.createUserWithEmailAndPassword( UserCredential user = await _firebaseAuth.createUserWithEmailAndPassword(
email: myUser.email, email: email,
password: password, password: password,
); );
myUser = myUser.copyWith( return await getMyUser(user.user!.uid);
id: user.user!.uid,
);
return myUser;
} catch (e) { } catch (e) {
log(e.toString()); log(e.toString());
rethrow; rethrow;
@@ -71,34 +68,21 @@ class FirebaseUserRepository implements UserRepository {
@override @override
Future<void> signInWithPhoneNumber(String phoneNumber) async { Future<void> signInWithPhoneNumber(String phoneNumber) async {
try { try {
await FirebaseAuth.instance.verifyPhoneNumber( await _firebaseAuth.verifyPhoneNumber(
phoneNumber: phoneNumber, phoneNumber: phoneNumber,
verificationCompleted: (PhoneAuthCredential credential) async { verificationCompleted: (PhoneAuthCredential credential) async {
// Esta función se llama automáticamente cuando se completa la verificación del número de teléfono. await _firebaseAuth.signInWithCredential(credential);
// Puedes usar 'credential' para iniciar sesión o vincular la cuenta.
// En la mayoría de los casos, no necesitas implementar esto, ya que Firebase manejará la autenticación automáticamente.
await FirebaseAuth.instance.signInWithCredential(credential);
},
verificationFailed: (FirebaseAuthException e) {
// Esta función se llama si la verificación del número de teléfono falla.
// Maneja los errores o muestra un mensaje al usuario.
if (e.code == 'invalid-phone-number') {
// Manejar el caso de número de teléfono no válido
} else if (e.code == 'network-request-failed') {
// Manejar problemas de conectividad
} else {
// Manejar otros errores
}
}, },
codeSent: (String verificationId, int? resendToken) { codeSent: (String verificationId, int? resendToken) {
// Esta función se llama cuando se envía el código de verificación al número de teléfono del usuario. this.verificationId = verificationId;
// Debes guardar 'verificationId' para usarlo posteriormente en la verificación.
// Puedes mostrar un diálogo para que el usuario ingrese el código o puedes verificarlo automáticamente.
}, },
codeAutoRetrievalTimeout: (String verificationId) { codeAutoRetrievalTimeout: (String verificationId) {
// Esta función se llama cuando el tiempo de espera de recuperación automática del código ha expirado. this.verificationId = verificationId;
// Puedes manejar esto como prefieras, por ejemplo, mostrando un mensaje al usuario o reenviando el código. },
verificationFailed: (FirebaseAuthException e) {
if (e.code == 'invalid-phone-number') {
} else if (e.code == 'network-request-failed') {
} else {}
}, },
); );
} catch (e) { } catch (e) {
@@ -107,6 +91,26 @@ class FirebaseUserRepository implements UserRepository {
} }
} }
@override
Future<bool> verifyOTP(String code) async {
try {
var credentials = await _firebaseAuth.signInWithCredential(
PhoneAuthProvider.credential(
verificationId: verificationId, smsCode: code));
return credentials.user != null ? true : false;
} catch (e) {
if (e is FirebaseAuthException) {
if (e.code == 'invalid-verification-code') {
return false;
} else {
rethrow;
}
} else {
rethrow;
}
}
}
// Sign in // Sign in
@override @override
Future<void> signIn(String email, String password) async { Future<void> signIn(String email, String password) async {
@@ -158,8 +162,12 @@ class FirebaseUserRepository implements UserRepository {
@override @override
Future<MyUser> getMyUser(String myUserId) async { Future<MyUser> getMyUser(String myUserId) async {
try { try {
return usersCollection.doc(myUserId).get().then((value) => return usersCollection.doc(myUserId).get().then((value) {
MyUser.fromEntity(MyUserEntity.fromDocument(value.data()!))); log('xd -- ${value.data().toString()}'); // Imprime el valor de value
return MyUser.fromEntity(
MyUserEntity.fromDocument(value.data()!),
);
});
} catch (e) { } catch (e) {
log(e.toString()); log(e.toString());
rethrow; rethrow;
@@ -9,10 +9,12 @@ abstract class UserRepository {
Future<void> logOut(); Future<void> logOut();
Future<MyUser> signUp(MyUser myUser, String password); Future<MyUser> signUp(String email, String password);
Future<void> signInWithPhoneNumber(String phoneNumber); Future<void> signInWithPhoneNumber(String phoneNumber);
Future<bool> verifyOTP(String code);
Future<void> resetPassword(String email); Future<void> resetPassword(String email);
Future<void> setUserData(MyUser user); Future<void> setUserData(MyUser user);
+2 -2
View File
@@ -11,7 +11,7 @@ environment:
dependencies: dependencies:
animate_do: ^3.0.2 animate_do: ^3.0.2
animated_splash_screen: ^1.3.0 animated_splash_screen: ^1.3.0
cloud_firestore: null cloud_firestore: ^4.15.4
community_material_icon: ^5.9.55 community_material_icon: ^5.9.55
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
diacritic: null diacritic: null
@@ -29,8 +29,8 @@ dependencies:
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
flutter_polyline_points: ^2.0.0 flutter_polyline_points: ^2.0.0
flutter_otp_text_field: ^1.1.1
flutter_rating_bar: ^4.0.1 flutter_rating_bar: ^4.0.1
flutter_otp_text_field: ^1.1.1
otp_timer_button: ^1.1.0 otp_timer_button: ^1.1.0
font_awesome_flutter: ^10.4.0 font_awesome_flutter: ^10.4.0
geocoding: ^2.1.0 geocoding: ^2.1.0