Merge remote-tracking branch 'origin/auth-login-and-2fa-login' into auth-recover-password
# Conflicts: # modules/auth/lib/src/core/data/datasource/auth_remote_datasource.dart # modules/auth/lib/src/core/data/datasource/auth_remote_datasource_impl.dart # modules/auth/lib/src/core/data/repositories/auth_repository_impl.dart # modules/auth/lib/src/core/domain/repositories/auth_repository.dart # modules/auth/lib/src/features/recover_password/presentation/new_password_screen.dart # modules/auth/lib/src/features/recover_password/presentation/restore_password_screen.dart # packages/design_system/lib/src/inputs/textfields.dart # packages/sf_localizations/assets/l10n/de.json # packages/sf_localizations/assets/l10n/en.json # packages/sf_localizations/assets/l10n/es.json # packages/sf_localizations/assets/l10n/fr.json # packages/sf_localizations/assets/l10n/it.json # packages/sf_localizations/assets/l10n/pt.json # packages/sf_localizations/lib/src/generated/i18n.dart
This commit is contained in:
@@ -3,6 +3,10 @@ abstract class AuthRemoteDatasource {
|
|||||||
|
|
||||||
Future<void> verifyPhoneCode({required String phone, required String code});
|
Future<void> verifyPhoneCode({required String phone, required String code});
|
||||||
|
|
||||||
|
Future<String> login({required String email, required String password});
|
||||||
|
|
||||||
|
Future<void> twoFALogin({required String token, required String code});
|
||||||
|
|
||||||
Future<String> requestPasswordReset({String? phone, String? email});
|
Future<String> requestPasswordReset({String? phone, String? email});
|
||||||
|
|
||||||
Future<void> recoverPassword({required newPassword, required token});
|
Future<void> recoverPassword({required newPassword, required token});
|
||||||
|
|||||||
@@ -54,6 +54,35 @@ class AuthRemoteDatasourceImpl implements AuthRemoteDatasource {
|
|||||||
return Exception(message);
|
return Exception(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> login({
|
||||||
|
required String email,
|
||||||
|
required String password,
|
||||||
|
}) async {
|
||||||
|
try {
|
||||||
|
final response = await _repository.post<Map<String, dynamic>>(
|
||||||
|
'/auth/login',
|
||||||
|
body: <String, dynamic>{'email': email, 'password': password},
|
||||||
|
);
|
||||||
|
final token = response.data!['token'];
|
||||||
|
return token;
|
||||||
|
} on DioException catch (error) {
|
||||||
|
throw _mapDioError(error, defaultMessage: 'Error in login');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> twoFALogin({required String token, required String code}) async {
|
||||||
|
try {
|
||||||
|
await _repository.post<Map<String, dynamic>>(
|
||||||
|
'/auth/login',
|
||||||
|
body: <String, dynamic>{'token': token, 'password': code},
|
||||||
|
);
|
||||||
|
} on DioException catch (error) {
|
||||||
|
throw _mapDioError(error, defaultMessage: 'Error in login');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> requestPasswordReset({
|
Future<String> requestPasswordReset({
|
||||||
String? phone,
|
String? phone,
|
||||||
|
|||||||
@@ -16,6 +16,16 @@ class AuthRepositoryImpl implements AuthRepository {
|
|||||||
return _remote.verifyPhoneCode(phone: phone, code: code);
|
return _remote.verifyPhoneCode(phone: phone, code: code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<String> login({required String email, required String password}) {
|
||||||
|
return _remote.login(email: email, password: password);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> twoFALogin({required String token, required String code}) {
|
||||||
|
return _remote.twoFALogin(token: token, code: code);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> requestPasswordReset({String? phone, String? email}) {
|
Future<String> requestPasswordReset({String? phone, String? email}) {
|
||||||
return _remote.requestPasswordReset(phone: phone, email: email);
|
return _remote.requestPasswordReset(phone: phone, email: email);
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ abstract class AuthRepository {
|
|||||||
|
|
||||||
Future<void> verifyPhoneCode({required String phone, required String code});
|
Future<void> verifyPhoneCode({required String phone, required String code});
|
||||||
|
|
||||||
|
Future<String> login({required String email, required String password});
|
||||||
|
|
||||||
|
Future<void> twoFALogin({required String token, required String code});
|
||||||
|
|
||||||
Future<String> requestPasswordReset({String phone, String email});
|
Future<String> requestPasswordReset({String phone, String email});
|
||||||
|
|
||||||
Future<void> recoverPassword({required String newPassword, required String token});
|
Future<void> recoverPassword({required String newPassword, required String token});
|
||||||
|
|||||||
@@ -17,16 +17,14 @@ class CreateProfileScreen extends ConsumerWidget {
|
|||||||
Text(
|
Text(
|
||||||
"Comienza con un peque; luego podrás agregar más",
|
"Comienza con un peque; luego podrás agregar más",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, letterSpacing: 0),
|
style: TextStyle(
|
||||||
),
|
fontSize: 14,
|
||||||
CustomTextField(
|
fontWeight: FontWeight.w500,
|
||||||
label: "Nombre",
|
letterSpacing: 0,
|
||||||
hint: "Nombre",
|
),
|
||||||
),
|
|
||||||
CustomTextField(
|
|
||||||
label: "Apellidos",
|
|
||||||
hint: "Apellidos",
|
|
||||||
),
|
),
|
||||||
|
CustomTextField(label: "Nombre", hint: "Nombre"),
|
||||||
|
CustomTextField(label: "Apellidos", hint: "Apellidos"),
|
||||||
Column(
|
Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
@@ -42,21 +40,21 @@ class CreateProfileScreen extends ConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: CustomTextField(
|
child: CustomTextField(
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
hint: "DD",
|
hint: "DD",
|
||||||
length: 2,
|
length: 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: CustomTextField(
|
child: CustomTextField(
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
hint: "MM",
|
hint: "MM",
|
||||||
length: 2,
|
length: 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: CustomTextField(
|
child: CustomTextField(
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
hint: "AAAA",
|
hint: "AAAA",
|
||||||
length: 4,
|
length: 4,
|
||||||
),
|
),
|
||||||
@@ -77,7 +75,7 @@ class CreateProfileScreen extends ConsumerWidget {
|
|||||||
size: 18,
|
size: 18,
|
||||||
weight: FontWeight.w500,
|
weight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class RequestLinkPhoneScreen extends ConsumerWidget {
|
|||||||
child: CustomTextField(
|
child: CustomTextField(
|
||||||
controller: viewModel.phoneNumberController,
|
controller: viewModel.phoneNumberController,
|
||||||
hint: context.translate(I18n.phoneNumber),
|
hint: context.translate(I18n.phoneNumber),
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
abstract class LoginUseCase {
|
||||||
|
Future<void> login({required String email, required String password});
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import 'package:auth/src/core/domain/repositories/auth_repository.dart';
|
||||||
|
import 'package:auth/src/features/login/domain/login_use_case.dart';
|
||||||
|
|
||||||
|
class LoginUseCaseImpl implements LoginUseCase {
|
||||||
|
LoginUseCaseImpl(this._repository);
|
||||||
|
|
||||||
|
final AuthRepository _repository;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> login({required String email, required String password}) {
|
||||||
|
return _repository.login(email: email, password: password);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,172 +1,165 @@
|
|||||||
import 'package:auth/src/features/login/presentation/loading_google_screen.dart';
|
import 'package:auth/src/features/login/presentation/loading_google_screen.dart';
|
||||||
import 'package:auth/src/features/sign_up/signup_screen.dart';
|
import 'package:auth/src/features/login/presentation/state/login_view_model.dart';
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:navigation/navigation.dart';
|
import 'package:navigation/navigation.dart';
|
||||||
import 'package:sf_localizations/sf_localizations.dart';
|
import 'package:sf_localizations/sf_localizations.dart';
|
||||||
|
|
||||||
class LoginScreen extends ConsumerStatefulWidget {
|
class LoginScreen extends ConsumerWidget {
|
||||||
final NavigationContract navigationContract;
|
final NavigationContract navigationContract;
|
||||||
|
|
||||||
const LoginScreen({super.key, required this.navigationContract});
|
const LoginScreen({super.key, required this.navigationContract});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<ConsumerStatefulWidget> createState() => _LoginScreenState();
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
}
|
|
||||||
|
|
||||||
class _LoginScreenState extends ConsumerState<LoginScreen> {
|
|
||||||
bool passwordVisible = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = ref.watch(themePortProvider);
|
final theme = ref.watch(themePortProvider);
|
||||||
|
final vm = ref.read(loginViewModelProvider.notifier);
|
||||||
|
final state = ref.watch(loginViewModelProvider);
|
||||||
|
|
||||||
bool passwordVisible = true;
|
Future<void> onSignIn() async {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
final content = [
|
final login = await vm.login();
|
||||||
Column(
|
if (login) navigationContract.goTo(AppRoutes.dashboardHome);
|
||||||
spacing: 8,
|
}
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.check,
|
|
||||||
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
|
||||||
size: 50,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
// context.translate(I18n.example)
|
|
||||||
"¡Te damos la bienvenida!",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
spacing: 32,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
spacing: 24,
|
|
||||||
children: [
|
|
||||||
CustomTextField(
|
|
||||||
hint: "Nombre de usuario",
|
|
||||||
label: "Nombre de usuario",
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
spacing: 12,
|
|
||||||
children: [
|
|
||||||
CustomTextField(
|
|
||||||
showPassword: passwordVisible,
|
|
||||||
label: "Contraseña",
|
|
||||||
hint: "********",
|
|
||||||
),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.topLeft,
|
|
||||||
child: CustomTextButton(
|
|
||||||
text: "¿Has olvidado la contraseña?",
|
|
||||||
onPressed: () => widget.navigationContract.pushTo(
|
|
||||||
AppRoutes.recoverPassword,
|
|
||||||
),
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
PrimaryButton(
|
|
||||||
onPressed: () =>
|
|
||||||
widget.navigationContract.goTo(AppRoutes.dashboardHome),
|
|
||||||
text: "Iniciar sesión",
|
|
||||||
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.only(top: 24),
|
|
||||||
child: Column(
|
|
||||||
spacing: 24,
|
|
||||||
children: [
|
|
||||||
Stack(
|
|
||||||
children: [
|
|
||||||
Divider(endIndent: 74, indent: 74),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14),
|
|
||||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
|
||||||
child: Text("o continúa con"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
spacing: 20,
|
|
||||||
children: [
|
|
||||||
Spacer(),
|
|
||||||
SecondaryButton(
|
|
||||||
onPressed: () => Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (_) => LoadingGoogleScreen(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
radius: 16,
|
|
||||||
padding: 44,
|
|
||||||
text: "Google",
|
|
||||||
label: "Google",
|
|
||||||
),
|
|
||||||
SecondaryButton(
|
|
||||||
onPressed: () => {},
|
|
||||||
radius: 16,
|
|
||||||
padding: 44,
|
|
||||||
icon: Icons.apple,
|
|
||||||
label: "Apple",
|
|
||||||
),
|
|
||||||
Spacer(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
spacing: 8,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"¿No tienes cuenta?",
|
|
||||||
style: TextStyle(fontSize: 18, letterSpacing: 0),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () =>
|
|
||||||
widget.navigationContract.goTo(AppRoutes.signup),
|
|
||||||
child: Text(
|
|
||||||
"Crear una ahora",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
letterSpacing: 0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
|
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||||
body: Expanded(
|
body: SingleChildScrollView(
|
||||||
child: Center(
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 40),
|
||||||
child: Container(
|
child: Column(
|
||||||
margin: EdgeInsets.all(30),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
child: ListView.separated(
|
children: [
|
||||||
itemBuilder: (BuildContext context, int index) {
|
Icon(
|
||||||
return content[index];
|
Icons.check,
|
||||||
},
|
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||||
separatorBuilder: (BuildContext context, int index) {
|
size: 54,
|
||||||
return Divider(color: Colors.transparent, height: 48);
|
|
||||||
},
|
|
||||||
itemCount: content.length,
|
|
||||||
),
|
),
|
||||||
),
|
Text(
|
||||||
|
context.translate(I18n.welcome),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 48),
|
||||||
|
|
||||||
|
CustomTextField(
|
||||||
|
hint: context.translate(I18n.username),
|
||||||
|
label: context.translate(I18n.username),
|
||||||
|
controller: vm.emailController,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
CustomTextField(
|
||||||
|
showPassword: state.passwordVisible,
|
||||||
|
label: context.translate(I18n.password),
|
||||||
|
hint: "********",
|
||||||
|
controller: vm.passwordController,
|
||||||
|
|
||||||
|
textInputAction: TextInputAction.done,
|
||||||
|
onSubmitted: (_) => onSignIn(),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: CustomTextButton(
|
||||||
|
text: context.translate(I18n.forgotPassword),
|
||||||
|
onPressed: state.isLoading
|
||||||
|
? () {}
|
||||||
|
: () =>
|
||||||
|
navigationContract.pushTo(AppRoutes.recoverPassword),
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
|
||||||
|
PrimaryButton(
|
||||||
|
onPressed: state.isLoading ? () {} : onSignIn,
|
||||||
|
text: context.translate(I18n.signIn),
|
||||||
|
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||||
|
leading: state.isLoading
|
||||||
|
? const SizedBox(
|
||||||
|
height: 18,
|
||||||
|
width: 18,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
const Divider(endIndent: 74, indent: 74),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||||||
|
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||||
|
child: Text(context.translate(I18n.orContinueWith)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
SecondaryButton(
|
||||||
|
onPressed: state.isLoading
|
||||||
|
? () {}
|
||||||
|
: () => Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (_) => const LoadingGoogleScreen(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
radius: 16,
|
||||||
|
padding: 44,
|
||||||
|
text: context.translate(I18n.google),
|
||||||
|
label: 'Google',
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
SecondaryButton(
|
||||||
|
onPressed: state.isLoading ? () {} : () {},
|
||||||
|
radius: 16,
|
||||||
|
padding: 44,
|
||||||
|
icon: Icons.apple,
|
||||||
|
label: 'Apple',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
|
||||||
|
Text(
|
||||||
|
context.translate(I18n.dontHaveAccount),
|
||||||
|
style: const TextStyle(fontSize: 18, letterSpacing: 0),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: state.isLoading
|
||||||
|
? null
|
||||||
|
: () => navigationContract.goTo(AppRoutes.signup),
|
||||||
|
child: Text(
|
||||||
|
context.translate(I18n.createOneNow),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import 'package:auth/src/core/providers/auth_repository_provider.dart';
|
||||||
|
import 'package:auth/src/features/login/domain/login_use_case.dart';
|
||||||
|
import 'package:auth/src/features/login/domain/login_use_case_impl.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
final loginUseCaseProvider = Provider.autoDispose<LoginUseCase>((ref) {
|
||||||
|
final authRepository = ref.read(authRepositoryProvider);
|
||||||
|
return LoginUseCaseImpl(authRepository);
|
||||||
|
});
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import 'package:auth/src/features/login/domain/login_use_case.dart';
|
||||||
|
import 'package:auth/src/features/login/presentation/providers/login_provider.dart';
|
||||||
|
import 'package:auth/src/features/login/presentation/state/login_view_state.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
final loginViewModelProvider =
|
||||||
|
NotifierProvider.autoDispose<LoginViewModel, LoginViewState>(
|
||||||
|
LoginViewModel.new,
|
||||||
|
);
|
||||||
|
|
||||||
|
class LoginViewModel extends Notifier<LoginViewState> {
|
||||||
|
late final LoginUseCase _loginUseCase;
|
||||||
|
late final TextEditingController emailController;
|
||||||
|
late final TextEditingController passwordController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
LoginViewState build() {
|
||||||
|
_loginUseCase = ref.read(loginUseCaseProvider);
|
||||||
|
|
||||||
|
emailController = TextEditingController();
|
||||||
|
passwordController = TextEditingController();
|
||||||
|
|
||||||
|
emailController.addListener(_onEmailChanged);
|
||||||
|
passwordController.addListener(_onPasswordChanged);
|
||||||
|
|
||||||
|
ref.onDispose(disposeControllers);
|
||||||
|
|
||||||
|
return const LoginViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onEmailChanged() {
|
||||||
|
if (emailController.text != state.email) {
|
||||||
|
state = state.copyWith(email: emailController.text, errorMessage: '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onPasswordChanged() {
|
||||||
|
if (passwordController.text != state.password) {
|
||||||
|
state = state.copyWith(
|
||||||
|
password: passwordController.text,
|
||||||
|
errorMessage: '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void togglePasswordVisible() {
|
||||||
|
state = state.copyWith(passwordVisible: !state.passwordVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> login() async {
|
||||||
|
final email = state.email.trim();
|
||||||
|
final password = state.password.trim();
|
||||||
|
|
||||||
|
if (email.isEmpty) {
|
||||||
|
state = state.copyWith(errorMessage: 'errorMessageIsEmpty');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = state.copyWith(isLoading: true, errorMessage: '');
|
||||||
|
try {
|
||||||
|
await _loginUseCase.login(email: email, password: password);
|
||||||
|
if (!ref.mounted) return false;
|
||||||
|
state = state.copyWith(isLoading: false);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
if (!ref.mounted) return false;
|
||||||
|
state = state.copyWith(isLoading: false, errorMessage: e.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void disposeControllers() {
|
||||||
|
emailController.removeListener(_onEmailChanged);
|
||||||
|
passwordController.removeListener(_onPasswordChanged);
|
||||||
|
emailController.dispose();
|
||||||
|
passwordController.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'login_view_state.freezed.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
abstract class LoginViewState with _$LoginViewState {
|
||||||
|
const factory LoginViewState({
|
||||||
|
@Default('') String email,
|
||||||
|
@Default('') String password,
|
||||||
|
@Default(false) bool passwordVisible,
|
||||||
|
@Default('') String errorMessage,
|
||||||
|
@Default(false) bool isLoading,
|
||||||
|
}) = _LoginViewState;
|
||||||
|
}
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// coverage:ignore-file
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'login_view_state.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// dart format off
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$LoginViewState {
|
||||||
|
|
||||||
|
String get email; String get password; bool get passwordVisible; String get errorMessage; bool get isLoading;
|
||||||
|
/// Create a copy of LoginViewState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$LoginViewStateCopyWith<LoginViewState> get copyWith => _$LoginViewStateCopyWithImpl<LoginViewState>(this as LoginViewState, _$identity);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is LoginViewState&&(identical(other.email, email) || other.email == email)&&(identical(other.password, password) || other.password == password)&&(identical(other.passwordVisible, passwordVisible) || other.passwordVisible == passwordVisible)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,email,password,passwordVisible,errorMessage,isLoading);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'LoginViewState(email: $email, password: $password, passwordVisible: $passwordVisible, errorMessage: $errorMessage, isLoading: $isLoading)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $LoginViewStateCopyWith<$Res> {
|
||||||
|
factory $LoginViewStateCopyWith(LoginViewState value, $Res Function(LoginViewState) _then) = _$LoginViewStateCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String email, String password, bool passwordVisible, String errorMessage, bool isLoading
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$LoginViewStateCopyWithImpl<$Res>
|
||||||
|
implements $LoginViewStateCopyWith<$Res> {
|
||||||
|
_$LoginViewStateCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final LoginViewState _self;
|
||||||
|
final $Res Function(LoginViewState) _then;
|
||||||
|
|
||||||
|
/// Create a copy of LoginViewState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? email = null,Object? password = null,Object? passwordVisible = null,Object? errorMessage = null,Object? isLoading = null,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,passwordVisible: null == passwordVisible ? _self.passwordVisible : passwordVisible // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [LoginViewState].
|
||||||
|
extension LoginViewStatePatterns on LoginViewState {
|
||||||
|
/// A variant of `map` that fallback to returning `orElse`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _LoginViewState value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _LoginViewState() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// Callbacks receives the raw object, upcasted.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case final Subclass2 value:
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _LoginViewState value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _LoginViewState():
|
||||||
|
return $default(_that);case _:
|
||||||
|
throw StateError('Unexpected subclass');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `map` that fallback to returning `null`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _LoginViewState value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _LoginViewState() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to an `orElse` callback.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String email, String password, bool passwordVisible, String errorMessage, bool isLoading)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _LoginViewState() when $default != null:
|
||||||
|
return $default(_that.email,_that.password,_that.passwordVisible,_that.errorMessage,_that.isLoading);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// As opposed to `map`, this offers destructuring.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case Subclass2(:final field2):
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String email, String password, bool passwordVisible, String errorMessage, bool isLoading) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _LoginViewState():
|
||||||
|
return $default(_that.email,_that.password,_that.passwordVisible,_that.errorMessage,_that.isLoading);case _:
|
||||||
|
throw StateError('Unexpected subclass');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to returning `null`
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String email, String password, bool passwordVisible, String errorMessage, bool isLoading)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _LoginViewState() when $default != null:
|
||||||
|
return $default(_that.email,_that.password,_that.passwordVisible,_that.errorMessage,_that.isLoading);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
|
||||||
|
class _LoginViewState implements LoginViewState {
|
||||||
|
const _LoginViewState({this.email = '', this.password = '', this.passwordVisible = false, this.errorMessage = '', this.isLoading = false});
|
||||||
|
|
||||||
|
|
||||||
|
@override@JsonKey() final String email;
|
||||||
|
@override@JsonKey() final String password;
|
||||||
|
@override@JsonKey() final bool passwordVisible;
|
||||||
|
@override@JsonKey() final String errorMessage;
|
||||||
|
@override@JsonKey() final bool isLoading;
|
||||||
|
|
||||||
|
/// Create a copy of LoginViewState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$LoginViewStateCopyWith<_LoginViewState> get copyWith => __$LoginViewStateCopyWithImpl<_LoginViewState>(this, _$identity);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _LoginViewState&&(identical(other.email, email) || other.email == email)&&(identical(other.password, password) || other.password == password)&&(identical(other.passwordVisible, passwordVisible) || other.passwordVisible == passwordVisible)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,email,password,passwordVisible,errorMessage,isLoading);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'LoginViewState(email: $email, password: $password, passwordVisible: $passwordVisible, errorMessage: $errorMessage, isLoading: $isLoading)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$LoginViewStateCopyWith<$Res> implements $LoginViewStateCopyWith<$Res> {
|
||||||
|
factory _$LoginViewStateCopyWith(_LoginViewState value, $Res Function(_LoginViewState) _then) = __$LoginViewStateCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String email, String password, bool passwordVisible, String errorMessage, bool isLoading
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$LoginViewStateCopyWithImpl<$Res>
|
||||||
|
implements _$LoginViewStateCopyWith<$Res> {
|
||||||
|
__$LoginViewStateCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _LoginViewState _self;
|
||||||
|
final $Res Function(_LoginViewState) _then;
|
||||||
|
|
||||||
|
/// Create a copy of LoginViewState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? email = null,Object? password = null,Object? passwordVisible = null,Object? errorMessage = null,Object? isLoading = null,}) {
|
||||||
|
return _then(_LoginViewState(
|
||||||
|
email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,passwordVisible: null == passwordVisible ? _self.passwordVisible : passwordVisible // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// dart format on
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
@@ -6,12 +6,11 @@ class SignupAddressScreen extends ConsumerStatefulWidget {
|
|||||||
const SignupAddressScreen({super.key});
|
const SignupAddressScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<SignupAddressScreen> createState() => SignupAddressScreenState();
|
ConsumerState<SignupAddressScreen> createState() =>
|
||||||
|
SignupAddressScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class SignupAddressScreenState extends ConsumerState<SignupAddressScreen>{
|
class SignupAddressScreenState extends ConsumerState<SignupAddressScreen> {
|
||||||
|
|
||||||
late String country;
|
late String country;
|
||||||
late int relation;
|
late int relation;
|
||||||
|
|
||||||
@@ -29,30 +28,39 @@ class SignupAddressScreenState extends ConsumerState<SignupAddressScreen>{
|
|||||||
Column(
|
Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Align(alignment: Alignment.bottomLeft, child: Text(
|
Align(
|
||||||
"Fecha de nacimiento",
|
alignment: Alignment.bottomLeft,
|
||||||
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
child: Text(
|
||||||
)),
|
"Fecha de nacimiento",
|
||||||
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
|
),
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: CustomTextField(
|
Expanded(
|
||||||
|
child: CustomTextField(
|
||||||
//label: "Fecha de nacimiento",
|
//label: "Fecha de nacimiento",
|
||||||
hint: "DD",
|
hint: "DD",
|
||||||
length: 2,
|
length: 2,
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
)),
|
),
|
||||||
Expanded(child: CustomTextField(
|
),
|
||||||
|
Expanded(
|
||||||
|
child: CustomTextField(
|
||||||
hint: "MM",
|
hint: "MM",
|
||||||
length: 2,
|
length: 2,
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
)),
|
),
|
||||||
Expanded(child: CustomTextField(
|
),
|
||||||
|
Expanded(
|
||||||
|
child: CustomTextField(
|
||||||
hint: "AAAA",
|
hint: "AAAA",
|
||||||
length: 4,
|
length: 4,
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
)),
|
),
|
||||||
]
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -69,13 +77,16 @@ class SignupAddressScreenState extends ConsumerState<SignupAddressScreen>{
|
|||||||
CustomDropdown(
|
CustomDropdown(
|
||||||
items: [Text("Padre"), Text("Madre"), Text("Tutor")],
|
items: [Text("Padre"), Text("Madre"), Text("Tutor")],
|
||||||
hint: "¿Qué familiar eres?",
|
hint: "¿Qué familiar eres?",
|
||||||
onChanged: (value)=>setState(() {
|
onChanged: (value) => setState(() {
|
||||||
relation = value;
|
relation = value;
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
CustomTextField(label: "Dirección completa", hint: "Calle Gran Vía 30 6º, 28013"),
|
CustomTextField(
|
||||||
|
label: "Dirección completa",
|
||||||
|
hint: "Calle Gran Vía 30 6º, 28013",
|
||||||
|
),
|
||||||
CustomTextField(label: "Ciudad", hint: "Ciudad"),
|
CustomTextField(label: "Ciudad", hint: "Ciudad"),
|
||||||
Column(
|
Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
@@ -90,9 +101,9 @@ class SignupAddressScreenState extends ConsumerState<SignupAddressScreen>{
|
|||||||
CustomDropdown(
|
CustomDropdown(
|
||||||
items: [Text("España"), Text("Francia"), Text("Portugal")],
|
items: [Text("España"), Text("Francia"), Text("Portugal")],
|
||||||
hint: "País",
|
hint: "País",
|
||||||
onChanged: (value)=>setState(() {
|
onChanged: (value) => setState(() {
|
||||||
country = value;
|
country = value;
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class SignupPersonalScreen extends StatelessWidget{
|
class SignupPersonalScreen extends StatelessWidget {
|
||||||
const SignupPersonalScreen({super.key});
|
const SignupPersonalScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -21,16 +21,20 @@ class SignupPersonalScreen extends StatelessWidget{
|
|||||||
onChanged: (value)=> {},
|
onChanged: (value)=> {},
|
||||||
width: 80,
|
width: 80,
|
||||||
),*/
|
),*/
|
||||||
Expanded(child: CustomTextField(
|
Expanded(
|
||||||
label: "Teléfono móvil",
|
child: CustomTextField(
|
||||||
hint: "123456789",
|
label: "Teléfono móvil",
|
||||||
numeric: true
|
hint: "123456789",
|
||||||
))
|
keyboardType: TextInputType.number,
|
||||||
]
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
CustomTextField(
|
||||||
|
label: "Correo electrónico",
|
||||||
|
hint: "Correo electrónico",
|
||||||
),
|
),
|
||||||
CustomTextField(label: "Correo electrónico", hint: "Correo electrónico"),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ class _DepositScreenState extends ConsumerState<DepositScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
onPressed: ()=>{},
|
onPressed: () => {},
|
||||||
text: "Añadir dinero",
|
text: "Añadir dinero",
|
||||||
color: theme.getColorFor(ThemeCode.buttonPrimary)
|
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
@@ -58,7 +58,7 @@ class _DepositScreenState extends ConsumerState<DepositScreen> {
|
|||||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
||||||
),
|
),
|
||||||
CustomTextField(
|
CustomTextField(
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
label: "Cantidad",
|
label: "Cantidad",
|
||||||
hint: "0€",
|
hint: "0€",
|
||||||
),
|
),
|
||||||
@@ -135,7 +135,8 @@ class _DepositScreenState extends ConsumerState<DepositScreen> {
|
|||||||
CustomTextField(
|
CustomTextField(
|
||||||
lines: 3,
|
lines: 3,
|
||||||
length: 150,
|
length: 150,
|
||||||
label: "Escribir mensaje a ${widget.kid.name} del motivo del ingreso",
|
label:
|
||||||
|
"Escribir mensaje a ${widget.kid.name} del motivo del ingreso",
|
||||||
hint: "Escribe tu mensaje",
|
hint: "Escribe tu mensaje",
|
||||||
),
|
),
|
||||||
const Align(
|
const Align(
|
||||||
@@ -191,4 +192,4 @@ class _DepositScreenState extends ConsumerState<DepositScreen> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:home/src/presentation/wallet_management_layout.dart';
|
import 'package:home/src/presentation/wallet_management_layout.dart';
|
||||||
import 'package:sf_shared/sf_shared.dart';
|
import 'package:sf_shared/sf_shared.dart';
|
||||||
|
|
||||||
class ExtractScreen extends ConsumerWidget{
|
class ExtractScreen extends ConsumerWidget {
|
||||||
final Kid kid;
|
final Kid kid;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ExtractScreen({
|
ExtractScreen({super.key, required this.kid});
|
||||||
super.key,
|
|
||||||
required this.kid,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
@@ -20,92 +17,125 @@ class ExtractScreen extends ConsumerWidget{
|
|||||||
|
|
||||||
return WalletManagementLayout(
|
return WalletManagementLayout(
|
||||||
kid: kid,
|
kid: kid,
|
||||||
children: [Container(
|
children: [
|
||||||
padding: EdgeInsets.all(24),
|
Container(
|
||||||
decoration: BoxDecoration(
|
padding: EdgeInsets.all(24),
|
||||||
borderRadius: BorderRadius.all(Radius.circular(24)),
|
decoration: BoxDecoration(
|
||||||
color: theme.getColorFor(ThemeCode.backgroundPrimary)
|
borderRadius: BorderRadius.all(Radius.circular(24)),
|
||||||
|
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
spacing: 24,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
|
"Retirar dinero de la cuenta",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
|
"Este dato aparecerá en el reloj del peque",
|
||||||
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
CustomTextField(
|
||||||
|
label: "Selecciona la cantidad de dinero",
|
||||||
|
hint: "2€",
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
|
"Este es el mensaje fijado por defecto:",
|
||||||
|
style: TextStyle(fontSize: 16, letterSpacing: 0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
|
"\"Hemos quitado el dinero del reloj, ya no puedes pagar con él\"",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
|
"Escribir mensaje a ${kid.name} del motivo de la retirada de su dinero",
|
||||||
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CustomTextField(
|
||||||
|
hint: "Escribe tu mensaje",
|
||||||
|
lines: 4,
|
||||||
|
length: 150,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
spacing: 4,
|
||||||
|
children: [
|
||||||
|
Icon(Icons.info_outline, size: 16),
|
||||||
|
Text(
|
||||||
|
"Máximo 150 caracteres",
|
||||||
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
],
|
||||||
spacing: 24,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
spacing: 8,
|
|
||||||
children: [
|
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
|
||||||
"Retirar dinero de la cuenta",
|
|
||||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0)
|
|
||||||
)),
|
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
|
||||||
"Este dato aparecerá en el reloj del peque",
|
|
||||||
style: TextStyle(fontSize: 14, letterSpacing: 0)
|
|
||||||
))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
CustomTextField(
|
|
||||||
label: "Selecciona la cantidad de dinero",
|
|
||||||
hint: "2€",
|
|
||||||
numeric: true,
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
spacing: 8,
|
|
||||||
children: [
|
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
|
||||||
"Este es el mensaje fijado por defecto:",
|
|
||||||
style: TextStyle(fontSize: 16, letterSpacing: 0)
|
|
||||||
)),
|
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
|
||||||
"\"Hemos quitado el dinero del reloj, ya no puedes pagar con él\"",
|
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: 0)
|
|
||||||
))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
spacing: 8,
|
|
||||||
children: [
|
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
|
||||||
"Escribir mensaje a ${kid.name} del motivo de la retirada de su dinero",
|
|
||||||
style: TextStyle(fontSize: 14, letterSpacing: 0)
|
|
||||||
)),
|
|
||||||
CustomTextField(
|
|
||||||
hint: "Escribe tu mensaje",
|
|
||||||
lines: 4,
|
|
||||||
length: 150,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
spacing: 4,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.info_outline, size: 16),
|
|
||||||
Text("Máximo 150 caracteres", style: TextStyle(fontSize: 14, letterSpacing: 0))
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)],
|
|
||||||
footer: Container(
|
footer: Container(
|
||||||
padding: EdgeInsets.all(24),
|
padding: EdgeInsets.all(24),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||||
borderRadius: BorderRadius.only(topRight: Radius.circular(24), topLeft: Radius.circular(24))
|
borderRadius: BorderRadius.only(
|
||||||
|
topRight: Radius.circular(24),
|
||||||
|
topLeft: Radius.circular(24),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
spacing: 16,
|
spacing: 16,
|
||||||
children: [
|
children: [
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
onPressed: ()=>{Navigator.pop(context)},
|
onPressed: () => {Navigator.pop(context)},
|
||||||
text: "Enviar mensaje y bloquear",
|
text: "Enviar mensaje y bloquear",
|
||||||
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
style: ButtonStyle(padding: WidgetStatePropertyAll(EdgeInsets.all(0))),
|
style: ButtonStyle(
|
||||||
onPressed: ()=>Navigator.pop(context),
|
padding: WidgetStatePropertyAll(EdgeInsets.all(0)),
|
||||||
child: Text("Cancelar", style: TextStyle(fontSize: 18))
|
),
|
||||||
)
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: Text("Cancelar", style: TextStyle(fontSize: 18)),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,8 @@ class LimitsScreenState extends ConsumerState<LimitsScreen> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
dailyLimits = [ //dey, week, month, year
|
dailyLimits = [
|
||||||
|
//dey, week, month, year
|
||||||
{"title": "Diario L-V", "limit": "5", "edit": false},
|
{"title": "Diario L-V", "limit": "5", "edit": false},
|
||||||
{"title": "Fines de semana", "limit": "8", "edit": false},
|
{"title": "Fines de semana", "limit": "8", "edit": false},
|
||||||
{"title": "Semanal", "limit": "30", "edit": false},
|
{"title": "Semanal", "limit": "30", "edit": false},
|
||||||
@@ -41,12 +42,7 @@ class LimitsScreenState extends ConsumerState<LimitsScreen> {
|
|||||||
"end": "21:00",
|
"end": "21:00",
|
||||||
"edit": false,
|
"edit": false,
|
||||||
},
|
},
|
||||||
{
|
{"title": "Vacaciones", "start": "09:00", "end": "22:00", "edit": false},
|
||||||
"title": "Vacaciones",
|
|
||||||
"start": "09:00",
|
|
||||||
"end": "22:00",
|
|
||||||
"edit": false
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
conditions = [
|
conditions = [
|
||||||
{"title": "Alimentación", "limit": "10", "active": true, "edit": false},
|
{"title": "Alimentación", "limit": "10", "active": true, "edit": false},
|
||||||
@@ -73,7 +69,10 @@ class LimitsScreenState extends ConsumerState<LimitsScreen> {
|
|||||||
footer: Container(
|
footer: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||||
borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24))
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(24),
|
||||||
|
topRight: Radius.circular(24),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
padding: EdgeInsets.all(24),
|
padding: EdgeInsets.all(24),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -81,19 +80,19 @@ class LimitsScreenState extends ConsumerState<LimitsScreen> {
|
|||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
onPressed: () => {},
|
onPressed: () => {},
|
||||||
text: "Guardar límites",
|
text: "Guardar límites",
|
||||||
color: theme.getColorFor(ThemeCode.buttonPrimary)
|
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: ()=>Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
child: Text(
|
child: Text(
|
||||||
"Cancelar",
|
"Cancelar",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: theme.getColorFor(ThemeCode.textPrimary)
|
color: theme.getColorFor(ThemeCode.textPrimary),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -112,7 +111,7 @@ class LimitsScreenState extends ConsumerState<LimitsScreen> {
|
|||||||
child: Text(
|
child: Text(
|
||||||
"Pon límite de gastos",
|
"Pon límite de gastos",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
Text("Libertad para ellos, tranquilidad para ti"),
|
Text("Libertad para ellos, tranquilidad para ti"),
|
||||||
...List<Widget>.generate(dailyLimits.length, (int index) {
|
...List<Widget>.generate(dailyLimits.length, (int index) {
|
||||||
@@ -135,9 +134,7 @@ class LimitsScreenState extends ConsumerState<LimitsScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (dailyLimits[index]["edit"]) CustomTextField(
|
if (dailyLimits[index]["edit"]) CustomTextField(hint: "5€"),
|
||||||
hint: "5€",
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@@ -170,16 +167,15 @@ class LimitsScreenState extends ConsumerState<LimitsScreen> {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => {
|
onPressed: () => {
|
||||||
setState(() {
|
setState(() {
|
||||||
timeLimits[index]["edit"] = !timeLimits[index]["edit"];
|
timeLimits[index]["edit"] =
|
||||||
|
!timeLimits[index]["edit"];
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
child: Text("Editar"),
|
child: Text("Editar"),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (timeLimits[index]["edit"]) CustomTextField(
|
if (timeLimits[index]["edit"]) CustomTextField(hint: "5€"),
|
||||||
hint: "5€",
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@@ -197,67 +193,89 @@ class LimitsScreenState extends ConsumerState<LimitsScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Condiciones",
|
"Condiciones",
|
||||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0),
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: List<Widget>.generate(conditions.length, (int index)=>
|
children: List<Widget>.generate(
|
||||||
Column(
|
conditions.length,
|
||||||
|
(int index) => Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Row(children: [
|
Row(
|
||||||
Expanded(child: CheckboxListTile(
|
children: [
|
||||||
value: conditions[index]["active"],
|
Expanded(
|
||||||
onChanged: (_)=>setState(() {
|
child: CheckboxListTile(
|
||||||
conditions[index]["active"] = !conditions[index]["active"];
|
value: conditions[index]["active"],
|
||||||
}),
|
onChanged: (_) => setState(() {
|
||||||
title: Text(
|
conditions[index]["active"] =
|
||||||
"${conditions[index]["title"]}: ${conditions[index]["limit"]}€/sem",
|
!conditions[index]["active"];
|
||||||
style: TextStyle(fontSize: 16, letterSpacing: 0),
|
}),
|
||||||
|
title: Text(
|
||||||
|
"${conditions[index]["title"]}: ${conditions[index]["limit"]}€/sem",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
checkboxScaleFactor: 2,
|
||||||
|
controlAffinity: ListTileControlAffinity.leading,
|
||||||
|
activeColor: theme.getColorFor(
|
||||||
|
ThemeCode.buttonPrimary,
|
||||||
|
),
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
checkboxScaleFactor: 2,
|
TextButton(
|
||||||
controlAffinity: ListTileControlAffinity.leading,
|
onPressed: () => setState(() {
|
||||||
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
|
conditions[index]["edit"] =
|
||||||
contentPadding: EdgeInsets.zero,
|
!conditions[index]["edit"];
|
||||||
)),
|
}),
|
||||||
TextButton(
|
child: Text(
|
||||||
onPressed: ()=>setState(() {
|
"Editar",
|
||||||
conditions[index]["edit"] = ! conditions[index]["edit"];
|
style: TextStyle(fontSize: 16, letterSpacing: 0),
|
||||||
}),
|
),
|
||||||
child: Text(
|
),
|
||||||
"Editar",
|
],
|
||||||
style: TextStyle(fontSize: 16, letterSpacing: 0),
|
),
|
||||||
)
|
if (conditions[index]["edit"])
|
||||||
)
|
CustomTextField(
|
||||||
]),
|
hint: "5€",
|
||||||
if (conditions[index]["edit"]) CustomTextField(
|
keyboardType: TextInputType.number,
|
||||||
hint: "5€",
|
),
|
||||||
numeric: true,
|
],
|
||||||
)
|
),
|
||||||
]
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.all(24),
|
padding: EdgeInsets.all(24),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||||
borderRadius: BorderRadius.all(Radius.circular(24))
|
borderRadius: BorderRadius.all(Radius.circular(24)),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
spacing: 24,
|
spacing: 24,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Comercios bloqueados",
|
"Comercios bloqueados",
|
||||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0),
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: List<Widget>.generate(blocks.length, (int index) =>
|
children: List<Widget>.generate(
|
||||||
CheckboxListTile(
|
blocks.length,
|
||||||
|
(int index) => CheckboxListTile(
|
||||||
value: blocks[index]["active"],
|
value: blocks[index]["active"],
|
||||||
onChanged: (_) => setState(() {
|
onChanged: (_) => setState(() {
|
||||||
blocks[index]["active"] = !blocks[index]["active"];
|
blocks[index]["active"] = !blocks[index]["active"];
|
||||||
@@ -270,13 +288,13 @@ class LimitsScreenState extends ConsumerState<LimitsScreen> {
|
|||||||
controlAffinity: ListTileControlAffinity.leading,
|
controlAffinity: ListTileControlAffinity.leading,
|
||||||
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
|
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class _WageScreenState extends ConsumerState<WageScreen> {
|
|||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
onPressed: () => {},
|
onPressed: () => {},
|
||||||
text: "Activar paga automática",
|
text: "Activar paga automática",
|
||||||
color: theme.getColorFor(ThemeCode.buttonPrimary)
|
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||||
),
|
),
|
||||||
TextButton(onPressed: () {}, child: const Text('Cancelar')),
|
TextButton(onPressed: () {}, child: const Text('Cancelar')),
|
||||||
],
|
],
|
||||||
@@ -59,14 +59,15 @@ class _WageScreenState extends ConsumerState<WageScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
spacing: 10,
|
spacing: 10,
|
||||||
children: [
|
children: [
|
||||||
Align(alignment: Alignment.topLeft,
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"Paga automática",
|
"Paga automática",
|
||||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
|
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
CustomTextField(
|
CustomTextField(
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
label: "Cantidad",
|
label: "Cantidad",
|
||||||
hint: "0€",
|
hint: "0€",
|
||||||
),
|
),
|
||||||
@@ -84,13 +85,15 @@ class _WageScreenState extends ConsumerState<WageScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
spacing: 10,
|
spacing: 10,
|
||||||
children: [
|
children: [
|
||||||
Align(alignment: Alignment.topLeft,
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
child: const Text(
|
child: const Text(
|
||||||
"Frecuencia",
|
"Frecuencia",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
Align(alignment: Alignment.topLeft,
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
child: const Text("Cuándo se envía el dinero"),
|
child: const Text("Cuándo se envía el dinero"),
|
||||||
),
|
),
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
@@ -139,22 +142,30 @@ class _WageScreenState extends ConsumerState<WageScreen> {
|
|||||||
Text("Sábado"),
|
Text("Sábado"),
|
||||||
Text("Domingo"),
|
Text("Domingo"),
|
||||||
],
|
],
|
||||||
values: ["Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"],
|
values: [
|
||||||
onChanged: (value)=> {},
|
"Lunes",
|
||||||
|
"Martes",
|
||||||
|
"Miércoles",
|
||||||
|
"Jueves",
|
||||||
|
"Viernes",
|
||||||
|
"Sábado",
|
||||||
|
"Domingo",
|
||||||
|
],
|
||||||
|
onChanged: (value) => {},
|
||||||
hint: "Día de la semana",
|
hint: "Día de la semana",
|
||||||
),
|
),
|
||||||
CustomDropdown(
|
CustomDropdown(
|
||||||
hint: "Hora del día",
|
hint: "Hora del día",
|
||||||
items: List<Widget>.generate(24,(int index){
|
items: List<Widget>.generate(24, (int index) {
|
||||||
return Text("$index:00");
|
return Text("$index:00");
|
||||||
}),
|
}),
|
||||||
onChanged: (value)=> {},
|
onChanged: (value) => {},
|
||||||
),
|
),
|
||||||
CustomTextField(
|
CustomTextField(
|
||||||
lines: 3,
|
lines: 3,
|
||||||
length: 150,
|
length: 150,
|
||||||
label:
|
label:
|
||||||
"Escribir mensaje a ${widget.kid.name} del motivo del ingreso",
|
"Escribir mensaje a ${widget.kid.name} del motivo del ingreso",
|
||||||
hint: "Escribe tu mensaje",
|
hint: "Escribe tu mensaje",
|
||||||
),
|
),
|
||||||
const Align(
|
const Align(
|
||||||
@@ -174,13 +185,15 @@ class _WageScreenState extends ConsumerState<WageScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
spacing: 10,
|
spacing: 10,
|
||||||
children: [
|
children: [
|
||||||
const Align(alignment: Alignment.topLeft,
|
const Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
"Condiciones",
|
"Condiciones",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Align(alignment: Alignment.topLeft,
|
const Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
child: Text("Este dato aparecerá en el reloj del peque"),
|
child: Text("Este dato aparecerá en el reloj del peque"),
|
||||||
),
|
),
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class PrimaryButton extends StatelessWidget {
|
class PrimaryButton extends StatelessWidget {
|
||||||
final VoidCallback onPressed;
|
final VoidCallback? onPressed;
|
||||||
final String text;
|
final String text;
|
||||||
final Color color;
|
final Color color;
|
||||||
final double height;
|
final double height;
|
||||||
@@ -9,24 +9,35 @@ class PrimaryButton extends StatelessWidget {
|
|||||||
final double size;
|
final double size;
|
||||||
final double radius;
|
final double radius;
|
||||||
final double padding;
|
final double padding;
|
||||||
|
final Widget? leading;
|
||||||
|
final double leadingGap;
|
||||||
|
|
||||||
const PrimaryButton({
|
const PrimaryButton({
|
||||||
super.key,
|
super.key,
|
||||||
required this.onPressed,
|
|
||||||
required this.text,
|
required this.text,
|
||||||
required this.color,
|
required this.color,
|
||||||
|
this.onPressed,
|
||||||
this.height = 60,
|
this.height = 60,
|
||||||
this.width,
|
this.width,
|
||||||
this.size = 18,
|
this.size = 18,
|
||||||
this.radius = 18,
|
this.radius = 18,
|
||||||
this.padding = 0,
|
this.padding = 0,
|
||||||
|
this.leading,
|
||||||
|
this.leadingGap = 10,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final bgResolver = WidgetStateProperty.resolveWith<Color>((states) {
|
||||||
|
if (states.contains(WidgetState.disabled)) {
|
||||||
|
return color.withValues(alpha: 0.5);
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
});
|
||||||
|
|
||||||
return FilledButton(
|
return FilledButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor: WidgetStatePropertyAll<Color>(color),
|
backgroundColor: bgResolver,
|
||||||
padding: WidgetStatePropertyAll<EdgeInsetsGeometry>(
|
padding: WidgetStatePropertyAll<EdgeInsetsGeometry>(
|
||||||
EdgeInsets.symmetric(horizontal: padding),
|
EdgeInsets.symmetric(horizontal: padding),
|
||||||
),
|
),
|
||||||
@@ -40,17 +51,29 @@ class PrimaryButton extends StatelessWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
child: Center(
|
child: Row(
|
||||||
child: Text(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
text,
|
// mainAxisSize: MainAxisSize.min,
|
||||||
textAlign: TextAlign.center,
|
children: [
|
||||||
style: TextStyle(
|
if (leading != null) ...[
|
||||||
fontSize: size,
|
IconTheme.merge(
|
||||||
fontWeight: FontWeight.w500,
|
data: const IconThemeData(color: Colors.white, size: 20),
|
||||||
letterSpacing: 0,
|
child: leading!,
|
||||||
color: Colors.white, // theme.getColorFor(ThemeCode.textSecondary)
|
),
|
||||||
|
SizedBox(width: leadingGap),
|
||||||
|
],
|
||||||
|
Text(
|
||||||
|
text,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: size,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
class CustomTextField extends StatelessWidget {
|
class CustomTextField extends StatelessWidget {
|
||||||
final bool? showPassword;
|
final bool? showPassword;
|
||||||
|
final TextInputType keyboardType;
|
||||||
|
final TextInputAction? textInputAction;
|
||||||
|
final ValueChanged<String>? onSubmitted;
|
||||||
final VoidCallback? onVisibilityChanged;
|
final VoidCallback? onVisibilityChanged;
|
||||||
final bool numeric;
|
|
||||||
final String hint;
|
final String hint;
|
||||||
final String label;
|
final String label;
|
||||||
final double labelSize;
|
final double labelSize;
|
||||||
@@ -18,8 +19,10 @@ class CustomTextField extends StatelessWidget {
|
|||||||
const CustomTextField({
|
const CustomTextField({
|
||||||
super.key,
|
super.key,
|
||||||
this.showPassword,
|
this.showPassword,
|
||||||
|
this.keyboardType = TextInputType.text,
|
||||||
|
this.textInputAction,
|
||||||
|
this.onSubmitted,
|
||||||
this.onVisibilityChanged,
|
this.onVisibilityChanged,
|
||||||
this.numeric = false,
|
|
||||||
this.hint = '',
|
this.hint = '',
|
||||||
this.label = '',
|
this.label = '',
|
||||||
this.labelSize = 14,
|
this.labelSize = 14,
|
||||||
@@ -45,17 +48,14 @@ class CustomTextField extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: controller,
|
onFieldSubmitted: widget.onSubmitted,
|
||||||
keyboardType: numeric
|
textInputAction: widget.textInputAction,
|
||||||
? TextInputType.number
|
controller: widget.controller,
|
||||||
: TextInputType.text,
|
keyboardType: widget.keyboardType,
|
||||||
obscureText: !(showPassword ?? true),
|
obscureText: !(showPassword ?? true),
|
||||||
enableSuggestions: (showPassword ?? true),
|
enableSuggestions: (showPassword ?? true),
|
||||||
autocorrect: !(showPassword ?? true),
|
autocorrect: !(showPassword ?? true),
|
||||||
style: const TextStyle(color: Color(0xFF4B4B4B)),
|
style: const TextStyle(color: Color(0xFF4B4B4B)),
|
||||||
inputFormatters: numeric
|
|
||||||
? <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly]
|
|
||||||
: const <TextInputFormatter>[],
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
counterText: "",
|
counterText: "",
|
||||||
hintText: hint,
|
hintText: hint,
|
||||||
|
|||||||
@@ -93,7 +93,11 @@ void main() {
|
|||||||
)
|
)
|
||||||
..addScenario(
|
..addScenario(
|
||||||
'numeric',
|
'numeric',
|
||||||
SizedBox(height: 70, width: 250, child: CustomTextField(numeric: true)),
|
SizedBox(
|
||||||
|
height: 70,
|
||||||
|
width: 250,
|
||||||
|
child: CustomTextField(keyboardType: TextInputType.number),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
..addScenario(
|
..addScenario(
|
||||||
'password',
|
'password',
|
||||||
|
|||||||
@@ -21,6 +21,17 @@
|
|||||||
"enter": "Weiter",
|
"enter": "Weiter",
|
||||||
"didNotReceiveIt": "Hast du es nicht erhalten?",
|
"didNotReceiveIt": "Hast du es nicht erhalten?",
|
||||||
"tryAgain": "Erneut versuchen",
|
"tryAgain": "Erneut versuchen",
|
||||||
|
"welcome": "Wir heißen dich willkommen!",
|
||||||
|
"username": "Benutzername",
|
||||||
|
"password": "Passwort",
|
||||||
|
"forgotPassword": "Passwort vergessen?",
|
||||||
|
"signIn": "Anmelden",
|
||||||
|
"orContinueWith": "oder weiter mit",
|
||||||
|
"google": "Google",
|
||||||
|
"apple": "Apple",
|
||||||
|
"dontHaveAccount": "Du hast noch kein Konto?",
|
||||||
|
"createOneNow": "Jetzt eines erstellen",
|
||||||
|
"tryAgain": "Erneut versuchen",
|
||||||
"recoverPasswordTitle": "Passwort wiederherstellen",
|
"recoverPasswordTitle": "Passwort wiederherstellen",
|
||||||
"recoverPasswordSubtitle": "Geben Sie Ihre E-Mail-Adresse ein, um Ihnen einen Wiederherstellungslink zu senden",
|
"recoverPasswordSubtitle": "Geben Sie Ihre E-Mail-Adresse ein, um Ihnen einen Wiederherstellungslink zu senden",
|
||||||
"send": "Schicken",
|
"send": "Schicken",
|
||||||
|
|||||||
@@ -21,6 +21,17 @@
|
|||||||
"enter": "Enter",
|
"enter": "Enter",
|
||||||
"didNotReceiveIt": "Didn't receive it?",
|
"didNotReceiveIt": "Didn't receive it?",
|
||||||
"tryAgain": "Try again",
|
"tryAgain": "Try again",
|
||||||
|
"welcome": "Welcome!",
|
||||||
|
"username": "Username",
|
||||||
|
"password": "Password",
|
||||||
|
"forgotPassword": "Forgot your password?",
|
||||||
|
"signIn": "Sign in",
|
||||||
|
"orContinueWith": "or continue with",
|
||||||
|
"google": "Google",
|
||||||
|
"apple": "Apple",
|
||||||
|
"dontHaveAccount": "Don't have an account?",
|
||||||
|
"createOneNow": "Create one now",
|
||||||
|
"tryAgain": "Try again",
|
||||||
"recoverPasswordTitle": "Recover password",
|
"recoverPasswordTitle": "Recover password",
|
||||||
"recoverPasswordSubtitle": "Insert your email to send you a recovery link",
|
"recoverPasswordSubtitle": "Insert your email to send you a recovery link",
|
||||||
"send": "Send",
|
"send": "Send",
|
||||||
|
|||||||
@@ -21,6 +21,17 @@
|
|||||||
"enter": "enter",
|
"enter": "enter",
|
||||||
"didNotReceiveIt": "¿No lo has recibido?",
|
"didNotReceiveIt": "¿No lo has recibido?",
|
||||||
"tryAgain": "Volver a intentarlo",
|
"tryAgain": "Volver a intentarlo",
|
||||||
|
"welcome": "¡Te damos la bienvenida!",
|
||||||
|
"username": "Nombre de usuario",
|
||||||
|
"password": "Contraseña",
|
||||||
|
"forgotPassword": "¿Has olvidado la contraseña?",
|
||||||
|
"signIn": "Iniciar sesión",
|
||||||
|
"orContinueWith": "o continúa con",
|
||||||
|
"google": "Google",
|
||||||
|
"apple": "Apple",
|
||||||
|
"dontHaveAccount": "¿No tienes cuenta?",
|
||||||
|
"createOneNow": "Crear una ahora",
|
||||||
|
"tryAgain": "Volver a intentarlo",
|
||||||
"recoverPasswordTitle": "Recuperar contraseña",
|
"recoverPasswordTitle": "Recuperar contraseña",
|
||||||
"recoverPasswordSubtitle": "Introduce tu email para enviarte un enlace de recuperación",
|
"recoverPasswordSubtitle": "Introduce tu email para enviarte un enlace de recuperación",
|
||||||
"send": "Enviar",
|
"send": "Enviar",
|
||||||
|
|||||||
@@ -21,6 +21,17 @@
|
|||||||
"enter": "Entrer",
|
"enter": "Entrer",
|
||||||
"didNotReceiveIt": "Tu ne l'as pas reçu ?",
|
"didNotReceiveIt": "Tu ne l'as pas reçu ?",
|
||||||
"tryAgain": "Réessayer",
|
"tryAgain": "Réessayer",
|
||||||
|
"welcome": "Nous te souhaitons la bienvenue !",
|
||||||
|
"username": "Nom d'utilisateur",
|
||||||
|
"password": "Mot de passe",
|
||||||
|
"forgotPassword": "Mot de passe oublié ?",
|
||||||
|
"signIn": "Se connecter",
|
||||||
|
"orContinueWith": "ou continuer avec",
|
||||||
|
"google": "Google",
|
||||||
|
"apple": "Apple",
|
||||||
|
"dontHaveAccount": "Tu n'as pas de compte ?",
|
||||||
|
"createOneNow": "Crée-en un maintenant",
|
||||||
|
"tryAgain": "Réessayer",
|
||||||
"recoverPasswordTitle": "Récupérer le mot de passe",
|
"recoverPasswordTitle": "Récupérer le mot de passe",
|
||||||
"recoverPasswordSubtitle": "Entrez votre email pour vous envoyer un lien de récupération",
|
"recoverPasswordSubtitle": "Entrez votre email pour vous envoyer un lien de récupération",
|
||||||
"send": "Envoyer",
|
"send": "Envoyer",
|
||||||
|
|||||||
@@ -21,6 +21,17 @@
|
|||||||
"enter": "Entra",
|
"enter": "Entra",
|
||||||
"didNotReceiveIt": "Non lo hai ricevuto?",
|
"didNotReceiveIt": "Non lo hai ricevuto?",
|
||||||
"tryAgain": "Riprova",
|
"tryAgain": "Riprova",
|
||||||
|
"welcome": "Ti diamo il benvenuto!",
|
||||||
|
"username": "Nome utente",
|
||||||
|
"password": "Password",
|
||||||
|
"forgotPassword": "Hai dimenticato la password?",
|
||||||
|
"signIn": "Accedi",
|
||||||
|
"orContinueWith": "o continua con",
|
||||||
|
"google": "Google",
|
||||||
|
"apple": "Apple",
|
||||||
|
"dontHaveAccount": "Non hai un account?",
|
||||||
|
"createOneNow": "Creane uno adesso",
|
||||||
|
"tryAgain": "Riprova",
|
||||||
"recoverPasswordTitle": "Recupera la password",
|
"recoverPasswordTitle": "Recupera la password",
|
||||||
"recoverPasswordSubtitle": "Inserisci la tua email per inviarti un collegamento di recupero",
|
"recoverPasswordSubtitle": "Inserisci la tua email per inviarti un collegamento di recupero",
|
||||||
"send": "Inviare",
|
"send": "Inviare",
|
||||||
|
|||||||
@@ -21,6 +21,17 @@
|
|||||||
"enter": "Entrar",
|
"enter": "Entrar",
|
||||||
"didNotReceiveIt": "Você não recebeu?",
|
"didNotReceiveIt": "Você não recebeu?",
|
||||||
"tryAgain": "Tentar novamente",
|
"tryAgain": "Tentar novamente",
|
||||||
|
"welcome": "Bem-vindo!",
|
||||||
|
"username": "Nome de usuário",
|
||||||
|
"password": "Senha",
|
||||||
|
"forgotPassword": "Esqueceu a senha?",
|
||||||
|
"signIn": "Iniciar sessão",
|
||||||
|
"orContinueWith": "ou continuar com",
|
||||||
|
"google": "Google",
|
||||||
|
"apple": "Apple",
|
||||||
|
"dontHaveAccount": "Não tem conta?",
|
||||||
|
"createOneNow": "Criar uma agora",
|
||||||
|
"tryAgain": "Tentar novamente",
|
||||||
"recoverPasswordTitle": "Recuperar senha",
|
"recoverPasswordTitle": "Recuperar senha",
|
||||||
"recoverPasswordSubtitle": "Insira seu e-mail para enviar um link de recuperação",
|
"recoverPasswordSubtitle": "Insira seu e-mail para enviar um link de recuperação",
|
||||||
"send": "Enviar",
|
"send": "Enviar",
|
||||||
|
|||||||
@@ -3,6 +3,38 @@
|
|||||||
class I18n {
|
class I18n {
|
||||||
const I18n._();
|
const I18n._();
|
||||||
|
|
||||||
|
static const String example = 'example';
|
||||||
|
static const String start = 'start';
|
||||||
|
static const String next = 'next';
|
||||||
|
static const String skip = 'skip';
|
||||||
|
static const String onboardingTitle1 = 'onboardingTitle1';
|
||||||
|
static const String onboardingSubtitle1 = 'onboardingSubtitle1';
|
||||||
|
static const String onboardingTitle2 = 'onboardingTitle2';
|
||||||
|
static const String onboardingSubtitle2 = 'onboardingSubtitle2';
|
||||||
|
static const String onboardingTitle3 = 'onboardingTitle3';
|
||||||
|
static const String onboardingSubtitle3 = 'onboardingSubtitle3';
|
||||||
|
static const String linkPhoneTitle = 'linkPhoneTitle';
|
||||||
|
static const String linkPhoneSubtitle = 'linkPhoneSubtitle';
|
||||||
|
static const String mobilePhone = 'mobilePhone';
|
||||||
|
static const String phoneNumber = 'phoneNumber';
|
||||||
|
static const String selectYourCountry = 'selectYourCountry';
|
||||||
|
static const String errorMessagePhoneIsEmpty = 'errorMessagePhoneIsEmpty';
|
||||||
|
static const String connect = "connect";
|
||||||
|
static const String verificationCodeSentTo = "verificationCodeSentTo";
|
||||||
|
static const String enterCodeHere = "enterCodeHere";
|
||||||
|
static const String enter = "enter";
|
||||||
|
static const String didNotReceiveIt = "didNotReceiveIt";
|
||||||
|
static const String tryAgain = "tryAgain";
|
||||||
|
static const String welcome = "welcome";
|
||||||
|
static const String username = "username";
|
||||||
|
static const String password = "password";
|
||||||
|
static const String forgotPassword = "forgotPassword";
|
||||||
|
static const String signIn = "signIn";
|
||||||
|
static const String orContinueWith = "orContinueWith";
|
||||||
|
static const String google = "google";
|
||||||
|
static const String apple = "apple";
|
||||||
|
static const String dontHaveAccount = "dontHaveAccount";
|
||||||
|
static const String createOneNow = "createOneNow";
|
||||||
static const example = 'example';
|
static const example = 'example';
|
||||||
static const onboardingTitle1 = 'onboardingTitle1';
|
static const onboardingTitle1 = 'onboardingTitle1';
|
||||||
static const onboardingSubtitle1 = 'onboardingSubtitle1';
|
static const onboardingSubtitle1 = 'onboardingSubtitle1';
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class DepositBlock extends ConsumerWidget {
|
|||||||
child: CustomTextField(
|
child: CustomTextField(
|
||||||
label: "Cantidad",
|
label: "Cantidad",
|
||||||
hint: "0€",
|
hint: "0€",
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Align(
|
Align(
|
||||||
@@ -49,7 +49,7 @@ class DepositBlock extends ConsumerWidget {
|
|||||||
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||||
padding: 24,
|
padding: 24,
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Align(
|
Align(
|
||||||
@@ -60,10 +60,10 @@ class DepositBlock extends ConsumerWidget {
|
|||||||
Icon(Icons.info_outline, size: 16),
|
Icon(Icons.info_outline, size: 16),
|
||||||
Text("Máximo que puedes añadir: $max€"),
|
Text("Máximo que puedes añadir: $max€"),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user