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,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
),
|
),
|
||||||
CustomTextField(
|
|
||||||
label: "Nombre",
|
|
||||||
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,144 +1,158 @@
|
|||||||
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) {
|
||||||
|
final theme = ref.watch(themePortProvider);
|
||||||
|
final vm = ref.read(loginViewModelProvider.notifier);
|
||||||
|
final state = ref.watch(loginViewModelProvider);
|
||||||
|
|
||||||
|
Future<void> onSignIn() async {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
final login = await vm.login();
|
||||||
|
if (login) navigationContract.goTo(AppRoutes.dashboardHome);
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LoginScreenState extends ConsumerState<LoginScreen> {
|
return Scaffold(
|
||||||
bool passwordVisible = false;
|
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||||
|
body: SingleChildScrollView(
|
||||||
@override
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 40),
|
||||||
Widget build(BuildContext context) {
|
child: Column(
|
||||||
final theme = ref.watch(themePortProvider);
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|
||||||
bool passwordVisible = true;
|
|
||||||
|
|
||||||
final content = [
|
|
||||||
Column(
|
|
||||||
spacing: 8,
|
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Icons.check,
|
Icons.check,
|
||||||
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||||
size: 50,
|
size: 54,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
// context.translate(I18n.example)
|
context.translate(I18n.welcome),
|
||||||
"¡Te damos la bienvenida!",
|
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
|
style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
],
|
const SizedBox(height: 48),
|
||||||
),
|
|
||||||
Column(
|
|
||||||
spacing: 32,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
spacing: 24,
|
|
||||||
children: [
|
|
||||||
CustomTextField(
|
CustomTextField(
|
||||||
hint: "Nombre de usuario",
|
hint: context.translate(I18n.username),
|
||||||
label: "Nombre de usuario",
|
label: context.translate(I18n.username),
|
||||||
|
controller: vm.emailController,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
),
|
),
|
||||||
Column(
|
|
||||||
spacing: 12,
|
const SizedBox(height: 24),
|
||||||
children: [
|
|
||||||
CustomTextField(
|
CustomTextField(
|
||||||
showPassword: passwordVisible,
|
showPassword: state.passwordVisible,
|
||||||
label: "Contraseña",
|
label: context.translate(I18n.password),
|
||||||
hint: "********",
|
hint: "********",
|
||||||
|
controller: vm.passwordController,
|
||||||
|
|
||||||
|
textInputAction: TextInputAction.done,
|
||||||
|
onSubmitted: (_) => onSignIn(),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: CustomTextButton(
|
child: CustomTextButton(
|
||||||
text: "¿Has olvidado la contraseña?",
|
text: context.translate(I18n.forgotPassword),
|
||||||
onPressed: () => widget.navigationContract.pushTo(
|
onPressed: state.isLoading
|
||||||
AppRoutes.recoverPassword,
|
? () {}
|
||||||
),
|
: () =>
|
||||||
|
navigationContract.pushTo(AppRoutes.recoverPassword),
|
||||||
size: 16,
|
size: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
const SizedBox(height: 30),
|
||||||
],
|
|
||||||
),
|
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
onPressed: () =>
|
onPressed: state.isLoading ? () {} : onSignIn,
|
||||||
widget.navigationContract.goTo(AppRoutes.dashboardHome),
|
text: context.translate(I18n.signIn),
|
||||||
text: "Iniciar sesión",
|
|
||||||
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||||
|
leading: state.isLoading
|
||||||
|
? const SizedBox(
|
||||||
|
height: 18,
|
||||||
|
width: 18,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
Container(
|
)
|
||||||
padding: EdgeInsets.only(top: 24),
|
: null,
|
||||||
child: Column(
|
),
|
||||||
spacing: 24,
|
|
||||||
children: [
|
const SizedBox(height: 30),
|
||||||
|
|
||||||
Stack(
|
Stack(
|
||||||
children: [
|
children: [
|
||||||
Divider(endIndent: 74, indent: 74),
|
const Divider(endIndent: 74, indent: 74),
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14),
|
padding: const EdgeInsets.symmetric(horizontal: 14),
|
||||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||||
child: Text("o continúa con"),
|
child: Text(context.translate(I18n.orContinueWith)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
spacing: 20,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Spacer(),
|
|
||||||
SecondaryButton(
|
SecondaryButton(
|
||||||
onPressed: () => Navigator.push(
|
onPressed: state.isLoading
|
||||||
|
? () {}
|
||||||
|
: () => Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (_) => LoadingGoogleScreen(),
|
builder: (_) => const LoadingGoogleScreen(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
radius: 16,
|
radius: 16,
|
||||||
padding: 44,
|
padding: 44,
|
||||||
text: "Google",
|
text: context.translate(I18n.google),
|
||||||
label: "Google",
|
label: 'Google',
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
SecondaryButton(
|
SecondaryButton(
|
||||||
onPressed: () => {},
|
onPressed: state.isLoading ? () {} : () {},
|
||||||
radius: 16,
|
radius: 16,
|
||||||
padding: 44,
|
padding: 44,
|
||||||
icon: Icons.apple,
|
icon: Icons.apple,
|
||||||
label: "Apple",
|
label: 'Apple',
|
||||||
),
|
|
||||||
Spacer(),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
Column(
|
const SizedBox(height: 30),
|
||||||
spacing: 8,
|
|
||||||
children: [
|
|
||||||
Text(
|
Text(
|
||||||
"¿No tienes cuenta?",
|
context.translate(I18n.dontHaveAccount),
|
||||||
style: TextStyle(fontSize: 18, letterSpacing: 0),
|
style: const TextStyle(fontSize: 18, letterSpacing: 0),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () =>
|
onPressed: state.isLoading
|
||||||
widget.navigationContract.goTo(AppRoutes.signup),
|
? null
|
||||||
|
: () => navigationContract.goTo(AppRoutes.signup),
|
||||||
child: Text(
|
child: Text(
|
||||||
"Crear una ahora",
|
context.translate(I18n.createOneNow),
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0,
|
letterSpacing: 0,
|
||||||
@@ -147,27 +161,6 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
|
|
||||||
body: Expanded(
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
margin: EdgeInsets.all(30),
|
|
||||||
child: ListView.separated(
|
|
||||||
itemBuilder: (BuildContext context, int index) {
|
|
||||||
return content[index];
|
|
||||||
},
|
|
||||||
separatorBuilder: (BuildContext context, int index) {
|
|
||||||
return Divider(color: Colors.transparent, height: 48);
|
|
||||||
},
|
|
||||||
itemCount: content.length,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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(
|
||||||
|
alignment: Alignment.bottomLeft,
|
||||||
|
child: Text(
|
||||||
"Fecha de nacimiento",
|
"Fecha de nacimiento",
|
||||||
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
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,
|
||||||
)),
|
),
|
||||||
]
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -71,11 +79,14 @@ class SignupAddressScreenState extends ConsumerState<SignupAddressScreen>{
|
|||||||
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,
|
||||||
@@ -92,7 +103,7 @@ class SignupAddressScreenState extends ConsumerState<SignupAddressScreen>{
|
|||||||
hint: "País",
|
hint: "País",
|
||||||
onChanged: (value) => setState(() {
|
onChanged: (value) => setState(() {
|
||||||
country = value;
|
country = value;
|
||||||
})
|
}),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -21,16 +21,20 @@ class SignupPersonalScreen extends StatelessWidget{
|
|||||||
onChanged: (value)=> {},
|
onChanged: (value)=> {},
|
||||||
width: 80,
|
width: 80,
|
||||||
),*/
|
),*/
|
||||||
Expanded(child: CustomTextField(
|
Expanded(
|
||||||
|
child: CustomTextField(
|
||||||
label: "Teléfono móvil",
|
label: "Teléfono móvil",
|
||||||
hint: "123456789",
|
hint: "123456789",
|
||||||
numeric: true
|
keyboardType: TextInputType.number,
|
||||||
))
|
),
|
||||||
]
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
CustomTextField(
|
||||||
|
label: "Correo electrónico",
|
||||||
|
hint: "Correo electrónico",
|
||||||
),
|
),
|
||||||
CustomTextField(label: "Correo electrónico", hint: "Correo electrónico"),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ class _DepositScreenState extends ConsumerState<DepositScreen> {
|
|||||||
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(
|
||||||
|
|||||||
@@ -9,10 +9,7 @@ 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,11 +17,12 @@ class ExtractScreen extends ConsumerWidget{
|
|||||||
|
|
||||||
return WalletManagementLayout(
|
return WalletManagementLayout(
|
||||||
kid: kid,
|
kid: kid,
|
||||||
children: [Container(
|
children: [
|
||||||
|
Container(
|
||||||
padding: EdgeInsets.all(24),
|
padding: EdgeInsets.all(24),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(24)),
|
borderRadius: BorderRadius.all(Radius.circular(24)),
|
||||||
color: theme.getColorFor(ThemeCode.backgroundPrimary)
|
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
spacing: 24,
|
spacing: 24,
|
||||||
@@ -32,41 +30,64 @@ class ExtractScreen extends ConsumerWidget{
|
|||||||
Column(
|
Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
"Retirar dinero de la cuenta",
|
"Retirar dinero de la cuenta",
|
||||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0)
|
style: TextStyle(
|
||||||
)),
|
fontSize: 20,
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
"Este dato aparecerá en el reloj del peque",
|
"Este dato aparecerá en el reloj del peque",
|
||||||
style: TextStyle(fontSize: 14, letterSpacing: 0)
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
))
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
CustomTextField(
|
CustomTextField(
|
||||||
label: "Selecciona la cantidad de dinero",
|
label: "Selecciona la cantidad de dinero",
|
||||||
hint: "2€",
|
hint: "2€",
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
"Este es el mensaje fijado por defecto:",
|
"Este es el mensaje fijado por defecto:",
|
||||||
style: TextStyle(fontSize: 16, letterSpacing: 0)
|
style: TextStyle(fontSize: 16, letterSpacing: 0),
|
||||||
)),
|
),
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
"\"Hemos quitado el dinero del reloj, ya no puedes pagar con él\"",
|
"\"Hemos quitado el dinero del reloj, ya no puedes pagar con él\"",
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: 0)
|
style: TextStyle(
|
||||||
))
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
"Escribir mensaje a ${kid.name} del motivo de la retirada de su dinero",
|
"Escribir mensaje a ${kid.name} del motivo de la retirada de su dinero",
|
||||||
style: TextStyle(fontSize: 14, letterSpacing: 0)
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
CustomTextField(
|
CustomTextField(
|
||||||
hint: "Escribe tu mensaje",
|
hint: "Escribe tu mensaje",
|
||||||
lines: 4,
|
lines: 4,
|
||||||
@@ -76,19 +97,26 @@ class ExtractScreen extends ConsumerWidget{
|
|||||||
spacing: 4,
|
spacing: 4,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.info_outline, size: 16),
|
Icon(Icons.info_outline, size: 16),
|
||||||
Text("Máximo 150 caracteres", style: TextStyle(fontSize: 14, letterSpacing: 0))
|
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,
|
||||||
@@ -99,13 +127,15 @@ class ExtractScreen extends ConsumerWidget{
|
|||||||
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
style: ButtonStyle(padding: WidgetStatePropertyAll(EdgeInsets.all(0))),
|
style: ButtonStyle(
|
||||||
|
padding: WidgetStatePropertyAll(EdgeInsets.all(0)),
|
||||||
|
),
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
child: Text("Cancelar", style: TextStyle(fontSize: 18))
|
child: Text("Cancelar", style: TextStyle(fontSize: 18)),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,12 +7,18 @@ import 'package:sf_shared/sf_shared.dart';
|
|||||||
|
|
||||||
class GoalsScreen extends ConsumerStatefulWidget {
|
class GoalsScreen extends ConsumerStatefulWidget {
|
||||||
final Kid kid;
|
final Kid kid;
|
||||||
List<Task> tasks = [Task(rewardAmount: 10, subtasks: [
|
List<Task> tasks = [
|
||||||
|
Task(
|
||||||
|
rewardAmount: 10,
|
||||||
|
subtasks: [
|
||||||
Subtask(name: "Hacer la cama", completed: false),
|
Subtask(name: "Hacer la cama", completed: false),
|
||||||
Subtask(name: "Terminar los deberes", completed: true),
|
Subtask(name: "Terminar los deberes", completed: true),
|
||||||
Subtask(name: "Recoger la mesa", completed: true),
|
Subtask(name: "Recoger la mesa", completed: true),
|
||||||
Subtask(name: "Lavarse los dientes", completed: false)
|
Subtask(name: "Lavarse los dientes", completed: false),
|
||||||
]), Task(rewardAmount: 10)];
|
],
|
||||||
|
),
|
||||||
|
Task(rewardAmount: 10),
|
||||||
|
];
|
||||||
final List<SavingsGoal> savingsGoals;
|
final List<SavingsGoal> savingsGoals;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -21,8 +27,8 @@ class GoalsScreen extends ConsumerStatefulWidget{
|
|||||||
required this.kid,
|
required this.kid,
|
||||||
this.savingsGoals = const [
|
this.savingsGoals = const [
|
||||||
SavingsGoal(name: "Cumpleaños de Emma", goal: 24, saved: 12.09),
|
SavingsGoal(name: "Cumpleaños de Emma", goal: 24, saved: 12.09),
|
||||||
SavingsGoal(name: "Protecciones nuevas de patines", goal: 10, saved: 0)
|
SavingsGoal(name: "Protecciones nuevas de patines", goal: 10, saved: 0),
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -49,7 +55,7 @@ class GoalsScreenState extends ConsumerState<GoalsScreen>{
|
|||||||
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,
|
||||||
@@ -57,10 +63,20 @@ class GoalsScreenState extends ConsumerState<GoalsScreen>{
|
|||||||
Row(
|
Row(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Text("Metas", style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0)),
|
Text(
|
||||||
|
"Metas",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
Icon(Icons.workspace_premium),
|
Icon(Icons.workspace_premium),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
Text("Sólo con Plan Completo", style: TextStyle(fontSize: 14, letterSpacing: 0))
|
Text(
|
||||||
|
"Sólo con Plan Completo",
|
||||||
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
@@ -73,7 +89,7 @@ class GoalsScreenState extends ConsumerState<GoalsScreen>{
|
|||||||
SavingsBlock(fullPlan: fullPlan, savings: widget.savingsGoals),
|
SavingsBlock(fullPlan: fullPlan, savings: widget.savingsGoals),
|
||||||
TasksBlock(fullPlan: fullPlan, tasks: widget.tasks),
|
TasksBlock(fullPlan: fullPlan, tasks: widget.tasks),
|
||||||
],
|
],
|
||||||
footer: Container()
|
footer: Container(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +102,7 @@ class SavingsBlock extends ConsumerStatefulWidget {
|
|||||||
const SavingsBlock({
|
const SavingsBlock({
|
||||||
super.key,
|
super.key,
|
||||||
required this.fullPlan,
|
required this.fullPlan,
|
||||||
required this.savings
|
required this.savings,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -115,26 +131,40 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
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)),
|
||||||
border: BoxBorder.all(color: theme.getColorFor(ThemeCode.textPrimary))
|
border: BoxBorder.all(color: theme.getColorFor(ThemeCode.textPrimary)),
|
||||||
),
|
),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Align(alignment: Alignment.topRight, child: SvgPicture.asset("assets/images/ui/ahorros.svg")),
|
Align(
|
||||||
|
alignment: Alignment.topRight,
|
||||||
|
child: SvgPicture.asset("assets/images/ui/ahorros.svg"),
|
||||||
|
),
|
||||||
Column(
|
Column(
|
||||||
spacing: 24,
|
spacing: 24,
|
||||||
children: [
|
children: [
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
"Ahorros",
|
"Ahorros",
|
||||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0),
|
style: TextStyle(
|
||||||
)),
|
fontSize: 20,
|
||||||
Align(alignment: Alignment.topLeft, child: SizedBox(
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: SizedBox(
|
||||||
width: 200,
|
width: 200,
|
||||||
child: Text(
|
child: Text(
|
||||||
"Enséñale a ahorrar y refuerza la importancia de no malgastar su dinero",
|
"Enséñale a ahorrar y refuerza la importancia de no malgastar su dinero",
|
||||||
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
),
|
),
|
||||||
)),
|
),
|
||||||
if (fullPlan) Column(
|
),
|
||||||
|
if (fullPlan)
|
||||||
|
Column(
|
||||||
spacing: 24,
|
spacing: 24,
|
||||||
children: [
|
children: [
|
||||||
Align(
|
Align(
|
||||||
@@ -154,43 +184,52 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: TextButton(
|
child: TextButton(
|
||||||
onPressed: () => {},
|
onPressed: () => {},
|
||||||
style: ButtonStyle(padding: WidgetStatePropertyAll(EdgeInsets.zero)),
|
style: ButtonStyle(
|
||||||
|
padding: WidgetStatePropertyAll(EdgeInsets.zero),
|
||||||
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
"Ver estado de ahorros",
|
"Ver estado de ahorros",
|
||||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, letterSpacing: 0)
|
style: TextStyle(
|
||||||
)
|
fontSize: 14,
|
||||||
)
|
fontWeight: FontWeight.w500,
|
||||||
)
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (!fullPlan) TextButton(
|
if (!fullPlan)
|
||||||
|
TextButton(
|
||||||
onPressed: () => {},
|
onPressed: () => {},
|
||||||
child: Row(
|
child: Row(
|
||||||
spacing: 4,
|
spacing: 4,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.workspace_premium, size: 16),
|
Icon(Icons.workspace_premium, size: 16),
|
||||||
Text("Suscribirme al Plan Completo")
|
Text("Suscribirme al Plan Completo"),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
var editBlock = ({create, index}) => Column(
|
var editBlock = ({create, index}) => Column(
|
||||||
spacing: 24,
|
spacing: 24,
|
||||||
children: [
|
children: [
|
||||||
CustomTextField(
|
CustomTextField(
|
||||||
hint: create? "Ponle un título para reconocerlo" : widget.savings[index].name,
|
hint: create
|
||||||
|
? "Ponle un título para reconocerlo"
|
||||||
|
: widget.savings[index].name,
|
||||||
label: "Motivo del ahorro",
|
label: "Motivo del ahorro",
|
||||||
lines: create ? 2 : 1,
|
lines: create ? 2 : 1,
|
||||||
),
|
),
|
||||||
CustomTextField(
|
CustomTextField(
|
||||||
hint: "30€",
|
hint: "30€",
|
||||||
label: "Seleciona la cantidad a ahorrar",
|
label: "Seleciona la cantidad a ahorrar",
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
),
|
),
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
value: false,
|
value: false,
|
||||||
@@ -201,13 +240,17 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
title: Text(
|
title: Text(
|
||||||
"Ahorro automático desde su paga",
|
"Ahorro automático desde su paga",
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: 0),
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
CustomTextField(
|
CustomTextField(
|
||||||
hint: "2€",
|
hint: "2€",
|
||||||
label: "Selecciona la cantidad de dinero",
|
label: "Selecciona la cantidad de dinero",
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
),
|
),
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
value: false,
|
value: false,
|
||||||
@@ -230,8 +273,12 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"\"¡Genial, has conseguido ahorrar lo que querías!\"",
|
"\"¡Genial, has conseguido ahorrar lo que querías!\"",
|
||||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: 0),
|
style: TextStyle(
|
||||||
)
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
@@ -246,13 +293,13 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
Row(
|
Row(
|
||||||
spacing: 4,
|
spacing: 4,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.info_outline, size: 16,),
|
Icon(Icons.info_outline, size: 16),
|
||||||
Text(
|
Text(
|
||||||
"Máximo 150 caracteres",
|
"Máximo 150 caracteres",
|
||||||
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
@@ -261,8 +308,7 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
onPressed: () => {},
|
onPressed: () => {},
|
||||||
text: "Guardar cambios",
|
text: "Guardar cambios",
|
||||||
color: theme.getColorFor(
|
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||||
ThemeCode.buttonPrimary)
|
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -278,13 +324,15 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"Cancelar",
|
"Cancelar",
|
||||||
style: TextStyle(fontSize: 16,
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0),
|
letterSpacing: 0,
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -295,15 +343,22 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.getColorFor(ThemeCode.backgroundSecondary),
|
color: theme.getColorFor(ThemeCode.backgroundSecondary),
|
||||||
border: Border.all(color: theme.getColorFor(ThemeCode.textPrimary)),
|
border: Border.all(color: theme.getColorFor(ThemeCode.textPrimary)),
|
||||||
borderRadius: BorderRadius.all(Radius.circular(24))
|
borderRadius: BorderRadius.all(Radius.circular(24)),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
spacing: 24,
|
spacing: 24,
|
||||||
children: [
|
children: [
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
"Ahorros",
|
"Ahorros",
|
||||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0),
|
style: TextStyle(
|
||||||
)),
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
"Enséñale a ahorrar y refuerza la importancia de no malgastar su dinero",
|
"Enséñale a ahorrar y refuerza la importancia de no malgastar su dinero",
|
||||||
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
@@ -312,10 +367,10 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
padding: EdgeInsets.all(24),
|
padding: EdgeInsets.all(24),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(24)),
|
borderRadius: BorderRadius.all(Radius.circular(24)),
|
||||||
color: theme.getColorFor(ThemeCode.backgroundPrimary)
|
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||||
|
),
|
||||||
|
child: editBlock(create: true, index: null),
|
||||||
),
|
),
|
||||||
child: editBlock(create: true, index: null)
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -331,19 +386,24 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
child: Column(
|
child: Column(
|
||||||
spacing: 24,
|
spacing: 24,
|
||||||
children: [
|
children: [
|
||||||
Align(alignment: Alignment.topLeft,
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
"Ahorros",
|
"Ahorros",
|
||||||
style: TextStyle(fontSize: 20,
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0),
|
letterSpacing: 0,
|
||||||
)
|
|
||||||
),
|
),
|
||||||
...List<Widget>.generate(widget.savings.length, (int index) =>
|
),
|
||||||
Container(
|
),
|
||||||
|
...List<Widget>.generate(
|
||||||
|
widget.savings.length,
|
||||||
|
(int index) => Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: BoxBorder.all(
|
border: BoxBorder.all(
|
||||||
color: theme.getColorFor(ThemeCode.textPrimary)),
|
color: theme.getColorFor(ThemeCode.textPrimary),
|
||||||
|
),
|
||||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||||
borderRadius: BorderRadius.all(Radius.circular(24)),
|
borderRadius: BorderRadius.all(Radius.circular(24)),
|
||||||
),
|
),
|
||||||
@@ -354,7 +414,8 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
Row(
|
Row(
|
||||||
spacing: 16,
|
spacing: 16,
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: Column(
|
Expanded(
|
||||||
|
child: Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Align(
|
Align(
|
||||||
@@ -363,47 +424,61 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
"Ahorro para",
|
"Ahorro para",
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16, letterSpacing: 0),
|
fontSize: 16,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
widget.savings[index].name,
|
widget.savings[index].name,
|
||||||
style: TextStyle(fontSize: 16,
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0),
|
letterSpacing: 0,
|
||||||
)
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.getColorFor(
|
color: theme.getColorFor(
|
||||||
ThemeCode.backgroundTertiary),
|
ThemeCode.backgroundTertiary,
|
||||||
|
),
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(
|
||||||
Radius.circular(16)),
|
Radius.circular(16),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
horizontal: 8, vertical: 4),
|
horizontal: 8,
|
||||||
|
vertical: 4,
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.account_balance, size: 24,
|
Icon(
|
||||||
|
Icons.account_balance,
|
||||||
|
size: 24,
|
||||||
color: theme.getColorFor(
|
color: theme.getColorFor(
|
||||||
ThemeCode.buttonPrimary)),
|
ThemeCode.buttonPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
MoneyText(
|
MoneyText(
|
||||||
text: "${widget.savings[index]
|
text: "${widget.savings[index].goal}€",
|
||||||
.goal}€",
|
|
||||||
size: 30,
|
size: 30,
|
||||||
secondarySize: 14,
|
secondarySize: 14,
|
||||||
color: theme.getColorFor(
|
color: theme.getColorFor(
|
||||||
ThemeCode.buttonPrimary)
|
ThemeCode.buttonPrimary,
|
||||||
)
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
|
||||||
]
|
|
||||||
),
|
),
|
||||||
if (index == 0)Column(
|
],
|
||||||
|
),
|
||||||
|
if (index == 0)
|
||||||
|
Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
ProgressBar(
|
ProgressBar(
|
||||||
@@ -413,42 +488,52 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
textSize: 40,
|
textSize: 40,
|
||||||
textSecondarySize: 24,
|
textSecondarySize: 24,
|
||||||
backgroundColor: theme.getColorFor(
|
backgroundColor: theme.getColorFor(
|
||||||
ThemeCode.backgroundTertiary),
|
ThemeCode.backgroundTertiary,
|
||||||
|
),
|
||||||
foregroundColor: theme.getColorFor(
|
foregroundColor: theme.getColorFor(
|
||||||
ThemeCode.buttonPrimary),
|
ThemeCode.buttonPrimary,
|
||||||
|
),
|
||||||
textColor: theme.getColorFor(
|
textColor: theme.getColorFor(
|
||||||
ThemeCode.textSecondary)
|
ThemeCode.textSecondary,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Center(child: Text("Ahorrado")),
|
Center(child: Text("Ahorrado")),
|
||||||
if (!showAdd[index]) TextButton(
|
if (!showAdd[index])
|
||||||
|
TextButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: WidgetStatePropertyAll(
|
padding: WidgetStatePropertyAll(
|
||||||
EdgeInsets.zero)),
|
EdgeInsets.zero,
|
||||||
onPressed: () =>
|
),
|
||||||
setState(() {
|
),
|
||||||
|
onPressed: () => setState(() {
|
||||||
showAdd[index] = true;
|
showAdd[index] = true;
|
||||||
}),
|
}),
|
||||||
child: Text(
|
child: Text(
|
||||||
"+ Añadir dinero extra a este ahorro",
|
"+ Añadir dinero extra a este ahorro",
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16, letterSpacing: 0),
|
fontSize: 16,
|
||||||
)
|
letterSpacing: 0,
|
||||||
),
|
),
|
||||||
if (showAdd[index]) CustomTextField(
|
),
|
||||||
|
),
|
||||||
|
if (showAdd[index])
|
||||||
|
CustomTextField(
|
||||||
hint: "5€",
|
hint: "5€",
|
||||||
numeric: true,
|
keyboardType: TextInputType.number,
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (!showEdit[index]) Row(
|
if (!showEdit[index])
|
||||||
|
Row(
|
||||||
children: [
|
children: [
|
||||||
TextButton(
|
TextButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: WidgetStatePropertyAll(
|
padding: WidgetStatePropertyAll(
|
||||||
EdgeInsets.zero)),
|
EdgeInsets.zero,
|
||||||
onPressed: () =>
|
),
|
||||||
setState(() {
|
),
|
||||||
|
onPressed: () => setState(() {
|
||||||
showEdit[index] = true;
|
showEdit[index] = true;
|
||||||
}),
|
}),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -457,44 +542,51 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
Icon(Icons.edit_outlined, size: 24),
|
Icon(Icons.edit_outlined, size: 24),
|
||||||
Text(
|
Text(
|
||||||
"Editar",
|
"Editar",
|
||||||
style: TextStyle(fontSize: 16,
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0),
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
TextButton(
|
TextButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: WidgetStatePropertyAll(
|
padding: WidgetStatePropertyAll(
|
||||||
EdgeInsets.zero)),
|
EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
),
|
||||||
onPressed: () => {},
|
onPressed: () => {},
|
||||||
child: Row(
|
child: Row(
|
||||||
spacing: 4,
|
spacing: 4,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.delete_outline_outlined,
|
Icon(
|
||||||
size: 24),
|
Icons.delete_outline_outlined,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
"Eliminar",
|
"Eliminar",
|
||||||
style: TextStyle(fontSize: 16,
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0),
|
letterSpacing: 0,
|
||||||
)
|
),
|
||||||
],
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if(showEdit[index]) editBlock(
|
),
|
||||||
create: false, index: index)
|
],
|
||||||
|
),
|
||||||
|
if (showEdit[index])
|
||||||
|
editBlock(create: false, index: index),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () =>
|
onPressed: () => setState(() {
|
||||||
setState(() {
|
|
||||||
showNew = true;
|
showNew = true;
|
||||||
}),
|
}),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -504,13 +596,15 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
Icon(Icons.account_balance, size: 24),
|
Icon(Icons.account_balance, size: 24),
|
||||||
Text(
|
Text(
|
||||||
"Crear otro ahorro",
|
"Crear otro ahorro",
|
||||||
style: TextStyle(fontSize: 18,
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0),
|
letterSpacing: 0,
|
||||||
),
|
),
|
||||||
Spacer()
|
),
|
||||||
|
Spacer(),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -526,23 +620,17 @@ class SavingsBlockState extends ConsumerState<SavingsBlock>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TasksBlock extends ConsumerStatefulWidget {
|
class TasksBlock extends ConsumerStatefulWidget {
|
||||||
|
|
||||||
final bool fullPlan;
|
final bool fullPlan;
|
||||||
final List<Task> tasks;
|
final List<Task> tasks;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
const TasksBlock({
|
const TasksBlock({super.key, required this.fullPlan, required this.tasks});
|
||||||
super.key,
|
|
||||||
required this.fullPlan,
|
|
||||||
required this.tasks
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<ConsumerStatefulWidget> createState() => TasksBlockState();
|
ConsumerState<ConsumerStatefulWidget> createState() => TasksBlockState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class TasksBlockState extends ConsumerState<TasksBlock> {
|
class TasksBlockState extends ConsumerState<TasksBlock> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = ref.watch(themePortProvider);
|
final theme = ref.watch(themePortProvider);
|
||||||
@@ -552,26 +640,40 @@ class TasksBlockState extends ConsumerState<TasksBlock>{
|
|||||||
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)),
|
||||||
border: BoxBorder.all(color: theme.getColorFor(ThemeCode.textPrimary))
|
border: BoxBorder.all(color: theme.getColorFor(ThemeCode.textPrimary)),
|
||||||
),
|
),
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Align(alignment: Alignment.topRight, child: SvgPicture.asset("assets/images/ui/tareas.svg")),
|
Align(
|
||||||
|
alignment: Alignment.topRight,
|
||||||
|
child: SvgPicture.asset("assets/images/ui/tareas.svg"),
|
||||||
|
),
|
||||||
Column(
|
Column(
|
||||||
spacing: 24,
|
spacing: 24,
|
||||||
children: [
|
children: [
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
"Tareas",
|
"Tareas",
|
||||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500, letterSpacing: 0),
|
style: TextStyle(
|
||||||
)),
|
fontSize: 20,
|
||||||
Align(alignment: Alignment.topLeft, child: SizedBox(
|
fontWeight: FontWeight.w500,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: SizedBox(
|
||||||
width: 200,
|
width: 200,
|
||||||
child: Text(
|
child: Text(
|
||||||
"Refuerza su sentido de la responsabilidad con pequeñas tareas que cumplir",
|
"Refuerza su sentido de la responsabilidad con pequeñas tareas que cumplir",
|
||||||
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
),
|
),
|
||||||
)),
|
),
|
||||||
if (fullPlan) Align(
|
),
|
||||||
|
if (fullPlan)
|
||||||
|
Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: SecondaryButton(
|
child: SecondaryButton(
|
||||||
onPressed: () => {},
|
onPressed: () => {},
|
||||||
@@ -579,22 +681,23 @@ class TasksBlockState extends ConsumerState<TasksBlock>{
|
|||||||
size: 14,
|
size: 14,
|
||||||
width: 190,
|
width: 190,
|
||||||
height: 44,
|
height: 44,
|
||||||
)
|
|
||||||
),
|
),
|
||||||
if (!fullPlan) TextButton(
|
),
|
||||||
|
if (!fullPlan)
|
||||||
|
TextButton(
|
||||||
onPressed: () => {},
|
onPressed: () => {},
|
||||||
child: Row(
|
child: Row(
|
||||||
spacing: 4,
|
spacing: 4,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.workspace_premium, size: 16),
|
Icon(Icons.workspace_premium, size: 16),
|
||||||
Text("Suscribirme al Plan Completo")
|
Text("Suscribirme al Plan Completo"),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (widget.fullPlan) {
|
if (widget.fullPlan) {
|
||||||
@@ -604,8 +707,9 @@ class TasksBlockState extends ConsumerState<TasksBlock>{
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(24)),
|
borderRadius: BorderRadius.all(Radius.circular(24)),
|
||||||
border: BoxBorder.all(
|
border: BoxBorder.all(
|
||||||
color: theme.getColorFor(ThemeCode.textPrimary)),
|
color: theme.getColorFor(ThemeCode.textPrimary),
|
||||||
color: theme.getColorFor(ThemeCode.backgroundSecondary)
|
),
|
||||||
|
color: theme.getColorFor(ThemeCode.backgroundSecondary),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
spacing: 24,
|
spacing: 24,
|
||||||
@@ -613,113 +717,136 @@ class TasksBlockState extends ConsumerState<TasksBlock>{
|
|||||||
Column(
|
Column(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Align(alignment: Alignment.topLeft, child: Text(
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Text(
|
||||||
"Tareas",
|
"Tareas",
|
||||||
style: TextStyle(fontSize: 20,
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0),
|
letterSpacing: 0,
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
"Refuerza su sentido de la responsabilidad con pequeñas tareas que cumplir",
|
"Refuerza su sentido de la responsabilidad con pequeñas tareas que cumplir",
|
||||||
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
...List<Widget>.generate(widget.tasks.length, (int i) =>
|
...List<Widget>.generate(
|
||||||
Container(
|
widget.tasks.length,
|
||||||
|
(int i) => Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: BoxBorder.fromLTRB(top: BorderSide(
|
border: BoxBorder.fromLTRB(
|
||||||
|
top: BorderSide(
|
||||||
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
color: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||||
width: 8)),
|
width: 8,
|
||||||
|
),
|
||||||
|
),
|
||||||
borderRadius: BorderRadius.all(Radius.circular(24)),
|
borderRadius: BorderRadius.all(Radius.circular(24)),
|
||||||
color: theme.getColorFor(ThemeCode.backgroundPrimary)
|
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||||
),
|
),
|
||||||
padding: EdgeInsets.all(24),
|
padding: EdgeInsets.all(24),
|
||||||
child: Column(
|
child: Column(
|
||||||
spacing: 16,
|
spacing: 16,
|
||||||
children: [
|
children: [
|
||||||
Row(children: [
|
Row(
|
||||||
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Lista de tareas",
|
"Lista de tareas",
|
||||||
style: TextStyle(fontSize: 20,
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0),
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
Text(
|
Text(
|
||||||
"10 oct - 17 oct",
|
"10 oct - 17 oct",
|
||||||
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
)
|
),
|
||||||
]),
|
],
|
||||||
|
),
|
||||||
...List<CheckboxListTile>.generate(
|
...List<CheckboxListTile>.generate(
|
||||||
widget.tasks[i].subtasks.length, (int j) =>
|
widget.tasks[i].subtasks.length,
|
||||||
CheckboxListTile(
|
(int j) => CheckboxListTile(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
checkboxScaleFactor: 2,
|
checkboxScaleFactor: 2,
|
||||||
value: widget.tasks[i].subtasks[j].completed,
|
value: widget.tasks[i].subtasks[j].completed,
|
||||||
title: Text(widget.tasks[i].subtasks[j].name),
|
title: Text(widget.tasks[i].subtasks[j].name),
|
||||||
controlAffinity: ListTileControlAffinity
|
controlAffinity: ListTileControlAffinity.leading,
|
||||||
.leading,
|
|
||||||
activeColor: theme.getColorFor(
|
activeColor: theme.getColorFor(
|
||||||
ThemeCode.buttonPrimary),
|
ThemeCode.buttonPrimary,
|
||||||
onChanged: (_) =>
|
),
|
||||||
setState(() {
|
onChanged: (_) => setState(() {
|
||||||
widget.tasks[i].subtasks[j] = Subtask(
|
widget.tasks[i].subtasks[j] = Subtask(
|
||||||
name: widget.tasks[i].subtasks[j]
|
name: widget.tasks[i].subtasks[j].name,
|
||||||
.name,
|
completed: !widget.tasks[i].subtasks[j].completed,
|
||||||
completed: !widget.tasks[i]
|
);
|
||||||
.subtasks[j].completed);
|
|
||||||
}),
|
}),
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
padding: WidgetStatePropertyAll(EdgeInsets
|
padding: WidgetStatePropertyAll(EdgeInsets.zero),
|
||||||
.zero)),
|
),
|
||||||
onPressed: () => {},
|
onPressed: () => {},
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
"+ Añadir tarea",
|
"+ Añadir tarea",
|
||||||
style: TextStyle(fontSize: 18,
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0),
|
letterSpacing: 0,
|
||||||
)
|
),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: theme.getColorFor(ThemeCode
|
color: theme.getColorFor(
|
||||||
.backgroundTertiary),
|
ThemeCode.backgroundTertiary,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(
|
),
|
||||||
24))
|
borderRadius: BorderRadius.all(Radius.circular(24)),
|
||||||
),
|
),
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
horizontal: 20, vertical: 16),
|
horizontal: 20,
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
spacing: 16,
|
spacing: 16,
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: Text(
|
Expanded(
|
||||||
|
child: Text(
|
||||||
"Recompensa por cumplimiento",
|
"Recompensa por cumplimiento",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16, letterSpacing: 0),
|
fontSize: 16,
|
||||||
)),
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.emoji_events_outlined, size: 16,
|
Icon(
|
||||||
|
Icons.emoji_events_outlined,
|
||||||
|
size: 16,
|
||||||
color: theme.getColorFor(
|
color: theme.getColorFor(
|
||||||
ThemeCode.buttonPrimary)),
|
ThemeCode.buttonPrimary,
|
||||||
|
),
|
||||||
|
),
|
||||||
MoneyText(
|
MoneyText(
|
||||||
text: "${widget.tasks[i]
|
text: "${widget.tasks[i].rewardAmount}€",
|
||||||
.rewardAmount}€",
|
|
||||||
size: 40,
|
size: 40,
|
||||||
secondarySize: 24,
|
secondarySize: 24,
|
||||||
color: theme.getColorFor(
|
color: theme.getColorFor(
|
||||||
ThemeCode.buttonPrimary)
|
ThemeCode.buttonPrimary,
|
||||||
)
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -730,17 +857,18 @@ class TasksBlockState extends ConsumerState<TasksBlock>{
|
|||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
"Este es el mensaje que se enviará al cumplir con todas las tareas:",
|
"Este es el mensaje que se enviará al cumplir con todas las tareas:",
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 14, letterSpacing: 0),
|
||||||
fontSize: 14, letterSpacing: 0),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Align(
|
Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
"¡Enhorabuena, has cumplido todas las tareas de esta semana!",
|
"¡Enhorabuena, has cumplido todas las tareas de esta semana!",
|
||||||
style: TextStyle(fontSize: 14,
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0),
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -752,21 +880,22 @@ class TasksBlockState extends ConsumerState<TasksBlock>{
|
|||||||
child: Row(
|
child: Row(
|
||||||
spacing: 4,
|
spacing: 4,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.delete_outline_outlined,
|
Icon(Icons.delete_outline_outlined, size: 24),
|
||||||
size: 24),
|
|
||||||
Text(
|
Text(
|
||||||
"Eliminar lista de tareas",
|
"Eliminar lista de tareas",
|
||||||
style: TextStyle(fontSize: 16,
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0),
|
letterSpacing: 0,
|
||||||
)
|
),
|
||||||
],
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
SecondaryButton(
|
SecondaryButton(
|
||||||
onPressed: () => {},
|
onPressed: () => {},
|
||||||
@@ -774,7 +903,7 @@ class TasksBlockState extends ConsumerState<TasksBlock>{
|
|||||||
size: 14,
|
size: 14,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return emptyBlock(fullPlan: true);
|
return emptyBlock(fullPlan: true);
|
||||||
|
|||||||
@@ -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,7 +80,7 @@ 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),
|
||||||
@@ -90,10 +89,10 @@ class LimitsScreenState extends ConsumerState<LimitsScreen> {
|
|||||||
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: [
|
||||||
|
Expanded(
|
||||||
|
child: CheckboxListTile(
|
||||||
value: conditions[index]["active"],
|
value: conditions[index]["active"],
|
||||||
onChanged: (_) => setState(() {
|
onChanged: (_) => setState(() {
|
||||||
conditions[index]["active"] = !conditions[index]["active"];
|
conditions[index]["active"] =
|
||||||
|
!conditions[index]["active"];
|
||||||
}),
|
}),
|
||||||
title: Text(
|
title: Text(
|
||||||
"${conditions[index]["title"]}: ${conditions[index]["limit"]}€/sem",
|
"${conditions[index]["title"]}: ${conditions[index]["limit"]}€/sem",
|
||||||
style: TextStyle(fontSize: 16, letterSpacing: 0),
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
letterSpacing: 0,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
checkboxScaleFactor: 2,
|
checkboxScaleFactor: 2,
|
||||||
controlAffinity: ListTileControlAffinity.leading,
|
controlAffinity: ListTileControlAffinity.leading,
|
||||||
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
|
activeColor: theme.getColorFor(
|
||||||
|
ThemeCode.buttonPrimary,
|
||||||
|
),
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => setState(() {
|
onPressed: () => setState(() {
|
||||||
conditions[index]["edit"] = ! conditions[index]["edit"];
|
conditions[index]["edit"] =
|
||||||
|
!conditions[index]["edit"];
|
||||||
}),
|
}),
|
||||||
child: Text(
|
child: Text(
|
||||||
"Editar",
|
"Editar",
|
||||||
style: TextStyle(fontSize: 16, letterSpacing: 0),
|
style: TextStyle(fontSize: 16, letterSpacing: 0),
|
||||||
)
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
if (conditions[index]["edit"]) CustomTextField(
|
|
||||||
hint: "5€",
|
|
||||||
numeric: true,
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
)
|
),
|
||||||
|
if (conditions[index]["edit"])
|
||||||
|
CustomTextField(
|
||||||
|
hint: "5€",
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
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,12 +288,12 @@ 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,7 +142,15 @@ 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: [
|
||||||
|
"Lunes",
|
||||||
|
"Martes",
|
||||||
|
"Miércoles",
|
||||||
|
"Jueves",
|
||||||
|
"Viernes",
|
||||||
|
"Sábado",
|
||||||
|
"Domingo",
|
||||||
|
],
|
||||||
onChanged: (value) => {},
|
onChanged: (value) => {},
|
||||||
hint: "Día de la semana",
|
hint: "Día de la semana",
|
||||||
),
|
),
|
||||||
@@ -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,
|
||||||
|
// mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (leading != null) ...[
|
||||||
|
IconTheme.merge(
|
||||||
|
data: const IconThemeData(color: Colors.white, size: 20),
|
||||||
|
child: leading!,
|
||||||
|
),
|
||||||
|
SizedBox(width: leadingGap),
|
||||||
|
],
|
||||||
|
Text(
|
||||||
text,
|
text,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: size,
|
fontSize: size,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
letterSpacing: 0,
|
letterSpacing: 0,
|
||||||
color: Colors.white, // theme.getColorFor(ThemeCode.textSecondary)
|
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