This commit is contained in:
Felipe
2024-02-19 18:16:51 -05:00
parent 9e6f76e309
commit 997c6047bb
34 changed files with 1235 additions and 594 deletions
+7 -7
View File
@@ -25,16 +25,16 @@ class MainApp extends StatelessWidget {
BlocProvider<UpdateUserInfoBloc>(
create: (context) => Injector.appInstance.get<UpdateUserInfoBloc>(),
),
BlocProvider<SignInBloc>(
create: (context) => Injector.appInstance.get<SignInBloc>(),
),
BlocProvider<SignUpBloc>(
create: (context) => Injector.appInstance.get<SignUpBloc>(),
)
// BlocProvider<SignInBloc>(
// create: (context) => Injector.appInstance.get<SignInBloc>(),
// ),
// BlocProvider<SignUpBloc>(
// create: (context) => Injector.appInstance.get<SignUpBloc>(),
// ),
],
child: BlocBuilder<MyUserBloc, MyUserState>(
builder: (context, state) {
return const MyAppView();
return const SafeArea(child: MyAppView());
},
),
);
+24 -19
View File
@@ -16,29 +16,34 @@ class MyAppView extends StatelessWidget {
title: 'Prosappco',
theme: ThemeData(
colorScheme: const ColorScheme.light(
background: Colors.white,
onBackground: Colors.black,
primary: Color.fromRGBO(66, 164, 239, 1),
onPrimary: Colors.black,
secondary: Color.fromRGBO(35, 108, 244, 1),
onSecondary: Colors.white,
tertiary: Color.fromRGBO(255, 204, 128, 1),
error: Colors.red,
outline: Color(0xFF424242)),
background: Colors.white,
onBackground: Colors.black,
primary: Color.fromRGBO(66, 164, 239, 1),
onPrimary: Colors.black,
secondary: Color.fromRGBO(35, 108, 244, 1),
onSecondary: Colors.white,
tertiary: Color.fromRGBO(214, 244, 255, 1),
error: Colors.red,
outline: Color(0xFF424242),
),
),
home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
builder: (context, state) {
switch (state.status) {
case AuthenticationStatus.authenticated:
return const HomeScreen();
case AuthenticationStatus.unauthenticated:
return const WelcomeScreen();
case AuthenticationStatus.unknown:
return const SplashScreen();
}
return getScreen(state);
}),
);
}
getScreen(AuthenticationState state) {
switch (state.status) {
case AuthenticationStatus.authenticated:
return const HomeScreen();
case AuthenticationStatus.unauthenticated:
return WelcomeScreen();
case AuthenticationStatus.unknown:
return const SplashScreen();
}
}
}
@@ -19,13 +19,23 @@ class AuthenticationBloc
_userSubscription = userRepository.streamUser().listen((authUser) {
add(AuthenticationUserChanged(authUser));
});
on<AuthenticationUserChanged>((event, emit) {
if (event.user != null) {
emit(AuthenticationState.authenticated(event.user!));
} else {
emit(const AuthenticationState.unauthenticated());
}
});
on<AuthenticationUserChanged>(_onAuthenticationUserChanged);
on<AuthenticationLogoutRequested>(_onAuthenticationLogoutRequested);
}
void _onAuthenticationUserChanged(
AuthenticationUserChanged event, Emitter<AuthenticationState> emit) {
emit(
event.user != null
? AuthenticationState.authenticated(event.user!)
: const AuthenticationState.unauthenticated(),
);
}
void _onAuthenticationLogoutRequested(AuthenticationLogoutRequested event,
Emitter<AuthenticationState> emit) async {
await userRepository.logOut();
emit(const AuthenticationState.unauthenticated());
}
@override
+13 -10
View File
@@ -11,15 +11,18 @@ class SignUpBloc extends Bloc<SignUpEvent, SignUpState> {
SignUpBloc({required UserRepository userRepository})
: _userRepository = userRepository,
super(SignUpInitial()) {
on<SignUpRequired>((event, emit) async {
emit(SignUpProcess());
try {
MyUser user = await _userRepository.signUp(event.user, event.password);
await _userRepository.setUserData(user);
emit(SignUpSuccess());
} catch (e) {
emit(SignUpFailure());
}
});
on<SignUpRequired>(_onSignUpRequired);
}
void _onSignUpRequired(
SignUpRequired event, Emitter<SignUpState> emit) async {
emit(SignUpProcess());
try {
MyUser user = await _userRepository.signUp(event.user, event.password);
await _userRepository.setUserData(user);
emit(SignUpSuccess());
} catch (e) {
emit(SignUpFailure());
}
}
}
+25 -8
View File
@@ -14,18 +14,35 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
SignInBloc({required UserRepository userRepository})
: _userRepository = userRepository,
super(SignInInitial()) {
on<SignInRequired>((event, emit) async {
emit(SignInProcess());
userRepository.streamUser().listen((authUser) {
try {
await _userRepository.signIn(event.email, event.password);
emit(SignInSuccess());
if (authUser == null) {
emit(SignInFailure());
} else {
emit(SignInSuccess());
}
} catch (e) {
log(e.toString());
emit(const SignInFailure());
}
});
on<SignOutRequired>((event, emit) async {
await _userRepository.logOut();
});
on<SignInRequired>(_onSignInRequired);
on<SignOutRequired>(_onSignOutRequired);
}
void _onSignInRequired(
SignInRequired event, Emitter<SignInState> emit) async {
emit(SignInProcess());
try {
await _userRepository.signIn(event.email, event.password);
emit(SignInSuccess());
} catch (e) {
log(e.toString());
emit(const SignInFailure());
}
}
void _onSignOutRequired(
SignOutRequired event, Emitter<SignInState> emit) async {
await _userRepository.logOut();
}
}
+75 -80
View File
@@ -1,96 +1,91 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart';
import 'package:prosappco/blocs/update_user_info_bloc/update_user_info_bloc.dart';
import 'package:prosappco/components/general_drawer_header.dart';
import 'package:prosappco/components/general_drawer_item.dart';
import 'package:prosappco/screens/configuration/configuration_screen.dart';
class GeneralDrawer extends StatefulWidget {
class GeneralDrawer extends StatelessWidget {
const GeneralDrawer({super.key});
@override
State<GeneralDrawer> createState() => _GeneralDrawerState();
}
class _GeneralDrawerState extends State<GeneralDrawer> {
@override
Widget build(BuildContext context) {
return BlocListener<UpdateUserInfoBloc, UpdateUserInfoState>(
listener: (context, state) {
if (state is UploadPictureSuccess) {
setState(() {
context.read<MyUserBloc>().state.user!.picture = state.userImage;
});
}
},
child: Drawer(
backgroundColor: Theme.of(context).colorScheme.background,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const GeneralDrawerHeader(),
Divider(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.1),
thickness: 0.5,
height: 1,
),
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
GeneralDrawerItem(
icon: Icons.person_outline,
label: 'Mi Perfil',
onTap: () {
Navigator.pop(context);
},
),
],
),
return Drawer(
backgroundColor: Theme.of(context).colorScheme.background,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const GeneralDrawerHeader(),
Divider(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.1),
thickness: 0.5,
height: 1,
),
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
GeneralDrawerItem(
leading: Icons.history,
label: 'Mis servicios',
onTap: () {
Navigator.pop(context);
},
),
GeneralDrawerItem(
leading: Icons.settings_outlined,
label: 'Configuración',
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => const ConfigurationScreen(),
),
);
},
),
GeneralDrawerItem(
leading: Icons.help_outline,
label: 'Soporte',
onTap: () {
Navigator.pop(context);
},
),
GeneralDrawerItem(
leading: Icons.campaign_outlined,
label: 'Sugerencias',
onTap: () {
Navigator.pop(context);
},
),
],
),
),
Divider(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.1),
thickness: 0.5,
height: 1,
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
padding: const EdgeInsets.symmetric(vertical: 15),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
)),
child: const Text(
'Modo Profesional',
style: TextStyle(color: Colors.white, fontSize: 18),
),
),
Divider(
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.1),
thickness: 0.5,
height: 1,
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
padding: const EdgeInsets.symmetric(vertical: 15),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
)),
child: const Text(
'Modo Profesional',
style: TextStyle(color: Colors.white, fontSize: 18),
),
),
const SizedBox(height: 15),
// Container(
// height: MediaQuery.of(context).size.height,
// color: Theme.of(context).colorScheme.background,
// child: ListView(
// children: [
// ListTile(
// onTap: () {
// context.read<SignInBloc>().add(const SignOutRequired());
// },
// title: const Text(
// 'Cerrar Sesión',
// ),
// )
// ],
// ),
// )
],
),
),
const SizedBox(height: 15),
],
),
);
}
+47 -62
View File
@@ -2,78 +2,63 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart';
import 'package:prosappco/blocs/update_user_info_bloc/update_user_info_bloc.dart';
import 'package:prosappco/screens/profile/profile_screen.dart';
class GeneralDrawerHeader extends StatefulWidget {
class GeneralDrawerHeader extends StatelessWidget {
const GeneralDrawerHeader({super.key});
@override
State<GeneralDrawerHeader> createState() => _GeneralDrawerHeaderState();
}
class _GeneralDrawerHeaderState extends State<GeneralDrawerHeader> {
@override
Widget build(BuildContext context) {
return BlocListener<UpdateUserInfoBloc, UpdateUserInfoState>(
listener: (context, state) {
if (state is UploadPictureSuccess) {
setState(() {
context.read<MyUserBloc>().state.user!.picture = state.userImage;
});
}
return ListTile(
onTap: () {
Navigator.pop(context);
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return const ProfileScreen();
},
),
);
},
child: ListTile(
onTap: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (BuildContext context) {
return const ProfileScreen();
},
),
);
},
title: Text(
context.read<MyUserBloc>().state.user!.name,
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(
context.read<MyUserBloc>().state.user!.email,
style: const TextStyle(fontSize: 12),
),
leading: context.read<MyUserBloc>().state.user!.picture == ""
? Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.grey.shade300,
shape: BoxShape.circle,
),
child: Icon(
CupertinoIcons.person,
color: Colors.grey.shade400,
size: 35,
),
)
: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.grey,
shape: BoxShape.circle,
image: DecorationImage(
image: NetworkImage(
context.read<MyUserBloc>().state.user!.picture!,
),
fit: BoxFit.cover,
title: Text(
context.read<MyUserBloc>().state.user!.name,
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(
context.read<MyUserBloc>().state.user!.email,
style: const TextStyle(fontSize: 12),
),
leading: context.read<MyUserBloc>().state.user!.picture == ""
? Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.grey.shade300,
shape: BoxShape.circle,
),
child: Icon(
CupertinoIcons.person,
color: Colors.grey.shade400,
size: 35,
),
)
: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.grey,
shape: BoxShape.circle,
image: DecorationImage(
image: NetworkImage(
context.read<MyUserBloc>().state.user!.picture!,
),
fit: BoxFit.cover,
),
),
trailing: const Icon(Icons.keyboard_arrow_right, color: Colors.black),
contentPadding:
const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
),
),
trailing: const Icon(Icons.keyboard_arrow_right, color: Colors.black),
contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
);
}
}
+19 -7
View File
@@ -2,13 +2,17 @@ import 'package:flutter/material.dart';
class GeneralDrawerItem extends StatelessWidget {
final String label;
final IconData icon;
final IconData? leading;
final bool trailing;
final Color? color;
final VoidCallback onTap;
const GeneralDrawerItem({
super.key,
required this.label,
required this.icon,
this.leading,
this.trailing = false,
this.color,
required this.onTap,
});
@@ -16,13 +20,21 @@ class GeneralDrawerItem extends StatelessWidget {
Widget build(BuildContext context) {
return ListTile(
onTap: onTap,
leading: Icon(
icon,
color: Colors.black,
),
leading: leading == null
? null
: Icon(
leading,
color: Colors.black,
),
trailing: trailing
? const Icon(
Icons.keyboard_arrow_right,
color: Colors.black,
)
: null,
title: Text(
label,
style: const TextStyle(fontSize: 15),
style: TextStyle(fontSize: 15, color: color),
),
);
}
@@ -0,0 +1,10 @@
import 'package:flutter/material.dart';
class GeneralPrimaryButton extends StatelessWidget {
const GeneralPrimaryButton({super.key});
@override
Widget build(BuildContext context) {
return Container();
}
}
+2 -2
View File
@@ -23,10 +23,10 @@ class UserDI {
injector.registerSingleton<UpdateUserInfoBloc>((() =>
UpdateUserInfoBloc(userRepository: injector.get<UserRepository>())));
injector.registerSingleton<SignInBloc>(
injector.registerDependency<SignInBloc>(
(() => SignInBloc(userRepository: injector.get<UserRepository>())));
injector.registerSingleton<SignUpBloc>(
injector.registerDependency<SignUpBloc>(
(() => SignUpBloc(userRepository: injector.get<UserRepository>())));
}
}
+26 -25
View File
@@ -103,32 +103,33 @@ class _SignInScreenState extends State<SignInScreen> {
width: MediaQuery.of(context).size.width * 0.9,
height: 50,
child: TextButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
context.read<SignInBloc>().add(SignInRequired(
emailController.text,
passwordController.text));
}
},
style: TextButton.styleFrom(
elevation: 3.0,
backgroundColor:
Theme.of(context).colorScheme.primary,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(60))),
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 25, vertical: 5),
child: Text(
'Sign In',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600),
onPressed: () {
if (_formKey.currentState!.validate()) {
context.read<SignInBloc>().add(SignInRequired(
emailController.text, passwordController.text));
}
},
style: TextButton.styleFrom(
elevation: 3.0,
backgroundColor:
Theme.of(context).colorScheme.primary,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(60))),
child: const Padding(
padding:
EdgeInsets.symmetric(horizontal: 25, vertical: 5),
child: Text(
'Iniciar Sesión',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600,
),
)),
),
),
),
)
: const CircularProgressIndicator()
],
+131
View File
@@ -0,0 +1,131 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injector/injector.dart';
import 'package:prosappco/blocs/authentication_bloc/authentication_bloc.dart';
import 'package:prosappco/blocs/sign_up_bloc/sign_up_bloc.dart';
import 'package:prosappco/blocs/sing_in_bloc/sign_in_bloc.dart';
import 'package:prosappco/screens/authentication/sign_in_screen.dart';
import 'package:prosappco/screens/authentication/sign_up_screen.dart';
class SignScreen extends StatefulWidget {
final int initialIndex;
SignScreen({super.key, required this.initialIndex});
@override
State<SignScreen> createState() => _SignScreenState();
}
class _SignScreenState extends State<SignScreen> with TickerProviderStateMixin {
late TabController tabController;
@override
void initState() {
super.initState();
tabController = TabController(
initialIndex: widget.initialIndex,
length: 2,
vsync: this,
);
}
@override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
return BlocListener<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {
if (state.status == AuthenticationStatus.authenticated) {
Navigator.pop(context);
}
},
child: Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.tertiary,
),
body: Column(
children: [
// Text('${context.read<SignInBloc>().state}'),
Container(
color: Theme.of(context).colorScheme.tertiary,
child: Column(
children: [
Center(
child: Image(
width: width * 0.7,
image: AssetImage('images/logo_prosapp.png'),
),
),
SizedBox(height: 20),
TabBar(
controller: tabController,
unselectedLabelColor:
Theme.of(context).colorScheme.onBackground,
labelColor: Theme.of(context).colorScheme.onBackground,
tabs: [
Padding(
padding: EdgeInsets.all(12.0),
child: Text(
'Inicia sesión',
style: TextStyle(
fontSize: 18,
),
),
),
Padding(
padding: EdgeInsets.all(12.0),
child: Text(
'Registrate',
style: TextStyle(
fontSize: 18,
),
),
),
],
),
],
),
),
Expanded(
child: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
children: [
Expanded(
child: TabBarView(controller: tabController, children: [
BlocProvider<SignInBloc>(
create: (context) =>
Injector.appInstance.get<SignInBloc>(),
child: SignInScreen(),
),
BlocProvider<SignUpBloc>(
create: (context) =>
Injector.appInstance.get<SignUpBloc>(),
child: SignUpScreen(),
),
// BlocProvider<SignInBloc>(
// create: (context) => SignInBloc(
// userRepository: context
// .read<AuthenticationBloc>()
// .userRepository),
// child: SignInScreen(),
// ),
// BlocProvider<SignUpBloc>(
// create: (context) => SignUpBloc(
// userRepository: context
// .read<AuthenticationBloc>()
// .userRepository),
// child: SignUpScreen(),
// ),
]),
)
],
),
),
),
),
],
),
),
);
}
}
+206 -204
View File
@@ -45,212 +45,214 @@ class _SignUpScreenState extends State<SignUpScreen> {
return;
}
},
child: Form(
key: _formKey,
child: Center(
child: Column(
children: [
const SizedBox(height: 20),
SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
child: MyTextField(
controller: emailController,
hintText: 'Email',
obscureText: false,
keyboardType: TextInputType.emailAddress,
prefixIcon: const Icon(CupertinoIcons.mail_solid),
validator: (val) {
if (val!.isEmpty) {
return 'Please fill in this field';
} else if (!emailRexExp.hasMatch(val)) {
return 'Please enter a valid email';
}
return null;
}),
),
const SizedBox(height: 10),
SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
child: MyTextField(
controller: passwordController,
hintText: 'Password',
obscureText: obscurePassword,
keyboardType: TextInputType.visiblePassword,
prefixIcon: const Icon(CupertinoIcons.lock_fill),
onChanged: (val) {
if (val!.contains(RegExp(r'[A-Z]'))) {
setState(() {
containsUpperCase = true;
});
} else {
setState(() {
containsUpperCase = false;
});
}
if (val.contains(RegExp(r'[a-z]'))) {
setState(() {
containsLowerCase = true;
});
} else {
setState(() {
containsLowerCase = false;
});
}
if (val.contains(RegExp(r'[0-9]'))) {
setState(() {
containsNumber = true;
});
} else {
setState(() {
containsNumber = false;
});
}
if (val.contains(specialCharRexExp)) {
setState(() {
containsSpecialChar = true;
});
} else {
setState(() {
containsSpecialChar = false;
});
}
if (val.length >= 8) {
setState(() {
contains8Length = true;
});
} else {
setState(() {
contains8Length = false;
});
}
return null;
},
suffixIcon: IconButton(
onPressed: () {
setState(() {
obscurePassword = !obscurePassword;
if (obscurePassword) {
iconPassword = CupertinoIcons.eye_fill;
} else {
iconPassword = CupertinoIcons.eye_slash_fill;
}
});
child: Scaffold(
body: Form(
key: _formKey,
child: Center(
child: Column(
children: [
const SizedBox(height: 20),
SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
child: MyTextField(
controller: emailController,
hintText: 'Email',
obscureText: false,
keyboardType: TextInputType.emailAddress,
prefixIcon: const Icon(CupertinoIcons.mail_solid),
validator: (val) {
if (val!.isEmpty) {
return 'Please fill in this field';
} else if (!emailRexExp.hasMatch(val)) {
return 'Please enter a valid email';
}
return null;
}),
),
const SizedBox(height: 10),
SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
child: MyTextField(
controller: passwordController,
hintText: 'Password',
obscureText: obscurePassword,
keyboardType: TextInputType.visiblePassword,
prefixIcon: const Icon(CupertinoIcons.lock_fill),
onChanged: (val) {
if (val!.contains(RegExp(r'[A-Z]'))) {
setState(() {
containsUpperCase = true;
});
} else {
setState(() {
containsUpperCase = false;
});
}
if (val.contains(RegExp(r'[a-z]'))) {
setState(() {
containsLowerCase = true;
});
} else {
setState(() {
containsLowerCase = false;
});
}
if (val.contains(RegExp(r'[0-9]'))) {
setState(() {
containsNumber = true;
});
} else {
setState(() {
containsNumber = false;
});
}
if (val.contains(specialCharRexExp)) {
setState(() {
containsSpecialChar = true;
});
} else {
setState(() {
containsSpecialChar = false;
});
}
if (val.length >= 8) {
setState(() {
contains8Length = true;
});
} else {
setState(() {
contains8Length = false;
});
}
return null;
},
icon: Icon(iconPassword),
),
validator: (val) {
if (val!.isEmpty) {
return 'Please fill in this field';
} else if (!passwordRexExp.hasMatch(val)) {
return 'Please enter a valid password';
}
return null;
}),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"⚈ 1 uppercase",
style: TextStyle(
color: containsUpperCase
? Colors.green
: Theme.of(context).colorScheme.onBackground),
),
Text(
"⚈ 1 lowercase",
style: TextStyle(
color: containsLowerCase
? Colors.green
: Theme.of(context).colorScheme.onBackground),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"⚈ 8 minimum character",
style: TextStyle(
color: contains8Length
? Colors.green
: Theme.of(context).colorScheme.onBackground),
),
Text(
"⚈ 1 number",
style: TextStyle(
color: containsNumber
? Colors.green
: Theme.of(context).colorScheme.onBackground),
),
],
),
],
),
const SizedBox(height: 10),
SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
child: MyTextField(
controller: nameController,
hintText: 'Name',
obscureText: false,
keyboardType: TextInputType.name,
prefixIcon: const Icon(CupertinoIcons.person_fill),
validator: (val) {
if (val!.isEmpty) {
return 'Please fill in this field';
} else if (val.length > 30) {
return 'Name too long';
}
return null;
}),
),
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
!signUpRequired
? SizedBox(
width: MediaQuery.of(context).size.width * 0.5,
child: TextButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
MyUser myUser = MyUser.empty;
myUser = myUser.copyWith(
email: emailController.text,
name: nameController.text,
);
setState(() {
context.read<SignUpBloc>().add(SignUpRequired(
myUser, passwordController.text));
});
suffixIcon: IconButton(
onPressed: () {
setState(() {
obscurePassword = !obscurePassword;
if (obscurePassword) {
iconPassword = CupertinoIcons.eye_fill;
} else {
iconPassword = CupertinoIcons.eye_slash_fill;
}
},
style: TextButton.styleFrom(
elevation: 3.0,
backgroundColor:
Theme.of(context).colorScheme.primary,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(60))),
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 25, vertical: 5),
child: Text(
'Sign Up',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600),
),
)),
)
: const CircularProgressIndicator()
],
});
},
icon: Icon(iconPassword),
),
validator: (val) {
if (val!.isEmpty) {
return 'Please fill in this field';
} else if (!passwordRexExp.hasMatch(val)) {
return 'Please enter a valid password';
}
return null;
}),
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"⚈ 1 uppercase",
style: TextStyle(
color: containsUpperCase
? Colors.green
: Theme.of(context).colorScheme.onBackground),
),
Text(
"⚈ 1 lowercase",
style: TextStyle(
color: containsLowerCase
? Colors.green
: Theme.of(context).colorScheme.onBackground),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"⚈ 8 minimum character",
style: TextStyle(
color: contains8Length
? Colors.green
: Theme.of(context).colorScheme.onBackground),
),
Text(
"⚈ 1 number",
style: TextStyle(
color: containsNumber
? Colors.green
: Theme.of(context).colorScheme.onBackground),
),
],
),
],
),
const SizedBox(height: 10),
SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
child: MyTextField(
controller: nameController,
hintText: 'Name',
obscureText: false,
keyboardType: TextInputType.name,
prefixIcon: const Icon(CupertinoIcons.person_fill),
validator: (val) {
if (val!.isEmpty) {
return 'Please fill in this field';
} else if (val.length > 30) {
return 'Name too long';
}
return null;
}),
),
SizedBox(height: MediaQuery.of(context).size.height * 0.02),
!signUpRequired
? SizedBox(
width: MediaQuery.of(context).size.width * 0.5,
child: TextButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
MyUser myUser = MyUser.empty;
myUser = myUser.copyWith(
email: emailController.text,
name: nameController.text,
);
setState(() {
context.read<SignUpBloc>().add(SignUpRequired(
myUser, passwordController.text));
});
}
},
style: TextButton.styleFrom(
elevation: 3.0,
backgroundColor:
Theme.of(context).colorScheme.primary,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(60))),
child: const Padding(
padding: EdgeInsets.symmetric(
horizontal: 25, vertical: 5),
child: Text(
'Sign Up',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600),
),
)),
)
: const CircularProgressIndicator(),
],
),
),
),
),
+112 -86
View File
@@ -1,102 +1,128 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:prosappco/blocs/sing_in_bloc/sign_in_bloc.dart';
import 'package:prosappco/screens/authentication/sign_in_screen.dart';
import 'package:prosappco/screens/authentication/sign_up_screen.dart';
import 'package:flutter/services.dart';
import 'package:intl_phone_field/intl_phone_field.dart';
import 'package:prosappco/screens/authentication/sign_screen.dart';
import '../../blocs/authentication_bloc/authentication_bloc.dart';
import '../../blocs/sign_up_bloc/sign_up_bloc.dart';
class WelcomeScreen extends StatefulWidget {
class WelcomeScreen extends StatelessWidget {
const WelcomeScreen({super.key});
@override
State<WelcomeScreen> createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen>
with TickerProviderStateMixin {
late TabController tabController;
@override
void initState() {
super.initState();
tabController = TabController(
initialIndex: 0,
length: 2,
vsync: this,
);
}
@override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
return Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.transparent,
),
body: SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
backgroundColor: Theme.of(context).colorScheme.surface,
body: Column(
children: [
Container(
color: Theme.of(context).colorScheme.tertiary,
child: Column(
children: [
const Text(
'Welcome Back !',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
const SizedBox(height: 20),
Center(
child: Image(
width: width * 0.7,
image: const AssetImage('images/logo_prosapp.png'),
),
),
const SizedBox(height: kToolbarHeight),
TabBar(
controller: tabController,
unselectedLabelColor: Theme.of(context)
.colorScheme
.onBackground
.withOpacity(0.5),
labelColor: Theme.of(context).colorScheme.onBackground,
tabs: const [
Padding(
padding: EdgeInsets.all(12.0),
child: Text(
'Sign In',
style: TextStyle(
fontSize: 18,
),
),
),
Padding(
padding: EdgeInsets.all(12.0),
child: Text(
'Sign Up',
style: TextStyle(
fontSize: 18,
),
),
),
]),
Expanded(
child: TabBarView(controller: tabController, children: [
BlocProvider<SignInBloc>(
create: (context) => SignInBloc(
userRepository: context
.read<AuthenticationBloc>()
.userRepository),
child: const SignInScreen(),
),
BlocProvider<SignUpBloc>(
create: (context) => SignUpBloc(
userRepository: context
.read<AuthenticationBloc>()
.userRepository),
child: const SignUpScreen(),
),
]),
)
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),
),
);
},
),
],
),
),
],
),
),
),
],
),
);
}
@@ -0,0 +1,171 @@
// import 'package:cloud_firestore/cloud_firestore.dart';
// import 'package:firebase_auth/firebase_auth.dart';
// import 'package:flutter/cupertino.dart';
// import 'package:flutter/material.dart';
// import 'package:get/get.dart';
// import 'package:prosappco/src/components/pop_appbar.dart';
// import 'package:prosappco/src/presentation/screens/about.dart';
// class ConfigurationScreen extends StatefulWidget {
// const ConfigurationScreen({super.key});
// @override
// State<ConfigurationScreen> createState() => _ConfigurationScreenState();
// }
// class _ConfigurationScreenState extends State<ConfigurationScreen> {
// late final FirebaseAuth _auth;
// @override
// void initState() {
// super.initState();
// _auth = FirebaseAuth.instance;
// }
// Future<void> deleteAccount() async {
// try {
// final currentUser = _auth.currentUser;
// if (currentUser != null) {
// final uid = currentUser.uid;
// await FirebaseFirestore.instance.collection('users').doc(uid).delete();
// await currentUser.delete();
// await _auth.signOut();
// Get.snackbar(
// 'Cuenta Eliminada',
// 'Tu cuenta ha sido eliminada con éxito.',
// snackPosition: SnackPosition.BOTTOM,
// );
// }
// } catch (e) {
// Get.snackbar(
// 'Error al Eliminar Cuenta',
// 'Hubo un error al eliminar tu cuenta. Por favor, inténtalo de nuevo más tarde.',
// snackPosition: SnackPosition.BOTTOM,
// );
// }
// }
// Future<void> _showDeleteAccountConfirmationDialog(
// BuildContext context) async {
// return showDialog(
// context: context,
// builder: (BuildContext context) {
// return AlertDialog(
// title: const Text('Eliminar Cuenta'),
// content: const Text(
// '¿Estás seguro de que deseas eliminar tu cuenta? Esta acción no se puede deshacer.'),
// actions: [
// TextButton(
// onPressed: () {
// Navigator.of(context).pop();
// },
// child: const Text('Cancelar'),
// ),
// TextButton(
// onPressed: () {
// deleteAccount();
// Navigator.of(context).pop();
// },
// child: const Text(
// 'Eliminar',
// style:
// TextStyle(color: Colors.red, fontWeight: FontWeight.w600),
// ),
// ),
// ],
// );
// },
// );
// }
// @override
// Widget build(BuildContext context) {
// return Scaffold(
// appBar: PopAppbar(
// onPressed: () {
// Navigator.pop(context);
// },
// label: 'Configuración'),
// body: ListView(
// children: [
// 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,
// ),
// ),
// ListTile(
// onTap: () {
// _showDeleteAccountConfirmationDialog(context);
// },
// title: const Text(
// 'Eliminar cuenta',
// style: TextStyle(color: Colors.red),
// ),
// ),
// ],
// ),
// );
// }
// }
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:prosappco/blocs/authentication_bloc/authentication_bloc.dart';
import 'package:prosappco/components/general_drawer_item.dart';
class ConfigurationScreen extends StatelessWidget {
const ConfigurationScreen({super.key});
@override
Widget build(BuildContext context) {
return BlocListener<AuthenticationBloc, AuthenticationState>(
listener: (context, state) {
if (state.status == AuthenticationStatus.unauthenticated) {
Navigator.pop(context);
}
},
child: Scaffold(
appBar: AppBar(
title: const Text('Configuración'),
),
body: ListView(children: [
GeneralDrawerItem(
label: 'Acerca de la aplicación',
onTap: () {},
trailing: true,
),
GeneralDrawerItem(
label: 'Cerrar sesión',
onTap: () {
context
.read<AuthenticationBloc>()
.add(AuthenticationLogoutRequested());
},
),
GeneralDrawerItem(
label: 'Eliminar cuenta',
onTap: () {},
color: Theme.of(context).colorScheme.error,
)
]),
),
);
}
}
+8 -26
View File
@@ -1,36 +1,18 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart';
import 'package:prosappco/blocs/update_user_info_bloc/update_user_info_bloc.dart';
import 'package:prosappco/components/general_drawer.dart';
class HomeScreen extends StatefulWidget {
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return BlocListener<UpdateUserInfoBloc, UpdateUserInfoState>(
listener: (context, state) {
if (state is UploadPictureSuccess) {
setState(() {
context.read<MyUserBloc>().state.user!.picture = state.userImage;
});
}
},
child: SafeArea(
child: Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
drawer: const GeneralDrawer(),
appBar: AppBar(),
body: const Center(
child: Text('Bienvenido'),
),
return SafeArea(
child: Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
drawer: GeneralDrawer(),
appBar: AppBar(),
body: const Center(
child: Text('Bienvenido'),
),
),
);
+1 -3
View File
@@ -27,9 +27,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
return BlocListener<UpdateUserInfoBloc, UpdateUserInfoState>(
listener: (context, state) {
if (state is UploadPictureSuccess) {
setState(() {
context.read<MyUserBloc>().state.user!.picture = state.userImage;
});
setState(() {});
}
},
child: Scaffold(
+25 -24
View File
@@ -69,6 +69,30 @@ class _LoginScreenState extends State<LoginScreen> {
}
Widget _mobileView(BuildContext context) {
const inputDecoration = InputDecoration(
border: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color.fromARGB(255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
fillColor: Color.fromARGB(255, 239, 239, 239),
filled: true,
);
return BottomSheetExpanded(
children: [
const SizedBox(
@@ -103,30 +127,7 @@ class _LoginScreenState extends State<LoginScreen> {
onChanged: (phoneNo) {
completePhoneNumber = phoneNo.completeNumber;
},
decoration: const InputDecoration(
border: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
errorBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color.fromARGB(255, 184, 0, 0)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xFFECECEC)),
borderRadius: BorderRadius.all(
Radius.circular(50),
)),
fillColor: Color.fromARGB(255, 239, 239, 239),
filled: true,
),
decoration: inputDecoration,
),
),
const Text(
@@ -0,0 +1,36 @@
import 'package:equatable/equatable.dart';
class CityEntity extends Equatable {
final String name;
final String coords;
const CityEntity({
required this.name,
required this.coords,
});
Map<String, Object?> toDocument() {
return {
'name': name,
'coords': coords,
};
}
static CityEntity fromDocument(Map<String, dynamic> doc) {
return CityEntity(
name: doc['name'] as String,
coords: doc['coords'] as String,
);
}
@override
List<Object?> get props => [name, coords];
@override
String toString() {
return '''CityEntity {
name: $name
coords: $coords
}''';
}
}
@@ -0,0 +1,39 @@
import 'package:equatable/equatable.dart';
import 'package:user_repository/user_repository.dart';
class CountryEntity extends Equatable {
final String name;
final List<RegionEntity> regions;
const CountryEntity({
required this.name,
required this.regions,
});
Map<String, Object?> toDocument() {
return {
'name': name,
'regions': regions,
};
}
static CountryEntity fromDocument(Map<String, dynamic> doc) {
return CountryEntity(
name: doc['name'] as String,
regions: (doc['regions'] as List)
.map((region) => RegionEntity.fromDocument(region))
.toList(),
);
}
@override
List<Object?> get props => [name, regions];
@override
String toString() {
return '''CountryEntity {
name: $name
regions: $regions
}''';
}
}
@@ -1 +1,4 @@
export 'my_user_entity.dart';
export 'country_entity.dart';
export 'region_entity.dart';
export 'city_entity.dart';
@@ -0,0 +1,39 @@
import 'package:equatable/equatable.dart';
import 'package:user_repository/user_repository.dart';
class RegionEntity extends Equatable {
final String name;
final List<CityEntity> cities;
const RegionEntity({
required this.name,
required this.cities,
});
Map<String, Object?> toDocument() {
return {
'name': name,
'cities': cities,
};
}
static RegionEntity fromDocument(Map<String, dynamic> doc) {
return RegionEntity(
name: doc['name'] as String,
cities: (doc['cities'] as List)
.map((city) => CityEntity.fromDocument(city))
.toList(),
);
}
@override
List<Object?> get props => [name, cities];
@override
String toString() {
return '''RegionEntity {
name: $name
cities: $cities
}''';
}
}
@@ -0,0 +1,46 @@
import 'package:equatable/equatable.dart';
import '../entities/entities.dart';
class City extends Equatable {
final String name;
final String coords;
const City({
required this.name,
required this.coords,
});
static const empty = City(name: '', coords: '');
City copyWith({
String? name,
String? coords,
}) {
return City(
name: name ?? this.name,
coords: coords ?? this.coords,
);
}
bool get isEmpty => this == City.empty;
bool get isNotEmpty => this != City.empty;
City toEntity() {
return City(
name: name,
coords: coords,
);
}
static City fromEntity(CityEntity entity) {
return City(
name: entity.name,
coords: entity.coords,
);
}
@override
List<Object?> get props => [name, coords];
}
@@ -0,0 +1,42 @@
import 'package:equatable/equatable.dart';
import 'package:user_repository/user_repository.dart';
class Country extends Equatable {
final String name;
final List<Region> regions;
const Country({
required this.name,
required this.regions,
});
static const empty = Country(name: '', regions: []);
Country copyWith({
String? name,
List<Region>? regions,
}) {
return Country(
name: name ?? this.name,
regions: regions ?? this.regions,
);
}
bool get isEmpty => this == Country.empty;
bool get isNotEmpty => this != Country.empty;
Country toEntity() {
return Country(name: name, regions: regions);
}
static Country fromEntity(CountryEntity entity) {
return Country(
name: entity.name,
regions:
entity.regions.map((region) => Region.fromEntity(region)).toList(),
);
}
@override
List<Object?> get props => [name, regions];
}
@@ -1 +1,4 @@
export 'my_user.dart';
export 'country.dart';
export 'region.dart';
export 'city.dart';
@@ -60,11 +60,3 @@ class MyUser extends Equatable {
@override
List<Object?> get props => [id, email, name, picture];
}
// final String name;
// final String city;
// final String? profession;
// final String? state;
// final Reference? photo;
// final int? tarifa;
// final String? phoneNumber;
// final String? token;
@@ -0,0 +1,41 @@
import 'package:equatable/equatable.dart';
import 'package:user_repository/user_repository.dart';
class Region extends Equatable {
final String name;
final List<City> cities;
const Region({
required this.name,
required this.cities,
});
static const empty = Region(name: '', cities: []);
Region copyWith({
String? name,
List<City>? cities,
}) {
return Region(
name: name ?? this.name,
cities: cities ?? this.cities,
);
}
bool get isEmpty => this == Region.empty;
bool get isNotEmpty => this != Region.empty;
Region toEntity() {
return Region(name: name, cities: cities);
}
static Region fromEntity(RegionEntity entity) {
return Region(
name: entity.name,
cities: entity.cities.map((city) => City.fromEntity(city)).toList(),
);
}
@override
List<Object?> get props => [name, cities];
}
@@ -0,0 +1 @@
abstract class CityRepository {}
@@ -0,0 +1,7 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'city_repo.dart';
class FirebaseCityRepository implements CityRepository {
final usersCollection = FirebaseFirestore.instance.collection('users');
}
@@ -6,7 +6,7 @@ import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:user_repository/src/models/my_user.dart';
import 'entities/entities.dart';
import '../entities/entities.dart';
import 'user_repo.dart';
class FirebaseUserRepository implements UserRepository {
@@ -42,14 +42,6 @@ class FirebaseUserRepository implements UserRepository {
return _userStreamController.stream;
}
// @override
// Stream<User?> get user {
// return _firebaseAuth.authStateChanges().map((firebaseUser) {
// final user = firebaseUser;
// return user;
// });
// }
// Sign up
@override
Future<MyUser> signUp(MyUser myUser, String password) async {
@@ -70,6 +62,46 @@ class FirebaseUserRepository implements UserRepository {
}
}
// Sign in with phone number
@override
Future<void> signInWithPhoneNumber(String phoneNumber) async {
try {
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: phoneNumber,
verificationCompleted: (PhoneAuthCredential credential) async {
// Esta función se llama automáticamente cuando se completa la verificación del número de teléfono.
// 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) {
// Esta función se llama cuando se envía el código de verificación al número de teléfono del usuario.
// 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) {
// Esta función se llama cuando el tiempo de espera de recuperación automática del código ha expirado.
// Puedes manejar esto como prefieras, por ejemplo, mostrando un mensaje al usuario o reenviando el código.
},
);
} catch (e) {
log(e.toString());
rethrow;
}
}
// Sign in
@override
Future<void> signIn(String email, String password) async {
@@ -1,4 +1,4 @@
import '../user_repository.dart';
import '../../user_repository.dart';
abstract class UserRepository {
// Stream<User?> get user;
@@ -10,6 +10,8 @@ abstract class UserRepository {
Future<MyUser> signUp(MyUser myUser, String password);
Future<void> signInWithPhoneNumber(String phoneNumber);
Future<void> resetPassword(String email);
Future<void> setUserData(MyUser user);
@@ -2,5 +2,5 @@ library user_repository;
export 'src/models/models.dart';
export 'src/entities/entities.dart';
export 'src/user_repo.dart';
export 'src/firebase_user_repository.dart';
export 'src/repositories/user_repo.dart';
export 'src/repositories/firebase_user_repository.dart';
+8
View File
@@ -853,6 +853,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
otp_timer_button:
dependency: "direct main"
description:
name: otp_timer_button
sha256: e6516573bc31b99ae3b67f4f8f8bbe721672f6f4d04c96e493b08b5ff1afa06e
url: "https://pub.dev"
source: hosted
version: "1.1.0"
package_config:
dependency: transitive
description:
+5 -4
View File
@@ -28,9 +28,10 @@ dependencies:
flutter_local_notifications: ^14.1.1
flutter_localizations:
sdk: flutter
flutter_otp_text_field: null
flutter_polyline_points: ^2.0.0
flutter_rating_bar: null
flutter_otp_text_field: ^1.1.1
flutter_rating_bar: ^4.0.1
otp_timer_button: ^1.1.0
font_awesome_flutter: ^10.4.0
geocoding: ^2.1.0
geolocator: ^9.0.2
@@ -40,8 +41,8 @@ dependencies:
http: ^1.1.0
image_picker: ^1.0.7
injector: ^3.0.0
intl: any
intl_phone_field: null
intl_phone_field: ^3.2.0
intl: ^0.18.1
location: ^5.0.3
package_info_plus: ^4.2.0
provider: ^6.0.5