Compare commits

..

2 Commits

34 changed files with 347 additions and 337 deletions

View File

@@ -15,7 +15,7 @@ late final GoRouter appRouter;
void configureAppRouter() {
appRouter = GoRouter(
navigatorKey: rootNavigatorKey,
initialLocation: AppRoutes.onboarding,
initialLocation: AppRoutes.login,
debugLogDiagnostics: true,
routes: [
GoRoute(

View File

@@ -832,7 +832,7 @@ packages:
source: hosted
version: "1.4.0"
utils:
dependency: "direct overridden"
dependency: "direct main"
description:
path: "../../packages/utils"
relative: true

View File

@@ -1,4 +1,4 @@
export 'src/features/device_sign_up/link_watch/create_profile_screen.dart';
export 'src/features/device_sign_up/presentation/link_watch/create_profile_screen.dart';
export 'src/features/onboarding/onboarding_builder.dart';
export 'src/features/link_phone/presentation/request_phone/request_link_phone_builder.dart';
export 'src/features/link_phone/presentation/verify_code/verify_link_phone_code_builder.dart';

View File

@@ -16,7 +16,21 @@ abstract class AuthRemoteDatasource {
required String token,
required String code,
});
Future<String> requestPasswordReset({String? phone, String? email});
Future<String> requestPasswordReset({required String email});
Future<void> recoverPassword({required newPassword, required token});
Future<String> createChildProfile({
required String id,
required String parentId,
required String firstName,
required String lastName,
required int bornAt,
required String gender,
required String relationType,
required String address,
required String cardPublicKey,
required String deviceActivationCode,
required String scaProof,
});
}

View File

@@ -169,32 +169,26 @@ class AuthRemoteDatasourceImpl implements AuthRemoteDatasource {
}
@override
Future<String> requestPasswordReset({
String? phone,
String? email
}) async {
Future<String> requestPasswordReset({required String email}) async {
try {
if (phone == null && email == null) {
throw FormatException("No phone or email address given");
}
late final Map<String, dynamic> body;
body = {'email': email};
// late final Map<String, dynamic> body;
if (email != null) {
// body = {'email': email};
return 'ec14b7e7-58dd-4a59-9f41-0da86eaabf14';
} else {
// body = {'phone': phone!};
return 'ec14b7e7-58dd-4a59-9f41-0da86eaabf14';
// throw Exception("reset by phone is not currently implemented");
}
/*final response = await _repository.put<Map<String, dynamic>>(
final response = await _repository.put<String>(
'/auth/reset-password',
body: body,
);
final token = response.data!['token'];
return token;*/
final data = response.data;
if (data == null || data.isEmpty) {
throw Exception('Empty response from /auth/totp/code');
}
return data;
} on DioException catch (error) {
throw _mapDioError(error, defaultMessage: 'Error to request password reset');
throw _mapDioError(
error,
defaultMessage: 'Error to request password reset',
);
}
}
@@ -206,7 +200,52 @@ class AuthRemoteDatasourceImpl implements AuthRemoteDatasource {
body: <String, dynamic>{'newPassword': newPassword, 'token': token},
);
} on DioException catch (error) {
throw _mapDioError(error, defaultMessage: 'Error to request password recovery');
throw _mapDioError(
error,
defaultMessage: 'Error to request password recovery',
);
}
}
@override
Future<String> createChildProfile({
required String id,
required String parentId,
required String firstName,
required String lastName,
required int bornAt,
required String gender,
required String relationType,
required String address,
required String cardPublicKey,
required String deviceActivationCode,
required String scaProof,
}) async {
try {
final response = await _repository.post<Map<String, dynamic>>(
'/auth/child-profiles',
body: <String, dynamic>{
'id': id,
'parentId': parentId,
'firstName': firstName,
'lastName': lastName,
'bornAt': bornAt,
'gender': gender,
'relationType': relationType,
'address': address,
'cardPublicKey': cardPublicKey,
'deviceActivationCode': deviceActivationCode,
'scaProof': scaProof,
},
);
final data = response.data;
if (data == null || data.isEmpty) {
throw Exception('Empty response from /auth/child-profiles');
} else {
return data['id'];
}
} on DioException catch (error) {
throw _mapDioError(error, defaultMessage: 'Error in createChildProfile');
}
}
}

View File

@@ -47,8 +47,8 @@ class AuthRepositoryImpl implements AuthRepository {
}
@override
Future<String> requestPasswordReset({String? phone, String? email}) {
return _remote.requestPasswordReset(phone: phone, email: email);
Future<String> requestPasswordReset({required String email}) {
return _remote.requestPasswordReset(email: email);
}
@override
@@ -58,4 +58,33 @@ class AuthRepositoryImpl implements AuthRepository {
}) {
return _remote.recoverPassword(newPassword: newPassword, token: token);
}
@override
Future<String> createChildProfile({
required String id,
required String parentId,
required String firstName,
required String lastName,
required int bornAt,
required String gender,
required String relationType,
required String address,
required String cardPublicKey,
required String deviceActivationCode,
required String scaProof,
}) {
return _remote.createChildProfile(
id: id,
parentId: parentId,
firstName: firstName,
lastName: lastName,
bornAt: bornAt,
gender: gender,
relationType: relationType,
address: address,
cardPublicKey: cardPublicKey,
deviceActivationCode: deviceActivationCode,
scaProof: scaProof,
);
}
}

View File

@@ -9,7 +9,7 @@ abstract class AuthRepository {
Future<String> login({required String email, required String password});
Future<void> twoFactor({required String token, required String code});
Future<String> requestPasswordReset({String phone, String email});
Future<String> requestPasswordReset({required String email});
Future<void> recoverPassword({
required String newPassword,
@@ -23,4 +23,17 @@ abstract class AuthRepository {
required String token,
required String code,
});
Future<String> createChildProfile({
required String id,
required String parentId,
required String firstName,
required String lastName,
required int bornAt,
required String gender,
required String relationType,
required String address,
required String cardPublicKey,
required String deviceActivationCode,
required String scaProof,
});
}

View File

@@ -1,4 +1,4 @@
import 'package:auth/src/features/device_sign_up/device_signup_screen.dart';
import 'package:auth/src/features/device_sign_up/presentation/device_signup_screen.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart';

View File

@@ -0,0 +1,15 @@
abstract class CreateChildProfileUseCase {
Future<String> createChildProfile({
required String id,
required String parentId,
required String firstName,
required String lastName,
required int bornAt,
required String gender,
required String relationType,
required String address,
required String cardPublicKey,
required String deviceActivationCode,
required String scaProof,
});
}

View File

@@ -0,0 +1,37 @@
import 'package:auth/src/core/domain/repositories/auth_repository.dart';
import 'package:auth/src/features/device_sign_up/domain/create_child_profile_use_case.dart';
class CreateChildProfileUseCaseImpl implements CreateChildProfileUseCase {
CreateChildProfileUseCaseImpl(this._repository);
final AuthRepository _repository;
@override
Future<String> createChildProfile({
required String id,
required String parentId,
required String firstName,
required String lastName,
required int bornAt,
required String gender,
required String relationType,
required String address,
required String cardPublicKey,
required String deviceActivationCode,
required String scaProof,
}) {
return _repository.createChildProfile(
id: id,
parentId: parentId,
firstName: firstName,
lastName: lastName,
bornAt: bornAt,
gender: gender,
relationType: relationType,
address: address,
cardPublicKey: cardPublicKey,
deviceActivationCode: deviceActivationCode,
scaProof: scaProof,
);
}
}

View File

@@ -1,7 +1,7 @@
import 'package:auth/auth.dart';
import 'package:auth/src/features/device_sign_up/add_kid_screen.dart';
import 'package:auth/src/features/device_sign_up/link_watch/link_watch_screen.dart';
import 'package:auth/src/features/device_sign_up/link_watch/link_watch_previous_screen.dart';
import 'package:auth/src/features/device_sign_up/presentation/add_kid_screen.dart';
import 'package:auth/src/features/device_sign_up/presentation/link_watch/link_watch_screen.dart';
import 'package:auth/src/features/device_sign_up/presentation/link_watch/link_watch_previous_screen.dart';
import 'package:auth/src/features/sign_up/presentation/screens/account_created_screen.dart';
import 'package:auth/src/widgets/layouts/form_step_layout.dart';
import 'package:design_system/design_system.dart';

View File

@@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
class RequestLinkPhoneScreen extends ConsumerWidget {
final NavigationContract navigationContract;
@@ -28,22 +27,19 @@ class RequestLinkPhoneScreen extends ConsumerWidget {
Text(
context.translate(I18n.linkPhoneTitle),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 30, big: 28, xl: 26),
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.w500,
letterSpacing: 0,
),
),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 22, xl: 20)),
const SizedBox(height: 24),
Text(
context.translate(I18n.linkPhoneSubtitle),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 16, big: 16, xl: 14),
letterSpacing: 0
),
style: const TextStyle(fontSize: 16, letterSpacing: 0),
),
SizedBox(height: SizeUtils.getByScreen(small: 48, big: 46, xl: 44)),
const SizedBox(height: 48),
Column(
spacing: 8,
@@ -93,7 +89,7 @@ class RequestLinkPhoneScreen extends ConsumerWidget {
),
],
SizedBox(height: SizeUtils.getByScreen(small: 10, big: 10)),
const SizedBox(height: 24),
PrimaryButton(
onPressed: () async {

View File

@@ -5,7 +5,6 @@ import 'package:flutter/material.dart';
import 'package:navigation/navigation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
class VerifyLinkPhoneCodeScreen extends ConsumerWidget {
final NavigationContract navigationContract;
@@ -35,32 +34,30 @@ class VerifyLinkPhoneCodeScreen extends ConsumerWidget {
children: [
Text(
context.translate(I18n.connect),
style: TextStyle(fontWeight: FontWeight.bold, fontSize: SizeUtils.getByScreen(small: 30, big: 28)),
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
),
const SizedBox(height: 24),
Text.rich(
TextSpan(
text: context.translate(I18n.verificationCodeSentTo),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 15),
),
children: [
TextSpan(
text: '${viewState.dialCode}${viewState.phoneNumber}',
style: TextStyle(
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
],
),
textAlign: TextAlign.center,
),
SizedBox(height: SizeUtils.getByScreen(small: 48, big: 42, xl: 36)),
const SizedBox(height: 48),
Text(
context.translate(I18n.enterCodeHere),
style: TextStyle(fontSize: 16),
),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 24, xl: 20)),
const SizedBox(height: 24),
LinkPhoneCodeInput(
length: 6,
@@ -93,22 +90,21 @@ class VerifyLinkPhoneCodeScreen extends ConsumerWidget {
},
text: context.translate(I18n.enter),
color: theme.getColorFor(ThemeCode.buttonPrimary),
size: SizeUtils.getByScreen(small: 18, big: 17, xl: 16),
),
const SizedBox(height: 24),
Text(
context.translate(I18n.didNotReceiveIt),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 18, big: 17, xl: 16),
fontSize: 18,
letterSpacing: 0,
height: SizeUtils.getByScreen(small: 1.5, big: 0),
height: 1.5,
),
),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 0)),
const SizedBox(height: 8),
CustomTextButton(
onPressed: () => navigationContract.goBack(),
text: context.translate(I18n.tryAgain),
size: SizeUtils.getByScreen(small: 18, big: 17, xl: 16),
size: 18,
weight: FontWeight.w500,
),
],
@@ -119,4 +115,4 @@ class VerifyLinkPhoneCodeScreen extends ConsumerWidget {
),
);
}
}
}

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:utils/utils.dart';
class LinkPhoneCodeInput extends StatefulWidget {
const LinkPhoneCodeInput({
@@ -75,9 +74,8 @@ class _LinkPhoneCodeInputState extends State<LinkPhoneCodeInput> {
decoration: const InputDecoration(
hintText: '0',
counterText: '',
border: OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(14))),
border: OutlineInputBorder(),
),
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 20, big: 22, xl: 17)),
maxLength: 1,
onChanged: (value) => _onDigitChanged(i, value),
),
@@ -85,4 +83,4 @@ class _LinkPhoneCodeInputState extends State<LinkPhoneCodeInput> {
}),
);
}
}
}

View File

@@ -6,7 +6,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
class LoginScreen extends ConsumerWidget {
final NavigationContract navigationContract;
@@ -99,28 +98,27 @@ class LoginScreen extends ConsumerWidget {
child: AbsorbPointer(
absorbing: isLoading,
child: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: SizeUtils.getByScreen(small: 24, big: 24)),
padding: EdgeInsets.symmetric(horizontal: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: SizeUtils.getByScreen(small: 0, big: 30, xl: 44)),
_Header(theme: theme),
SizedBox(height: SizeUtils.getByScreen(small: 26, big: 30, xl: 40)),
SizedBox(height: 48),
const _EmailSection(),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 24, xl: 20)),
SizedBox(height: 24),
_PasswordSection(onSubmitted: () => _onLogIn(context, ref)),
SizedBox(height: 16),
_ForgotPassword(navigationContract: navigationContract),
SizedBox(height: SizeUtils.getByScreen(small: 0, big: 24, xl: 22)),
SizedBox(height: 30),
_SignInSection(
theme: theme,
onSignIn: () => _onLogIn(context, ref),
),
SizedBox(height: SizeUtils.getByScreen(small: 30, big: 60, xl: 56)),
SizedBox(height: 30),
_OrContinueWith(theme: theme),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 24, xl: 26)),
SizedBox(height: 24),
_SocialButtons(theme: theme),
SizedBox(height: SizeUtils.getByScreen(small: 18, big: 36)),
SizedBox(height: 30),
_Footer(navigationContract: navigationContract),
],
),
@@ -142,15 +140,12 @@ class _Header extends StatelessWidget {
Icon(
Icons.check,
color: theme.getColorFor(ThemeCode.buttonPrimary),
size: SizeUtils.getByScreen(small: 54, big: 50, xl: 46),
size: 54,
),
Text(
context.translate(I18n.welcome),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 30, big: 28, xl: 27),
fontWeight: FontWeight.w500
),
style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
),
],
);
@@ -173,7 +168,6 @@ class _EmailSection extends ConsumerWidget {
CustomTextField(
hint: context.translate(I18n.username),
label: context.translate(I18n.username),
labelSize: SizeUtils.getByScreen(small: 14, big: 14, xl: 12),
controller: vm.emailController,
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
@@ -205,7 +199,6 @@ class _PasswordSection extends ConsumerWidget {
CustomTextField(
showPassword: passwordVisible,
label: context.translate(I18n.password),
labelSize: SizeUtils.getByScreen(small: 14, big: 14, xl: 12),
hint: '********',
controller: vm.passwordController,
textInputAction: TextInputAction.done,
@@ -234,7 +227,7 @@ class _ForgotPassword extends ConsumerWidget {
onPressed: isLoading
? () {}
: () => navigationContract.pushTo(AppRoutes.recoverPassword),
size: SizeUtils.getByScreen(small: 16, big: 15, xl: 14),
size: 16,
),
);
}
@@ -272,7 +265,6 @@ class _SignInSection extends ConsumerWidget {
),
)
: null,
size: SizeUtils.getByScreen(small: 18, big: 17, xl: 16),
),
if (errorMessage.isNotEmpty)
Padding(
@@ -282,7 +274,7 @@ class _SignInSection extends ConsumerWidget {
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
fontSize: SizeUtils.getByScreen(small: 13, big: 12, xl: 11),
fontSize: 13,
),
),
),
@@ -336,7 +328,7 @@ class _SocialButtons extends ConsumerWidget {
),
),
radius: 16,
padding: SizeUtils.getByScreen(small: 44, big: 40, xl: 36),
padding: 44,
text: context.translate(I18n.google),
label: 'Google',
),
@@ -344,7 +336,7 @@ class _SocialButtons extends ConsumerWidget {
SecondaryButton(
onPressed: isLoading ? () {} : () {},
radius: 16,
padding: SizeUtils.getByScreen(small: 44, big: 40, xl: 36),
padding: 44,
icon: Icons.apple,
label: 'Apple',
),
@@ -367,15 +359,20 @@ class _Footer extends ConsumerWidget {
children: [
Text(
context.translate(I18n.dontHaveAccount),
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 18, big: 17, xl: 16), letterSpacing: 0, height: 0),
style: const TextStyle(fontSize: 18, letterSpacing: 0),
),
CustomTextButton(
TextButton(
onPressed: isLoading
? null
: () => navigationContract.pushTo(AppRoutes.signup),
text: context.translate(I18n.createOneNow),
size: SizeUtils.getByScreen(small: 18, big: 17, xl: 16),
weight: FontWeight.w500,
child: Text(
context.translate(I18n.createOneNow),
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
letterSpacing: 0,
),
),
),
],
);

View File

@@ -3,7 +3,6 @@ import 'dart:async';
import 'package:auth/src/features/login/presentation/widgets/otp_code_fields.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:utils/utils.dart';
class TwoFactorBottomSheetView extends StatelessWidget {
const TwoFactorBottomSheetView({
@@ -83,9 +82,8 @@ class TwoFactorBottomSheetView extends StatelessWidget {
if (isOtpLoading || !_isValidOtp) return;
unawaited(onVerify());
},
gap: SizeUtils.getByScreen(small: 10, big: 8, xl: 4),
),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 38)),
const SizedBox(height: 20),
PrimaryButton(
onPressed: (isOtpLoading || !_isValidOtp)
@@ -105,7 +103,7 @@ class TwoFactorBottomSheetView extends StatelessWidget {
: null,
),
SizedBox(height: SizeUtils.getByScreen(small: 12, big: 9)),
const SizedBox(height: 12),
TextButton(
onPressed: isOtpLoading ? null : onClose,

View File

@@ -1,7 +1,8 @@
abstract class RecoverPasswordUseCase {
Future<String> requestEmail({required String email});
Future<String> requestSms({required String phone});
Future<void> recoverPassword({required String newPassword, required String token});
}
Future<void> recoverPassword({
required String newPassword,
required String token,
});
}

View File

@@ -12,12 +12,10 @@ class RecoverPasswordUseCaseImpl implements RecoverPasswordUseCase {
}
@override
Future<String> requestSms({required String phone}) async {
return await _repository.requestPasswordReset(phone: phone);
}
@override
Future<void> recoverPassword({required String newPassword, required String token}) async {
Future<void> recoverPassword({
required String newPassword,
required String token,
}) async {
await _repository.recoverPassword(newPassword: newPassword, token: token);
}
}

View File

@@ -45,7 +45,6 @@ class NewPasswordScreen extends ConsumerWidget {
labelSize: SizeUtils.getByScreen(small: 14, big: 14, xl: 12),
hint: '********',
controller: viewModel.passwordController,
// onVisibilityChanged: viewModel.togglePasswordVisible,
),
SizedBox(height: 16),
CustomTextField(
@@ -54,11 +53,9 @@ class NewPasswordScreen extends ConsumerWidget {
labelSize: SizeUtils.getByScreen(small: 14, big: 14, xl: 12),
hint: '********',
controller: viewModel.repeatedPasswordController,
// onVisibilityChanged: viewModel.togglePasswordVisible,
// color: viewState.equalPasswords ? const Color(0xFF4B4B4B) : const Color.fromRGBO(239, 17, 17, 1),
),
if (!viewState.equalPasswords) ...[
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 8)),
SizedBox(height: 4),
Row(
spacing: 8,
children: [
@@ -79,7 +76,7 @@ class NewPasswordScreen extends ConsumerWidget {
],
),
],
SizedBox(height: SizeUtils.getByScreen(small: 14, big: 12)),
SizedBox(height: 12),
Row(
spacing: 8,
children: [
@@ -176,43 +173,7 @@ class NewPasswordScreen extends ConsumerWidget {
],
),
SizedBox(
height: SizeUtils.getByScreen(small: 28, big: 32, xl: 24),
),
Align(
alignment: Alignment.bottomLeft,
child: Text(
context.translate(I18n.mobilePhone),
style: TextStyle(
fontSize: SizeUtils.getByScreen(
small: 14,
big: 14,
xl: 12,
),
letterSpacing: 0,
),
),
),
SizedBox(height: 8),
Row(
spacing: 8,
children: [
CountryPrefixPicker(
headerText: context.translate(I18n.selectYourCountry),
width: 80,
onChanged: (country) {
viewModel.updateDialCode(
country.dialCode ?? viewState.dialCode,
);
},
),
Expanded(
child: CustomTextField(
hint: context.translate(I18n.phoneNumber),
keyboardType: TextInputType.number,
controller: viewModel.newPhoneNumberController,
),
),
],
height: SizeUtils.getByScreen(small: 32, big: 32, xl: 24),
),
if (viewState.errorMessage.isNotEmpty) ...[
SizedBox(height: 10),

View File

@@ -23,70 +23,52 @@ class RequestRecoveryScreen extends ConsumerWidget {
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: Container(
margin: EdgeInsets.symmetric(horizontal: SizeUtils.getByScreen(small: 30, big: 30, xl: 20)),
margin: EdgeInsets.all(
SizeUtils.getByScreen(small: 30, big: 30, xl: 20),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
context.translate(I18n.recoverPasswordTitle),
style: TextStyle(fontWeight: FontWeight.w500, fontSize: SizeUtils.getByScreen(small: 29, big: 27, xl: 26)),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: SizeUtils.getByScreen(small: 29, big: 28, xl: 26),
),
),
SizedBox(height: SizeUtils.getByScreen(small: 24, big: 32)),
Text(
context.translate(I18n.recoverPasswordSubtitle),
textAlign: TextAlign.center,
style: TextStyle(letterSpacing: 0, fontSize: SizeUtils.getByScreen(small: 18, big: 18, xl: 16)),
style: TextStyle(
letterSpacing: 0,
fontSize: SizeUtils.getByScreen(small: 18, big: 18, xl: 16),
),
),
SizedBox(height: SizeUtils.getByScreen(small: 56, big: 52)),
SizedBox(height: SizeUtils.getByScreen(small: 56, big: 48)),
CustomTextField(
label: context.translate(I18n.email),
hint: context.translate(I18n.email),
controller: viewModel.emailController,
),
SizedBox(height: SizeUtils.getByScreen(small: 36, big: 36, xl: 26)),
Align(
alignment: Alignment.bottomLeft,
child: Text(
context.translate(I18n.mobilePhone),
style: TextStyle(fontSize: 14, letterSpacing: 0),
),
SizedBox(
height: SizeUtils.getByScreen(small: 20, big: 20, xl: 28),
),
SizedBox(height: 8),
Row(
children: [
CountryPrefixPicker(
headerText: context.translate(I18n.selectYourCountry),
initialCountryCode: viewState.dialCode,
onChanged: (country) {
viewModel.updateDialCode(
country.dialCode ?? viewState.dialCode,
);
},
width: 80,
),
SizedBox(width: SizeUtils.getByScreen(small: 10, big: 10, xl: 6)),
Expanded(
child: CustomTextField(
hint: context.translate(I18n.phoneNumber),
keyboardType: TextInputType.number,
controller: viewModel.phoneNumberController,
),
),
],
),
SizedBox(height: SizeUtils.getByScreen(small: 40, big: 40, xl: 28)),
if (viewState.errorMessage.isNotEmpty) ...[
Text(
context.translate(viewState.errorMessage),
textAlign: TextAlign.center,
style: const TextStyle(
color: Color.fromRGBO(239, 17, 17, 1),
fontSize: 12,
),
Text(
context.translate(viewState.errorMessage),
textAlign: TextAlign.center,
style: const TextStyle(
color: Color.fromRGBO(239, 17, 17, 1),
fontSize: 12,
),
SizedBox(height: 40),
],
),
],
SizedBox(
height: SizeUtils.getByScreen(small: 10, big: 10, xl: 18),
),
Row(
children: [
Expanded(
@@ -96,17 +78,23 @@ class RequestRecoveryScreen extends ConsumerWidget {
size: SizeUtils.getByScreen(small: 16, big: 16, xl: 14),
),
),
SizedBox(width: SizeUtils.getByScreen(small: 20, big: 20, xl: 10)),
SizedBox(
width: SizeUtils.getByScreen(small: 20, big: 20, xl: 10),
),
Expanded(
child: PrimaryButton(
onPressed: () async {
await viewModel.requestRecovery();
final updatedState = ref.read(recoverPasswordViewModelProvider);
final updatedState = ref.read(
recoverPasswordViewModelProvider,
);
if (updatedState.recoveryRequested) {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => SentScreen(navigationContract: navigationContract),
builder: (_) => SentScreen(
navigationContract: navigationContract,
),
),
);
}

View File

@@ -37,7 +37,9 @@ class SentScreen extends ConsumerWidget {
letterSpacing: 0,
),
),
SizedBox(height: SizeUtils.getByScreen(small: 48, big: 48, xl: 40)),
SizedBox(
height: SizeUtils.getByScreen(small: 48, big: 48, xl: 40),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@@ -45,41 +47,58 @@ class SentScreen extends ConsumerWidget {
Icons.check,
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
SizedBox(width: SizeUtils.getByScreen(small: 10, big: 10, xl: 6)),
SizedBox(
width: SizeUtils.getByScreen(small: 10, big: 10, xl: 6),
),
Text(
viewState.recoveryFormat == "email"
? context.translate(I18n.emailSent)
: context.translate(I18n.smsSent),
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 18, big: 18, xl: 15), fontWeight: FontWeight.bold),
style: TextStyle(
fontSize: SizeUtils.getByScreen(
small: 18,
big: 18,
xl: 15,
),
fontWeight: FontWeight.bold,
),
),
],
),
SizedBox(height: SizeUtils.getByScreen(small: 48, big: 48, xl: 40)),
SizedBox(
height: SizeUtils.getByScreen(small: 48, big: 48, xl: 40),
),
Text(
viewState.recoveryFormat == "email"
? context.translate(I18n.checkEmail1)
: context.translate(I18n.checkSms1),
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 17, big: 17, xl: 14), letterSpacing: 0),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 17, big: 17, xl: 15),
letterSpacing: 0,
),
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 16, xl: 14)),
SizedBox(height: 16),
Text(
viewState.recoveryFormat == "email"
? context.translate(I18n.checkEmail2)
: context.translate(I18n.checkSms2),
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 14, big: 14, xl: 12), letterSpacing: 0),
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 14, big: 14, xl: 12),
letterSpacing: 0,
),
),
SizedBox(
height: SizeUtils.getByScreen(small: 48, big: 48, xl: 40),
),
SizedBox(height: SizeUtils.getByScreen(small: 48, big: 48, xl: 38)),
Row(
children: [
Expanded(
child: SecondaryButton(
onPressed: () {
if ( viewState.recoveryFormat == "email") {
if (viewState.recoveryFormat == "email") {
viewModel.requestEmail();
} else {
viewModel.requestSms();
}
},
text: viewState.recoveryFormat == "email"
@@ -93,7 +112,11 @@ class SentScreen extends ConsumerWidget {
child: PrimaryButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => NewPasswordScreen(navigationContract: navigationContract)),
MaterialPageRoute(
builder: (_) => NewPasswordScreen(
navigationContract: navigationContract,
),
),
),
text: context.translate(I18n.continueKey),
color: theme.getColorFor(ThemeCode.buttonSecondary),

View File

@@ -6,9 +6,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../providers/recover_password_provider.dart';
final recoverPasswordViewModelProvider =
NotifierProvider.autoDispose<RecoverPasswordViewModel, RecoverPasswordViewState>(
RecoverPasswordViewModel.new,
);
NotifierProvider.autoDispose<
RecoverPasswordViewModel,
RecoverPasswordViewState
>(RecoverPasswordViewModel.new);
class RecoverPasswordViewModel extends Notifier<RecoverPasswordViewState> {
late final RecoverPasswordUseCase _recoverPasswordUseCase;
@@ -22,9 +23,6 @@ class RecoverPasswordViewModel extends Notifier<RecoverPasswordViewState> {
RecoverPasswordViewState build() {
_recoverPasswordUseCase = ref.read(recoverPasswordUseCaseProvider);
phoneNumberController = TextEditingController();
phoneNumberController.addListener(_onPhoneNumberChanged);
emailController = TextEditingController();
emailController.addListener(_onEmailChanged);
@@ -34,32 +32,11 @@ class RecoverPasswordViewModel extends Notifier<RecoverPasswordViewState> {
repeatedPasswordController = TextEditingController();
repeatedPasswordController.addListener(_onRepeatedPasswordChanged);
newPhoneNumberController = TextEditingController();
newPhoneNumberController.addListener(_onNewPhoneNumberChanged);
ref.onDispose(disposeControllers);
return const RecoverPasswordViewState();
}
void _onPhoneNumberChanged() {
final String raw = phoneNumberController.text;
state = state.copyWith(
phoneNumber: raw,
errorMessage: '',
recoveryRequested: false,
);
}
void _onNewPhoneNumberChanged() {
final String raw = newPhoneNumberController.text;
state = state.copyWith(
newPhoneNumber: raw,
errorMessage: '',
recoveryRequested: false,
);
}
void _onEmailChanged() {
final String raw = emailController.text;
state = state.copyWith(
@@ -103,28 +80,11 @@ class RecoverPasswordViewModel extends Notifier<RecoverPasswordViewState> {
);
}
void updateDialCode(String dialCode) {
state = state.copyWith(
dialCode: dialCode,
errorMessage: '',
);
}
void updateNewDialCode(String dialCode) {
state = state.copyWith(
newDialCode: dialCode,
errorMessage: '',
);
}
void togglePasswordVisible(){
state = state.copyWith(
passwordVisible: !state.passwordVisible,
);
void togglePasswordVisible() {
state = state.copyWith(passwordVisible: !state.passwordVisible);
}
Future<void> requestRecovery() async {
final trimmedNumber = state.phoneNumber.trim();
final email = state.email.trim();
state = state.copyWith(
@@ -135,8 +95,6 @@ class RecoverPasswordViewModel extends Notifier<RecoverPasswordViewState> {
if (email.isNotEmpty) {
await requestEmail();
} else if (trimmedNumber.isNotEmpty) {
await requestSms();
} else {
state = state.copyWith(
isLoading: false,
@@ -150,7 +108,9 @@ class RecoverPasswordViewModel extends Notifier<RecoverPasswordViewState> {
final email = state.email.trim();
try {
final String token = await _recoverPasswordUseCase.requestEmail(email: email);
final String token = await _recoverPasswordUseCase.requestEmail(
email: email,
);
if (!ref.mounted) return;
state = state.copyWith(
@@ -172,34 +132,6 @@ class RecoverPasswordViewModel extends Notifier<RecoverPasswordViewState> {
}
}
Future<void> requestSms() async {
final trimmedNumber = state.phoneNumber.trim();
final fullPhone = '${state.dialCode}$trimmedNumber';
try {
final String token = await _recoverPasswordUseCase.requestSms(phone: fullPhone);
if (!ref.mounted) return;
state = state.copyWith(
isLoading: false,
errorMessage: '',
recoveryRequested: true,
token: token,
recoveryFormat: 'sms'
);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(
isLoading: false,
errorMessage: e.toString(),
recoveryRequested: false,
passwordChanged: false,
);
}
}
Future<void> recoverPassword() async {
//final String fullPhone = state.newDialCode + state.newPhoneNumber;
final String password = state.password;
@@ -244,17 +176,13 @@ class RecoverPasswordViewModel extends Notifier<RecoverPasswordViewState> {
return;
}
state = state.copyWith(
isLoading: true,
passwordChanged: false,
);
state = state.copyWith(isLoading: true, passwordChanged: false);
try {
await _recoverPasswordUseCase.recoverPassword(
newPassword: password, token: state.token);
state = state.copyWith(
isLoading: false,
passwordChanged: true,
newPassword: password,
token: state.token,
);
state = state.copyWith(isLoading: false, passwordChanged: true);
} catch (error) {
state = state.copyWith(
errorMessage: error.toString(),
@@ -265,15 +193,11 @@ class RecoverPasswordViewModel extends Notifier<RecoverPasswordViewState> {
}
void disposeControllers() {
phoneNumberController.removeListener(_onPhoneNumberChanged);
phoneNumberController.dispose();
emailController.removeListener(_onPhoneNumberChanged);
emailController.removeListener(_onEmailChanged);
emailController.dispose();
passwordController.removeListener(_onPasswordChanged);
passwordController.dispose();
repeatedPasswordController.removeListener(_onRepeatedPasswordChanged);
repeatedPasswordController.dispose();
newPhoneNumberController.removeListener(_onNewPhoneNumberChanged);
newPhoneNumberController.dispose();
}
}
}

View File

@@ -2,7 +2,6 @@ import 'package:country_code_picker/country_code_picker.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
class SignupAddressScreen extends StatelessWidget {
const SignupAddressScreen({
@@ -146,9 +145,7 @@ class SignupAddressScreen extends StatelessWidget {
const SizedBox(height: 8),
Align(
alignment: Alignment.bottomLeft,
child: Text(birthCountryLabel, style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 14, big: 13, xl: 12),
)),
child: Text(birthCountryLabel, style: const TextStyle(fontSize: 14)),
),
const SizedBox(height: 8),
Row(
@@ -171,7 +168,6 @@ class SignupAddressScreen extends StatelessWidget {
CustomTextField(
label: streetLabel,
labelSize: SizeUtils.getByScreen(small: 14, big: 13, xl: 12),
hint: streetHint,
controller: streetController,
),
@@ -179,7 +175,6 @@ class SignupAddressScreen extends StatelessWidget {
CustomTextField(
label: cityLabel,
labelSize: SizeUtils.getByScreen(small: 14, big: 13, xl: 12),
hint: cityHint,
controller: cityController,
),
@@ -187,7 +182,6 @@ class SignupAddressScreen extends StatelessWidget {
CustomTextField(
label: provinceLabel,
labelSize: SizeUtils.getByScreen(small: 14, big: 13, xl: 12),
hint: provinceHint,
controller: provinceController,
),
@@ -195,7 +189,6 @@ class SignupAddressScreen extends StatelessWidget {
CustomTextField(
label: stateLabel,
labelSize: SizeUtils.getByScreen(small: 14, big: 13, xl: 12),
hint: stateHint,
controller: stateController,
),
@@ -229,7 +222,6 @@ class SignupAddressScreen extends StatelessWidget {
CustomTextField(
label: postCodeLabel,
labelSize: SizeUtils.getByScreen(small: 14, big: 13, xl: 12),
hint: postCodeHint,
keyboardType: TextInputType.number,
controller: postCodeController,
@@ -237,4 +229,4 @@ class SignupAddressScreen extends StatelessWidget {
],
);
}
}
}

View File

@@ -2,7 +2,6 @@ import 'package:country_code_picker/country_code_picker.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:sf_localizations/sf_localizations.dart';
import 'package:utils/utils.dart';
class SignupPersonalScreen extends StatelessWidget {
final TextEditingController firstNameTextFieldController;
@@ -73,14 +72,12 @@ class SignupPersonalScreen extends StatelessWidget {
children: [
CustomTextField(
label: firstNameLabel,
labelSize: SizeUtils.getByScreen(small: 14, big: 13, xl: 12),
hint: firstNameHint,
controller: firstNameTextFieldController,
),
const SizedBox(height: 8),
CustomTextField(
label: lastNameLabel,
labelSize: SizeUtils.getByScreen(small: 14, big: 13, xl: 12),
hint: lastNameHint,
controller: lastNameTextFieldController,
),
@@ -102,7 +99,6 @@ class SignupPersonalScreen extends StatelessWidget {
Expanded(
child: CustomTextField(
label: documentNumberLabel,
labelSize: SizeUtils.getByScreen(small: 14, big: 13, xl: 12),
hint: documentNumberHint,
controller: documentNumberTextFieldController,
),
@@ -112,9 +108,7 @@ class SignupPersonalScreen extends StatelessWidget {
const SizedBox(height: 8),
Align(
alignment: Alignment.bottomLeft,
child: Text(phoneLabel, style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 14, big: 13, xl: 12)
)),
child: Text(phoneLabel, style: const TextStyle(fontSize: 14)),
),
const SizedBox(height: 8),
Row(
@@ -136,12 +130,11 @@ class SignupPersonalScreen extends StatelessWidget {
const SizedBox(height: 8),
CustomTextField(
label: emailLabel,
labelSize: SizeUtils.getByScreen(small: 14, big: 13, xl: 12),
hint: emailHint,
keyboardType: TextInputType.emailAddress,
controller: emailTextFieldController,
),
const SizedBox(height: 8),
CheckboxListTile(
value: acceptTerms,
onChanged: onAcceptTermsPressed,

View File

@@ -1,7 +1,6 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:utils/utils.dart';
class FormStepLayout extends ConsumerWidget {
final int currentStep;
@@ -71,7 +70,7 @@ class FormStepLayout extends ConsumerWidget {
Text(
subtitle!,
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 18, big: 17)),
style: TextStyle(fontSize: 18),
),
],
),

View File

@@ -1,7 +1,6 @@
import 'package:auth/src/widgets/form_error_banner.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:utils/utils.dart';
class SignUpLayout extends StatelessWidget {
final ThemePort theme;
@@ -54,24 +53,24 @@ class SignUpLayout extends StatelessWidget {
Text(
supertitle,
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 18, big: 16, xl: 15)),
style: const TextStyle(fontSize: 18),
),
SizedBox(height: SizeUtils.getByScreen(small: 8, big: 5)),
const SizedBox(height: 10),
Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: SizeUtils.getByScreen(small: 30, big: 28, xl: 26),
fontWeight: FontWeight.w500,
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
Text(
subtitle,
textAlign: TextAlign.center,
style: TextStyle(fontSize: SizeUtils.getByScreen(small: 18, big: 17, xl: 15)),
style: const TextStyle(fontSize: 18),
),
SizedBox(height: SizeUtils.getByScreen(small: 16, big: 20, xl: 36)),
const SizedBox(height: 16),
Expanded(
child: SingleChildScrollView(

View File

@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
class CustomTextButton extends StatelessWidget {
final VoidCallback? onPressed;
final VoidCallback onPressed;
final String text;
final double size;
final FontWeight weight;

View File

@@ -76,18 +76,18 @@ class CustomTextFieldState extends State<CustomTextField> {
gapPadding: 16,
),
suffixIcon: widget.showPassword != null
? IconButton(
icon: Icon(
_showPassword ? Icons.visibility_off_outlined : Icons.visibility_outlined,
),
onPressed: () {
setState(() {
_showPassword = !_showPassword;
});
},
//onpressed: widget.onVisibilityChanged,
)
: null,
? IconButton(
icon: Icon(
_showPassword ? Icons.visibility_off : Icons.visibility,
),
onPressed: () {
setState(() {
_showPassword = !_showPassword;
});
},
//onpressed: widget.onVisibilityChanged,
)
: null,
),
minLines: widget.lines ?? 1,
maxLines: widget.lines ?? 1,

View File

@@ -10,7 +10,7 @@
"next": "Siguiente",
"skip": "Omitir",
"linkPhoneTitle": "¡Nos alegra mucho tenerte por aquí!",
"linkPhoneSubtitle": "Para poder entrar de forma segura,\nte vamos a enviar un código al teléfono",
"linkPhoneSubtitle": "Para poder entrar de forma segura, te vamos a enviar un código al teléfono",
"mobilePhone": "Teléfono móvil",
"phoneNumber": "Teléfono",
"selectYourCountry": "Selecciona tu país",
@@ -18,7 +18,7 @@
"connect": "Conéctate",
"verificationCodeSentTo": "Hemos enviado el código al ",
"enterCodeHere": "Introduce el código aquí",
"enter": "Entrar",
"enter": "entrar",
"didNotReceiveIt": "¿No lo has recibido?",
"tryAgain": "Volver a intentarlo",
"welcome": "¡Te damos la bienvenida!",