From 8201bff0a79133b45914d72a6c85cf457367aeb8 Mon Sep 17 00:00:00 2001 From: aitorarana Date: Fri, 21 Nov 2025 15:28:46 +0100 Subject: [PATCH] - created snackbar, step indicator, money text, text field and progress bar components. - created wallet balance block, wallet item and kid line chart widgets. - restructured onboarding, signup and device signup screens into layouts and main screens. - updated signup and kid wallet screens to 17/11 design. --- apps/mobile_app/lib/main.dart | 4 +- .../mobile_app/lib/navigation/app_router.dart | 7 +- apps/mobile_app/pubspec.yaml | 4 +- modules/auth/lib/auth.dart | 1 + .../src/device_sign_up/add_kid_screen.dart | 14 +- .../src/device_sign_up/contact_screen.dart | 31 +- .../device_sign_up/device_signup_builder.dart | 18 + .../device_sign_up/device_signup_screen.dart | 101 ++++ .../account_created_kid_screen.dart | 87 ---- .../link_watch/create_profile_screen.dart | 338 ++----------- .../link_watch_previous_screen.dart | 58 +++ .../link_watch/link_watch_screen.dart | 60 +++ .../login/presentation/link_phone_screen.dart | 32 +- .../src/login/presentation/login_screen.dart | 35 +- .../login/presentation/phone_code_screen.dart | 3 - .../presentation/welcome_screen.dart | 140 ++++-- .../presentation/email_sent_screen.dart | 3 +- .../presentation/new_password_screen.dart | 53 +- .../presentation/restore_password_screen.dart | 9 +- .../src/sign_up/account_created_screen.dart | 30 +- .../src/sign_up/signup_address_screen.dart | 58 ++- .../src/sign_up/signup_personal_screen.dart | 54 +- .../auth/lib/src/sign_up/signup_screen.dart | 173 ++----- .../lib/src/sign_up/signup_user_screen.dart | 64 +-- .../src/widgets/layouts/form_step_layout.dart | 91 ++++ .../lib/src/presentation/deposit_screen.dart | 32 +- .../lib/src/presentation/home_screen.dart | 214 +------- .../src/presentation/kid_wallet_screen.dart | 463 ++++++++++-------- .../lib/src/presentation/limits_screen.dart | 4 +- .../lib/src/presentation/wage_screen.dart | 29 +- .../lib/src/presentation/wallet_item.dart | 248 ++++++++++ .../wallet_management_layout.dart | 6 +- modules/home/pubspec.yaml | 2 + .../lib/src/core/activity_list.dart | 73 ++- .../lib/src/presentation/activity_screen.dart | 2 +- .../lib/src/presentation/alert_screen.dart | 6 +- .../profile/lib/src/core/kid_line_chart.dart | 178 +++++++ .../lib/src/presentation/profile_screen.dart | 71 ++- modules/profile/lib/src/profile_builder.dart | 1 - modules/profile/pubspec.yaml | 2 + packages/design_system/lib/design_system.dart | 5 + .../lib/src/inputs/textfields.dart | 72 +++ .../lib/src/progress_bars/progress_bar.dart | 56 +++ .../lib/src/snackbars/snackbar.dart | 65 +++ .../lib/src/steps/step_indicator.dart | 29 ++ .../lib/src/texts}/money_text.dart | 18 +- .../lib/src/theme/theme_port.dart | 5 + .../lib/src/theme/theme_sf_adapter.dart | 7 + packages/design_system/pubspec.yaml | 2 + packages/fonts/pubspec.yaml | 20 + packages/sf_shared/lib/sf_shared.dart | 1 + packages/sf_shared/lib/src/models/kid.dart | 3 +- .../lib/src/widgets/deposit_block.dart | 71 +-- .../sf_shared/lib/src/widgets/line_graph.dart | 268 +++++----- .../lib/src/widgets/wallet_balance_block.dart | 59 +++ pubspec.yaml | 2 +- 56 files changed, 1991 insertions(+), 1491 deletions(-) create mode 100644 modules/auth/lib/src/device_sign_up/device_signup_builder.dart create mode 100644 modules/auth/lib/src/device_sign_up/device_signup_screen.dart delete mode 100644 modules/auth/lib/src/device_sign_up/link_watch/account_created_kid_screen.dart create mode 100644 modules/auth/lib/src/device_sign_up/link_watch/link_watch_previous_screen.dart create mode 100644 modules/auth/lib/src/device_sign_up/link_watch/link_watch_screen.dart create mode 100644 modules/auth/lib/src/widgets/layouts/form_step_layout.dart create mode 100644 modules/home/lib/src/presentation/wallet_item.dart create mode 100644 modules/profile/lib/src/core/kid_line_chart.dart create mode 100644 packages/design_system/lib/src/inputs/textfields.dart create mode 100644 packages/design_system/lib/src/progress_bars/progress_bar.dart create mode 100644 packages/design_system/lib/src/snackbars/snackbar.dart create mode 100644 packages/design_system/lib/src/steps/step_indicator.dart rename {modules/home/lib/src/presentation => packages/design_system/lib/src/texts}/money_text.dart (66%) create mode 100644 packages/fonts/pubspec.yaml create mode 100644 packages/sf_shared/lib/src/widgets/wallet_balance_block.dart diff --git a/apps/mobile_app/lib/main.dart b/apps/mobile_app/lib/main.dart index 37384b82..8fc0aaa8 100644 --- a/apps/mobile_app/lib/main.dart +++ b/apps/mobile_app/lib/main.dart @@ -21,10 +21,12 @@ class PlatformApp extends ConsumerWidget { return MaterialApp.router( title: 'SaveFamily', theme: ThemeData( + fontFamily: 'Stolzl', + package: 'fonts', colorScheme: ColorScheme.fromSeed(seedColor: Color(0xFF329E95)), ), routerConfig: appRouter, debugShowCheckedModeBanner: false, ); } -} +} \ No newline at end of file diff --git a/apps/mobile_app/lib/navigation/app_router.dart b/apps/mobile_app/lib/navigation/app_router.dart index 020f8866..6d0f5127 100644 --- a/apps/mobile_app/lib/navigation/app_router.dart +++ b/apps/mobile_app/lib/navigation/app_router.dart @@ -16,7 +16,7 @@ late final GoRouter appRouter; void configureAppRouter() { appRouter = GoRouter( navigatorKey: rootNavigatorKey, - initialLocation: '/login', + initialLocation: '/onboarding', // redirect: (context, state) {}, routes: [ GoRoute( @@ -44,6 +44,11 @@ void configureAppRouter() { name: 'recover_password', pageBuilder: RecoverPasswordBuilder().buildPage, ), + GoRoute( + path: '/device_signup', + name: 'device_signup', + pageBuilder: DeviceSignupBuilder().buildPage, + ), StatefulShellRoute.indexedStack( builder: (context, state, navShell) { return DashboardBuilder().build(context, navShell); diff --git a/apps/mobile_app/pubspec.yaml b/apps/mobile_app/pubspec.yaml index 3568458c..74320dc5 100644 --- a/apps/mobile_app/pubspec.yaml +++ b/apps/mobile_app/pubspec.yaml @@ -50,7 +50,9 @@ dependencies: navigation: path: ../../packages/navigation design_system: - path: ../../packages/design_system + path: ../../packages/design_system + fonts: + path: ../../packages/fonts #dependencies go here cupertino_icons: ^1.0.8 diff --git a/modules/auth/lib/auth.dart b/modules/auth/lib/auth.dart index 03108e74..f7e85138 100644 --- a/modules/auth/lib/auth.dart +++ b/modules/auth/lib/auth.dart @@ -4,3 +4,4 @@ export 'src/login/link_phone_builder.dart'; export 'src/login/phone_code_builder.dart'; export 'src/login/login_builder.dart'; export 'src/recover_password/recover_password_builder.dart'; +export 'src/device_sign_up/device_signup_builder.dart'; \ No newline at end of file diff --git a/modules/auth/lib/src/device_sign_up/add_kid_screen.dart b/modules/auth/lib/src/device_sign_up/add_kid_screen.dart index 37531a5c..ea7d13f9 100644 --- a/modules/auth/lib/src/device_sign_up/add_kid_screen.dart +++ b/modules/auth/lib/src/device_sign_up/add_kid_screen.dart @@ -1,8 +1,9 @@ -import 'package:auth/src/device_sign_up/link_watch/create_profile_screen.dart'; import 'package:flutter/material.dart'; class AddKidScreen extends StatelessWidget { - const AddKidScreen({super.key}); + final nextStep; + + const AddKidScreen({super.key, required this.nextStep}); @override Widget build(BuildContext context) { @@ -13,7 +14,7 @@ class AddKidScreen extends StatelessWidget { spacing: 15, children: [ Spacer(flex: 6), - Text("Añade a tu peque"), + Text("Añade a tu peque", style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold)), Text( "Controla su gasto a la vez que aprende hábitos financieros responsables", ), @@ -32,7 +33,7 @@ class AddKidScreen extends StatelessWidget { ], ), ), - Text("¡Y todo listo para que tenga su dinero!"), + Text("¡Y todo listo para que tenga su dinero!", style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), Text("Recuerda que necesitas tener un Plan SaveFamily"), Text( "Si aún no lo tienes, puedes conseguirlo a través de nuestra web", @@ -41,10 +42,7 @@ class AddKidScreen extends StatelessWidget { Container( width: double.infinity, child: FilledButton( - onPressed: () => Navigator.push( - context, - MaterialPageRoute(builder: (_) => CreateProfileScreen()), - ), + onPressed: nextStep, child: Text("¡Empezar!"), ), ), diff --git a/modules/auth/lib/src/device_sign_up/contact_screen.dart b/modules/auth/lib/src/device_sign_up/contact_screen.dart index e7f7852c..6a7befe3 100644 --- a/modules/auth/lib/src/device_sign_up/contact_screen.dart +++ b/modules/auth/lib/src/device_sign_up/contact_screen.dart @@ -1,3 +1,4 @@ +import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; class ContactScreen extends StatelessWidget { @@ -36,32 +37,22 @@ class ContactScreen extends StatelessWidget { ], ), Expanded( - child: TextField( - decoration: InputDecoration( - labelText: "Nombre", - hintText: "Nombre y apellidos", - border: OutlineInputBorder(), - ), + child: CustomTextField( + label: "Nombre", + hint: "Nombre y apellidos", ), ), Expanded( - child: TextField( - decoration: InputDecoration( - labelText: "Correo electrónico", - hintText: "Correo electrónico", - border: OutlineInputBorder(), - ), + child: CustomTextField( + label: "Correo electrónico", + hint: "Correo electrónico", ), ), Expanded( - child: TextField( - minLines: 3, - maxLines: 3, - decoration: InputDecoration( - labelText: "Asunto del mensaje", - hintText: "Escribe tu mensaje", - border: OutlineInputBorder(), - ), + child: CustomTextField( + lines: 3, + label: "Asunto del mensaje", + hint: "Escribe tu mensaje", ), ), Expanded( diff --git a/modules/auth/lib/src/device_sign_up/device_signup_builder.dart b/modules/auth/lib/src/device_sign_up/device_signup_builder.dart new file mode 100644 index 00000000..f9ed93c6 --- /dev/null +++ b/modules/auth/lib/src/device_sign_up/device_signup_builder.dart @@ -0,0 +1,18 @@ +import 'package:auth/src/device_sign_up/device_signup_screen.dart'; +import 'package:flutter/material.dart'; +import 'package:get_it/get_it.dart'; +import 'package:go_router/go_router.dart'; +import 'package:navigation/navigation.dart'; + +class DeviceSignupBuilder { + const DeviceSignupBuilder(); + + Page buildPage(BuildContext context, GoRouterState state) { + final NavigationContract navigationContract = GetIt.I(); + + return MaterialPage( + key: state.pageKey, + child: DeviceSignupScreen(navigationContract: navigationContract), + ); + } +} diff --git a/modules/auth/lib/src/device_sign_up/device_signup_screen.dart b/modules/auth/lib/src/device_sign_up/device_signup_screen.dart new file mode 100644 index 00000000..1be84427 --- /dev/null +++ b/modules/auth/lib/src/device_sign_up/device_signup_screen.dart @@ -0,0 +1,101 @@ +import 'package:auth/auth.dart'; +import 'package:auth/src/device_sign_up/add_kid_screen.dart'; +import 'package:auth/src/device_sign_up/link_watch/link_watch_screen.dart'; +import 'package:auth/src/device_sign_up/link_watch/link_watch_previous_screen.dart'; +import 'package:auth/src/sign_up/account_created_screen.dart'; +import 'package:auth/src/widgets/layouts/form_step_layout.dart'; +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:navigation/navigation.dart'; + +class DeviceSignupScreen extends ConsumerStatefulWidget{ + final NavigationContract navigationContract; + + const DeviceSignupScreen({super.key, required this.navigationContract}); + + @override + ConsumerState createState() => DeviceSignupScreenState(navigationContract); +} + +class DeviceSignupScreenState extends ConsumerState{ + late int currentStep; + final NavigationContract navigationContract; + + DeviceSignupScreenState(this.navigationContract); + + @override + void initState() { + currentStep = 0; + } + + @override + Widget build(BuildContext context) { + return getSteps()[currentStep]; + } + + List getSteps(){ + final theme = ref.watch(themePortProvider); + + final continueBtn = Container( + padding: EdgeInsets.all(24), + color: theme.getColorFor(ThemeCode.backgroundPrimary), + child: FilledButton( + onPressed: ()=>{setState(() { + currentStep++; + })}, + child: Container( + width: double.infinity, + child: Expanded(child: Center(child: Text("Continuar")))) + ) + ); + + return [ + AddKidScreen(nextStep: ()=>{setState(() { + currentStep++; + })}), + FormStepLayout( + title: "Crea su perfil", + subtitle: "Necesitamos estos datos para crear su cuenta y gestionar sus pagos y gastos", + currentStep: 1, + numSteps: 3, + body: [CreateProfileScreen()], + footer: [Container( + padding: EdgeInsets.all(24), + color: theme.getColorFor(ThemeCode.backgroundPrimary), + child: continueBtn + )], + nextStep: ()=>{}, + previousStep: ()=>{} + ), + FormStepLayout( + title: "Vincula su correa y su reloj", + currentStep: 2, + numSteps: 3, + body: [LinkWatchPreviousScreen()], + footer: [continueBtn], + nextStep: ()=>{}, + previousStep: ()=>{} + ), + FormStepLayout( + title: "Vincula su correa\ny su reloj", + currentStep: 2, + numSteps: 3, + body: [LinkWatchScreen(step:1)], + footer: [continueBtn], + nextStep: ()=>{}, + previousStep: ()=>{} + ), + FormStepLayout( + title: "Vincula su correa\ny su reloj", + currentStep: 2, + numSteps: 3, + body: [LinkWatchScreen(step:2)], + footer: [continueBtn], + nextStep: ()=>{}, + previousStep: ()=>{} + ), + AccountCreatedScreen(navigationContract: navigationContract, kidAccount: true) + ]; + } +} \ No newline at end of file diff --git a/modules/auth/lib/src/device_sign_up/link_watch/account_created_kid_screen.dart b/modules/auth/lib/src/device_sign_up/link_watch/account_created_kid_screen.dart deleted file mode 100644 index ca63a267..00000000 --- a/modules/auth/lib/src/device_sign_up/link_watch/account_created_kid_screen.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:design_system/design_system.dart'; -import 'package:flutter/material.dart'; -import 'package:auth/src/device_sign_up/add_kid_screen.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class AccountCreatedKidScreen extends ConsumerWidget { - const AccountCreatedKidScreen({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final theme = ref.watch(themePortProvider); - - final model = "SaveWatch Plus 2"; - final id = "1106652524"; - final fullName = "Carlos Pérez Cruz"; - - return Scaffold( - backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), - body: Container( - margin: EdgeInsets.all(30), - child: Center( - child: Column( - spacing: 20, - children: [ - Spacer(flex: 2), - Icon( - Icons.check, - color: theme.getColorFor(ThemeCode.backgroundPrimary), - size: 50, - ), - Text( - "Cuenta creada", - style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold), - ), - Text.rich( - TextSpan( - text: "Has creado la cuenta para:\n", - children: [ - TextSpan( - text: fullName, - style: TextStyle(fontWeight: FontWeight.bold), - ), - ], - ), - ), - Text("Reloj: $model"), - Text("ID del reloj: $id"), - Text( - "Ya puedes darle su primera paga paa que empiece a disfrutarla en su reloj", - style: TextStyle(fontWeight: FontWeight.bold), - ), - Spacer(flex: 6), - Container( - padding: EdgeInsets.all(20), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topRight: Radius.circular(20), - topLeft: Radius.circular(20), - ), - ), - child: Column( - children: [ - Expanded( - child: FilledButton( - onPressed: () => { - Navigator.pushReplacement( - context, - MaterialPageRoute(builder: (_) => AddKidScreen()), - ), - }, - child: Text("Dale su primera paga"), - ), - ), - TextButton( - onPressed: () => {}, - child: Text("Añadir otro peque"), - ), - ], - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/modules/auth/lib/src/device_sign_up/link_watch/create_profile_screen.dart b/modules/auth/lib/src/device_sign_up/link_watch/create_profile_screen.dart index 23b2973c..a26e8ed2 100644 --- a/modules/auth/lib/src/device_sign_up/link_watch/create_profile_screen.dart +++ b/modules/auth/lib/src/device_sign_up/link_watch/create_profile_screen.dart @@ -1,304 +1,70 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_svg/flutter_svg.dart'; class CreateProfileScreen extends ConsumerWidget { const CreateProfileScreen({super.key}); - final int currentStep = 0; + final bool firstTime = false; @override Widget build(BuildContext context, WidgetRef ref) { final theme = ref.watch(themePortProvider); - return Scaffold( - backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), - body: Container( - margin: EdgeInsets.all(30), - child: Center( - child: Column( - spacing: 10, - children: [ - Stepper( - type: StepperType.horizontal, - currentStep: currentStep, - onStepCancel: () => currentStep == 0, - // ? null - // : - // setState(() { - // currentStep -= 1; - // }), - controlsBuilder: - (BuildContext context, ControlsDetails controls) { - return FilledButton( - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - theme.getColorFor(ThemeCode.buttonPrimary), - ), - ), - onPressed: controls.onStepContinue, - child: const Text('Continuar'), - ); - }, - steps: [ - Step( - state: currentStep > 0 - ? StepState.complete - : StepState.indexed, - isActive: currentStep >= 0, - stepStyle: currentStep >= 0 - ? StepStyle( - connectorThickness: 0, - color: Color(0xFF329e95), - indexStyle: TextStyle(color: Colors.transparent), - ) - : StepStyle( - connectorThickness: 0, - color: Colors.transparent, - boxShadow: BoxShadow(spreadRadius: 5), - indexStyle: TextStyle(color: Colors.transparent), - ), - title: Text(""), - content: Column( - spacing: 10, - children: [ - Text( - "Crea su perfil", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 30, - ), - ), - Text( - "Necesitamos estos datos para crear su cuenta y gestionar sus pagas y gastos", - ), - Text( - "Comienza con un peque; luego podrás agregar más", - style: TextStyle(fontWeight: FontWeight.bold), - ), - TextField( - decoration: InputDecoration( - labelText: "Nombre", - hintText: "Nombre", - border: OutlineInputBorder(), - ), - ), - TextField( - decoration: InputDecoration( - labelText: "Apellidos", - hintText: "Apellidos", - border: OutlineInputBorder(), - ), - ), - Row( - spacing: 10, - children: [ - Expanded( - child: TextField( - decoration: InputDecoration( - label: Text("Fecha de nacimiento"), - hintText: "DD", - border: OutlineInputBorder(), - ), - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - ), - ), - Expanded( - child: TextField( - decoration: InputDecoration( - hintText: "MM", - border: OutlineInputBorder(), - ), - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - ), - ), - Expanded( - child: TextField( - decoration: InputDecoration( - hintText: "AAAA", - border: OutlineInputBorder(), - ), - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - ), - ), - ], - ), - TextField( - decoration: InputDecoration( - labelText: "Dirección completa", - hintText: "Nombre de la calle", - border: OutlineInputBorder(), - ), - ), - TextButton( - onPressed: () => {}, - child: Text( - "Cambiar dirección", - style: TextStyle(fontWeight: FontWeight.bold), - ), - ), - ], - ), - ), - Step( - state: currentStep > 1 - ? StepState.complete - : StepState.indexed, - isActive: currentStep >= 1, - stepStyle: currentStep >= 1 - ? StepStyle( - connectorThickness: 0, - color: Color(0xFF329e95), - indexStyle: TextStyle(color: Colors.transparent), - ) - : StepStyle( - connectorThickness: 0, - color: Colors.transparent, - boxShadow: BoxShadow(spreadRadius: 5), - indexStyle: TextStyle(color: Colors.transparent), - ), - title: Text(""), - content: Column( - spacing: 10, - children: [ - Text( - "Vincula su correa y su reloj", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 30, - ), - ), - SvgPicture.asset("assets/images/ui/formulario.svg"), - Row( - spacing: 10, - children: [ - Text("1"), - Column( - children: [ - Text("Escanea la correa"), - Text("El peque podrá realizar pagos"), - ], - ), - ], - ), - Row( - spacing: 10, - children: [ - Text("2"), - Column( - children: [ - Text("Escanea el reloj"), - Text("Visualizarás los gastos que se hagan"), - ], - ), - ], - ), - ], - ), - ), - Step( - state: currentStep > 2 - ? StepState.complete - : StepState.indexed, - isActive: currentStep >= 2, - stepStyle: currentStep >= 2 - ? StepStyle( - connectorThickness: 0, - color: Color(0xFF329e95), - indexStyle: TextStyle(color: Colors.transparent), - ) - : StepStyle( - connectorThickness: 0, - color: Colors.transparent, - boxShadow: BoxShadow(spreadRadius: 5), - indexStyle: TextStyle(color: Colors.transparent), - ), - title: Text(""), - content: Column( - spacing: 10, - children: [ - Text( - "¡Dale su primera paga!", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 30, - ), - ), - Text( - "Enséñales a gestionar su dinero recargando su reloj", - ), - TextField( - decoration: InputDecoration( - labelText: "Cantidad de dinero de la paga", - hintText: "0€", - border: OutlineInputBorder(), - ), - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - ), - Text("Cantidad mínima: 10€"), - Text( - "Por seguridad sólo se puede disponer de un máximo de 150€ por wallet", - ), - Text("Método de ingreso"), - Row( - spacing: 20, - children: [ - OutlinedButton( - onPressed: () => {}, - child: SvgPicture.asset( - "assets/images/ui/visa.svg", - ), - ), - OutlinedButton( - onPressed: () => {}, - child: SvgPicture.asset( - "assets/images/ui/paypal.svg", - ), - ), - OutlinedButton( - onPressed: () => {}, - child: Row( - children: [ - SvgPicture.asset( - "assets/images/ui/banco.svg", - ), - Text("Transferencia"), - ], - ), - ), - ], - ), - Row( - children: [ - Icon(Icons.lock_outline), - Text( - "EL pago en esta app es seguro y cumple la normativa europea. Sólo se usará el dinero que decidas para las huchas de tus hijos.", - ), - ], - ), - ], - ), - ), - ], + return Column( + spacing: 24, + children: [ + Text( + "Comienza con un peque; luego podrás agregar más", + style: TextStyle(fontWeight: FontWeight.bold), + ), + CustomTextField( + label: "Nombre", + hint: "Nombre", + ), + CustomTextField( + label: "Apellidos", + hint: "Apellidos", + ), + Row( + spacing: 10, + children: [ + Expanded( + child: CustomTextField( + numeric: true, + label: "Fecha de nacimiento", + hint: "DD", + length: 2, ), - ], + ), + Expanded( + child: CustomTextField( + numeric: true, + hint: "MM", + length: 2, + ), + ), + Expanded( + child: CustomTextField( + numeric: true, + hint: "AAAA", + length: 4, + ), + ), + ], + ), + CustomTextField( + label: "Dirección completa", + hint: "Nombre de la calle", + ), + TextButton( + onPressed: () => {}, + child: Text( + "Cambiar dirección", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: theme.getColorFor(ThemeCode.textPrimary)), ), ), - ), + ], ); } } diff --git a/modules/auth/lib/src/device_sign_up/link_watch/link_watch_previous_screen.dart b/modules/auth/lib/src/device_sign_up/link_watch/link_watch_previous_screen.dart new file mode 100644 index 00000000..77cc1f12 --- /dev/null +++ b/modules/auth/lib/src/device_sign_up/link_watch/link_watch_previous_screen.dart @@ -0,0 +1,58 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; + +class LinkWatchPreviousScreen extends ConsumerWidget{ + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.watch(themePortProvider); + + return Column( + spacing: 10, + children: [ + SvgPicture.asset("assets/images/ui/formulario.svg"), + Row( + spacing: 16, + children: [ + Container( + decoration: ShapeDecoration( + shape: CircleBorder(side: BorderSide(color: theme.getColorFor(ThemeCode.backgroundSecondary))), + color: theme.getColorFor(ThemeCode.backgroundSecondary) + ), + width: 48, + height: 48, + child: Center(child: Text("1", style: TextStyle(fontSize: 24))), + ), + Column( + children: [ + Text("Escanea la correa", textAlign: TextAlign.left, style: TextStyle(fontSize: 24)), + Text("El peque podrá realizar pagos"), + ], + ), + ], + ), + Row( + spacing: 16, + children: [ + Container( + decoration: ShapeDecoration( + shape: CircleBorder(side: BorderSide(color: theme.getColorFor(ThemeCode.backgroundSecondary))), + color: theme.getColorFor(ThemeCode.backgroundSecondary) + ), + width: 48, + height: 48, + child: Center(child: Text("2", style: TextStyle(fontSize: 24))), + ), + Column( + children: [ + Text("Escanea el reloj", textAlign: TextAlign.left, style: TextStyle(fontSize: 24)), + Text("Visualizarás los gastos que se hagan"), + ] + ) + ], + ), + ], + ); + } +} \ No newline at end of file diff --git a/modules/auth/lib/src/device_sign_up/link_watch/link_watch_screen.dart b/modules/auth/lib/src/device_sign_up/link_watch/link_watch_screen.dart new file mode 100644 index 00000000..c6a8a9d6 --- /dev/null +++ b/modules/auth/lib/src/device_sign_up/link_watch/link_watch_screen.dart @@ -0,0 +1,60 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class LinkWatchScreen extends ConsumerStatefulWidget{ + final int step; + + const LinkWatchScreen({super.key, required this.step}); + + @override + ConsumerState createState() => LinkWatchScreenState(); +} + +class LinkWatchScreenState extends ConsumerState{ + + @override + Widget build(BuildContext context) { + final theme = ref.watch(themePortProvider); + + return Column( + spacing: 32, + children: [ + Row( + children: [ + Text("Escanea la correa"), + Spacer(), + Text("Escanea el reloj") + ], + ), + Container( + padding: EdgeInsets.all(40), + decoration: BoxDecoration( + border: Border.all(color: theme.getColorFor(ThemeCode.textPrimary)), + borderRadius: BorderRadius.all(Radius.circular(16)) + ), + child: SvgPicture.asset("assets/images/ui/qr.svg") + ), + if (widget.step == 2)Text("O inserta el código del reloj"), + if (widget.step == 2)Row( + spacing: 16, + children: [ + Expanded(child: CustomTextField( + hint: "XXXXXXXXXX", + )), + Expanded(child: FilledButton( + style: ButtonStyle(backgroundColor: WidgetStatePropertyAll(theme.getColorFor(ThemeCode.buttonSecondary))), + onPressed: ()=>{}, + child: Expanded(child: Center(child: Text( + "Continuar con código", + style: TextStyle(fontSize: 16, letterSpacing: 0)))) + )) + ], + ), + Text("Si no consigues vincular su correa o reloj"), + TextButton(onPressed: ()=>{}, child: Text("Contáctanos")) + ], + ); + } +} \ No newline at end of file diff --git a/modules/auth/lib/src/login/presentation/link_phone_screen.dart b/modules/auth/lib/src/login/presentation/link_phone_screen.dart index e8fe2563..9c28b293 100644 --- a/modules/auth/lib/src/login/presentation/link_phone_screen.dart +++ b/modules/auth/lib/src/login/presentation/link_phone_screen.dart @@ -1,3 +1,4 @@ +import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:navigation/navigation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -9,17 +10,18 @@ class LinkPhoneScreen extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - TextEditingController phoneController = TextEditingController(); + // TextEditingController phoneController = TextEditingController(); // String? phone; - return Scaffold( - body: Container( - margin: EdgeInsets.all(30), + return Scaffold(body: SafeArea( + child: Container( + margin: EdgeInsets.symmetric(horizontal: 24), child: Expanded( child: Center( child: Column( - spacing: 10, + spacing: 48, children: [ + Spacer(flex: 8), Text( "¡Nos alegra mucho tenerte por aquí!", style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold), @@ -43,18 +45,11 @@ class LinkPhoneScreen extends ConsumerWidget { }), ), Expanded( - child: TextField( - onSubmitted: (String value) { - // phone = value; - }, - controller: phoneController, - decoration: InputDecoration( - labelText: "Teléfono móvil", - hintText: "Teléfono", - border: OutlineInputBorder(), - ), - keyboardType: TextInputType.number, - ), + child: CustomTextField( + label: "Teléfono móvil", + hint: "Teléfono", + numeric: true + ) ), ], ), @@ -65,11 +60,12 @@ class LinkPhoneScreen extends ConsumerWidget { child: Text("Siguiente"), ), ), + Spacer(flex: 10) ], ), ), ), ), - ); + )); } } diff --git a/modules/auth/lib/src/login/presentation/login_screen.dart b/modules/auth/lib/src/login/presentation/login_screen.dart index fd7899af..e374bd39 100644 --- a/modules/auth/lib/src/login/presentation/login_screen.dart +++ b/modules/auth/lib/src/login/presentation/login_screen.dart @@ -1,5 +1,6 @@ import 'package:auth/src/login/presentation/loading_google_screen.dart'; import 'package:auth/src/sign_up/signup_screen.dart'; +import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:navigation/navigation.dart'; @@ -25,34 +26,14 @@ class LoginScreen extends ConsumerWidget { "¡Te damos la bienvenida!", style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold), ), - TextField( - decoration: InputDecoration( - hintText: "Nombre de usuario", - labelText: "Nombre de usuario", - border: OutlineInputBorder(), - ), + CustomTextField( + hint: "Nombre de usuario", + label: "Nombre de usuario", ), - TextField( - obscureText: passwordVisible, - enableSuggestions: false, - autocorrect: false, - decoration: InputDecoration( - labelText: "Contraseña", - hintText: "********", - border: OutlineInputBorder(), - suffixIcon: IconButton( - icon: Icon( - passwordVisible - ? Icons.visibility - : Icons.visibility_off, - ), - onPressed: () { - // setState(() { - // passwordVisible = !passwordVisible; - // }); - }, - ), - ), + CustomTextField( + showPassword: passwordVisible, + label: "Contraseña", + hint: "********" ), TextButton( onPressed: () => diff --git a/modules/auth/lib/src/login/presentation/phone_code_screen.dart b/modules/auth/lib/src/login/presentation/phone_code_screen.dart index 51b220d8..772703aa 100644 --- a/modules/auth/lib/src/login/presentation/phone_code_screen.dart +++ b/modules/auth/lib/src/login/presentation/phone_code_screen.dart @@ -1,15 +1,12 @@ import 'package:flutter/material.dart'; import 'package:navigation/navigation.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -// import 'package:sf_app_platform/payments/view/screens/core/dashboard_screen.dart'; -// import 'package:sf_app_platform/payments/view/screens/login_screen.dart'; class PhoneCodeScreen extends ConsumerWidget { // final String phone; final NavigationContract navigationContract; PhoneCodeScreen({super.key, required this.navigationContract}); - // const PhoneCodeScreen({super.key, required this.phone}); // class PhoneCodeScreenState extends State { final focusNodes = List.generate(6, (int i) { diff --git a/modules/auth/lib/src/onboarding/presentation/welcome_screen.dart b/modules/auth/lib/src/onboarding/presentation/welcome_screen.dart index 9db4fad3..0ae92371 100644 --- a/modules/auth/lib/src/onboarding/presentation/welcome_screen.dart +++ b/modules/auth/lib/src/onboarding/presentation/welcome_screen.dart @@ -1,75 +1,135 @@ +import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:navigation/navigation.dart'; -class WelcomeScreen extends ConsumerWidget { +class WelcomeScreen extends ConsumerStatefulWidget { final NavigationContract navigationContract; const WelcomeScreen({super.key, required this.navigationContract}); @override - Widget build(BuildContext context, WidgetRef ref) { + ConsumerState createState() => WelcomeScreenState(navigationContract: navigationContract); + +} + +class WelcomeScreenState extends ConsumerState{ + late int currentStep; + final NavigationContract navigationContract; + + WelcomeScreenState({required this.navigationContract}); + + @override + void initState() { + super.initState(); + currentStep = 0; + } + + @override + Widget build(BuildContext context) { + final theme = ref.watch(themePortProvider); + return Scaffold( - body: Center( - child: Column( - children: [ - Spacer(), - Expanded( - child: CarouselView( - scrollDirection: Axis.horizontal, - itemExtent: double.infinity, - itemSnapping: true, - shrinkExtent: 400, - children: generateSteps(), + body: Container( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Center( + child: Column( + spacing: 48, + children: [ + Spacer(), + generateSteps()[currentStep], + Column( + spacing: 24, + children: [ + StepIndicator(max: 3, current: currentStep+1, color: theme.getColorFor(ThemeCode.buttonSecondary)), + generateButtons(theme, 3, currentStep+1) + ] ), - ), - FilledButton( - onPressed: () => navigationContract.goTo('/link_phone'), - child: const Text('Continuar'), - ), - Spacer(), - ], - ), - ), + Spacer() + ] + ) + ) + ) ); } - void jumpToNext(BuildContext context) { - // Navigator.pushReplacement( - // context, - // MaterialPageRoute(builder: (_) => LinkPhoneScreen()), - // ); - return; + Widget generateButtons(ThemePort theme, int max, int step){ + if (step==max) { + return FilledButton( + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll(theme.getColorFor(ThemeCode.buttonPrimary)) + ), + onPressed: () => navigationContract.goTo('/link_phone'), + child: Expanded(child: Center(child: Text('Continuar'))) + ); + } else { + return Column( + spacing: 16, + children: [ + FilledButton( + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll(theme.getColorFor(ThemeCode.buttonSecondary)) + ), + onPressed: ()=>setState(() { + currentStep++; + }), + child: Expanded(child: Center( child: Text("Siguiente"))) + ), + TextButton( + onPressed: ()=>navigationContract.goTo('/link_phone'), + child: Text("Omitir") + ) + ], + ); + } } List generateSteps() { return [ Column( - spacing: 30, + spacing: 48, children: [ SvgPicture.asset("assets/images/ui/bienvenida_paso1.svg"), - Text( - "Aprende a gestionar su dinero", - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), - ), - Text("Tu peque crea hábitos y se divierte mientras lo hace"), - ], + Column( + spacing: 16, + children: [ + Text( + "Aprende a gestionar su dinero", + textAlign: TextAlign.center, + style: TextStyle(fontWeight: FontWeight.w500, fontSize: 30) + ), + Text( + "Tu peque crea hábitos y se divierte mientras lo hace", + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18) + ) + ] + ) + ] ), Column( children: [ SvgPicture.asset("assets/images/ui/bienvenida_paso2.svg"), - Text("Tranquilidad en cada pago que hacen"), - Text("Supervisa gastos, fija límites y acompáñalos en cada paso"), + Text("Tranquilidad en cada pago que hacen", + textAlign: TextAlign.center, + style: TextStyle(fontWeight: FontWeight.w500, fontSize: 30)), + Text("Supervisa gastos, fija límites y acompáñalos en cada paso", + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18)), ], ), Column( children: [ SvgPicture.asset("assets/images/ui/bienvenida_paso3.svg"), - Text("Pagos fáciles y seguros en sus manos"), - Text("Podrá pagar desde su reloj.\n Sin móvil ni efectivo"), + Text("Pagos fáciles y seguros en sus manos", + textAlign: TextAlign.center, + style: TextStyle(fontWeight: FontWeight.w500, fontSize: 30)), + Text("Podrá pagar desde su reloj.\n Sin móvil ni efectivo", + textAlign: TextAlign.center, + style: TextStyle(fontSize: 18)), ], ), ]; } -} +} \ No newline at end of file diff --git a/modules/auth/lib/src/recover_password/presentation/email_sent_screen.dart b/modules/auth/lib/src/recover_password/presentation/email_sent_screen.dart index 58694e37..3e21e2c1 100644 --- a/modules/auth/lib/src/recover_password/presentation/email_sent_screen.dart +++ b/modules/auth/lib/src/recover_password/presentation/email_sent_screen.dart @@ -1,3 +1,4 @@ +import 'package:auth/src/recover_password/presentation/new_password_screen.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -8,7 +9,7 @@ class EmailSentScreen extends ConsumerWidget { const EmailSentScreen({super.key, required this.email}); @override - Widget build(BuildContext contex, WidgetRef ref) { + Widget build(BuildContext context, WidgetRef ref) { final theme = ref.watch(themePortProvider); return Scaffold( diff --git a/modules/auth/lib/src/recover_password/presentation/new_password_screen.dart b/modules/auth/lib/src/recover_password/presentation/new_password_screen.dart index 35abf19d..ed2cb62d 100644 --- a/modules/auth/lib/src/recover_password/presentation/new_password_screen.dart +++ b/modules/auth/lib/src/recover_password/presentation/new_password_screen.dart @@ -1,6 +1,8 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:get_it/get_it.dart'; +import 'package:navigation/navigation.dart'; class NewPasswordScreen extends ConsumerStatefulWidget { const NewPasswordScreen({super.key}); @@ -24,7 +26,7 @@ class NewPasswordScreenState extends ConsumerState { void initState() { passwordVisible = false; equalPasswords = false; - String password = ""; + password = ""; securityChecks = { "min": false, "capital": false, @@ -36,6 +38,7 @@ class NewPasswordScreenState extends ConsumerState { @override Widget build(BuildContext context) { + final NavigationContract navigationContract = GetIt.I(); final theme = ref.watch(themePortProvider); return Scaffold( @@ -50,25 +53,10 @@ class NewPasswordScreenState extends ConsumerState { "Recuperar contraseña", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30), ), - TextField( - obscureText: passwordVisible, - enableSuggestions: false, - autocorrect: false, - decoration: InputDecoration( - labelText: "Nueva contraseña", - hintText: "********", - border: OutlineInputBorder(), - suffixIcon: IconButton( - icon: Icon( - passwordVisible ? Icons.visibility : Icons.visibility_off, - ), - onPressed: () { - setState(() { - passwordVisible = !passwordVisible; - }); - }, - ), - ), + CustomTextField( + showPassword: passwordVisible, + label: "Nueva contraseña", + hint: "********", onChanged: (value) => { setState(() { password = value; @@ -76,25 +64,10 @@ class NewPasswordScreenState extends ConsumerState { }), }, ), - TextField( - obscureText: passwordVisible, - enableSuggestions: false, - autocorrect: false, - decoration: InputDecoration( - labelText: "Repetir contraseña", - hintText: "********", - border: OutlineInputBorder(), - suffixIcon: IconButton( - icon: Icon( - passwordVisible ? Icons.visibility : Icons.visibility_off, - ), - onPressed: () { - setState(() { - passwordVisible = !passwordVisible; - }); - }, - ), - ), + CustomTextField( + showPassword: passwordVisible, + label: "Repetir contraseña", + hint: "********", onChanged: (value) => { setState(() { equalPasswords = password == value; @@ -159,7 +132,7 @@ class NewPasswordScreenState extends ConsumerState { ), Spacer(flex: 1), FilledButton( - onPressed: () => {}, + onPressed: ()=>{navigationContract.pushTo('/main/home')}, child: Container( width: double.infinity, padding: EdgeInsets.all(20), diff --git a/modules/auth/lib/src/recover_password/presentation/restore_password_screen.dart b/modules/auth/lib/src/recover_password/presentation/restore_password_screen.dart index 7dc81a88..56972a94 100644 --- a/modules/auth/lib/src/recover_password/presentation/restore_password_screen.dart +++ b/modules/auth/lib/src/recover_password/presentation/restore_password_screen.dart @@ -28,12 +28,9 @@ class RestorePasswordScreen extends ConsumerWidget { Text( "Introduce tu email para enviarte un enlace de recuperación", ), - TextField( - decoration: InputDecoration( - labelText: "Correo electrónico", - hintText: "Correo electrónico", - border: OutlineInputBorder(), - ), + CustomTextField( + label: "Correo electrónico", + hint: "Correo electrónico", ), Row( spacing: 20, diff --git a/modules/auth/lib/src/sign_up/account_created_screen.dart b/modules/auth/lib/src/sign_up/account_created_screen.dart index 263202ff..b9b8f8fd 100644 --- a/modules/auth/lib/src/sign_up/account_created_screen.dart +++ b/modules/auth/lib/src/sign_up/account_created_screen.dart @@ -1,10 +1,17 @@ -import 'package:auth/src/device_sign_up/add_kid_screen.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:navigation/navigation.dart'; class AccountCreatedScreen extends ConsumerWidget { - const AccountCreatedScreen({super.key}); + final bool kidAccount; + final NavigationContract navigationContract; + + const AccountCreatedScreen({ + super.key, + required this.kidAccount, + required this.navigationContract, + }); @override Widget build(BuildContext context, WidgetRef ref) { @@ -12,6 +19,8 @@ class AccountCreatedScreen extends ConsumerWidget { final email = "usuario@example.com"; final fullName = "Carlos Pérez Cruz"; + final model = "SaveWatch Plus 2"; + final id = "1106652524"; return Scaffold( backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary), @@ -24,7 +33,7 @@ class AccountCreatedScreen extends ConsumerWidget { Spacer(flex: 10), Icon( Icons.check, - color: theme.getColorFor(ThemeCode.backgroundPrimary), + color: theme.getColorFor(ThemeCode.buttonPrimary), size: 50, ), Text( @@ -32,6 +41,7 @@ class AccountCreatedScreen extends ConsumerWidget { style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold), ), Text.rich( + textAlign: TextAlign.center, TextSpan( text: "Has creado la cuenta para:\n", children: [ @@ -42,7 +52,8 @@ class AccountCreatedScreen extends ConsumerWidget { ], ), ), - Text.rich( + if (!kidAccount) Text.rich( + textAlign: TextAlign.center, TextSpan( text: "Hemos enviado un email de verificación a:\n", children: [ @@ -53,15 +64,18 @@ class AccountCreatedScreen extends ConsumerWidget { ], ), ), + if (kidAccount) Text("Reloj: $model\nID del reloj: $id"), Text( "Crea la cuenta de tu peque e ingresa su \nprimera paga para utilizarla con su reloj", + textAlign: TextAlign.center, ), FilledButton( onPressed: () => { - Navigator.pushReplacement( - context, - MaterialPageRoute(builder: (_) => AddKidScreen()), - ), + if (kidAccount){ + navigationContract.goTo('/main/home') + } else { + navigationContract.pushTo('/device_signup') + } }, child: Text("Continuar"), ), diff --git a/modules/auth/lib/src/sign_up/signup_address_screen.dart b/modules/auth/lib/src/sign_up/signup_address_screen.dart index d9cbc557..e5e3ac68 100644 --- a/modules/auth/lib/src/sign_up/signup_address_screen.dart +++ b/modules/auth/lib/src/sign_up/signup_address_screen.dart @@ -1,3 +1,4 @@ + import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; class SignupAddressScreen extends StatelessWidget { @@ -6,28 +7,40 @@ class SignupAddressScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Column( - spacing: 30, + spacing: 24, children: [ - Text("Domicilio"), - Text( - "Tu dirección", - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30), + Row( + spacing: 8, + children: [ + Expanded(child: CustomTextField( + label: "Fecha de nacimiento", + hint: "DD", + length: 2, + numeric: true, + )), + Expanded(child: CustomTextField( + hint: "MM", + length: 2, + numeric: true, + )), + Expanded(child: CustomTextField( + hint: "AAAA", + length: 4, + numeric: true, + )), + ] ), - Text( - "Tu dirección nos ayuda a verificar y mantener la seguridad de tu cuenta", - ), - TextField( - decoration: InputDecoration( - hintText: "Dirección completa", - border: OutlineInputBorder(), - ), - ), - TextField( - decoration: InputDecoration( - hintText: "Ciudad", - border: OutlineInputBorder(), - ), + DropdownMenu( + width: double.infinity, + label: Text("¿Qué familiar eres?"), + dropdownMenuEntries: [ + DropdownMenuEntry(label: "Padre", value: "Padre"), + DropdownMenuEntry(label: "Madre", value: "Madre"), + DropdownMenuEntry(label: "Tutor", value: "Tutor"), + ], ), + CustomTextField(label: "Dirección completa", hint: "Calle Gran Vía 30 6º, 28013"), + CustomTextField(label: "Ciudad", hint: "Ciudad"), DropdownMenu( dropdownMenuEntries: List.generate(3, (int index) { return DropdownMenuEntry(value: "España", label: "España"); @@ -35,12 +48,7 @@ class SignupAddressScreen extends StatelessWidget { hintText: "País", width: double.infinity, ), - TextField( - decoration: InputDecoration( - hintText: "Nacionalidad", - border: OutlineInputBorder(), - ), - ), + CustomTextField(label: "Nacionalidad", hint: "España"), ], ); } diff --git a/modules/auth/lib/src/sign_up/signup_personal_screen.dart b/modules/auth/lib/src/sign_up/signup_personal_screen.dart index 1e3aecb0..a8046eb4 100644 --- a/modules/auth/lib/src/sign_up/signup_personal_screen.dart +++ b/modules/auth/lib/src/sign_up/signup_personal_screen.dart @@ -1,5 +1,5 @@ +import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; class SignupPersonalScreen extends StatelessWidget{ const SignupPersonalScreen({super.key}); @@ -7,41 +7,25 @@ class SignupPersonalScreen extends StatelessWidget{ @override Widget build(BuildContext context) { return Column( - spacing: 30, + spacing: 24, children: [ - Text("Datos personales"), - Text("Identifícate", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30)), - Text("Nos aseguraremos de que la cuenta está a nombre del adulto responsable"), - TextField(decoration: InputDecoration(labelText: "Nombre", hintText: "Nombre", border: OutlineInputBorder())), - TextField(decoration: InputDecoration(labelText: "Apellidos", hintText: "Apellidos", border: OutlineInputBorder())), - Row( - children: [ - Expanded( child: TextField( - decoration: InputDecoration(label: Text("Fecha de nacimiento"), hintText: "DD", border: OutlineInputBorder()), - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - )), - Expanded( child: TextField( - decoration: InputDecoration(hintText: "MM", border: OutlineInputBorder()), - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - )), - Expanded( child: TextField( - decoration: InputDecoration(hintText: "AAAA", border: OutlineInputBorder()), - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - )), - ], - ), - DropdownMenu( - width: double.infinity, - label: Text("¿Qué familiar eres?"), - dropdownMenuEntries: [ - DropdownMenuEntry(label: "Padre", value: "Padre"), - DropdownMenuEntry(label: "Madre", value: "Madre"), - DropdownMenuEntry(label: "Tutor", value: "Tutor"), - ], - ), + CustomTextField(label: "Nombre", hint: "Nombre"), + CustomTextField(label: "Apellidos", hint: "Apellidos"), + CustomTextField(label: "DNI", hint: "DNI"), + Row(children: [ + DropdownMenu( + initialSelection: "es", + dropdownMenuEntries: List.generate(3, (int index){ + return DropdownMenuEntry(labelWidget: Icon(Icons.outlined_flag), label: "es", value: "es"); + }) + ), + Expanded(child: CustomTextField( + label: "Teléfono móvil", + hint: "123456789", + numeric: true + )) + ]), + CustomTextField(label: "Correo electrónico", hint: "Correo electrónico"), ], ); } diff --git a/modules/auth/lib/src/sign_up/signup_screen.dart b/modules/auth/lib/src/sign_up/signup_screen.dart index a6432de6..361389c2 100644 --- a/modules/auth/lib/src/sign_up/signup_screen.dart +++ b/modules/auth/lib/src/sign_up/signup_screen.dart @@ -1,136 +1,71 @@ -import 'package:auth/src/sign_up/account_created_screen.dart'; -import 'package:design_system/design_system.dart'; +import 'package:auth/src/widgets/layouts/form_step_layout.dart'; import 'package:flutter/material.dart'; import 'package:auth/src/sign_up/signup_address_screen.dart'; import 'package:auth/src/sign_up/signup_personal_screen.dart'; import 'package:auth/src/sign_up/signup_user_screen.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:get_it/get_it.dart'; +import 'package:navigation/navigation.dart'; -class SignupScreen extends ConsumerWidget { +class SignupScreen extends ConsumerStatefulWidget { SignupScreen({super.key}); - int currentStep = 0; + ConsumerState createState() => SignupScreenState(); +} + +class SignupScreenState extends ConsumerState { + late int currentStep; @override - Widget build(BuildContext context, WidgetRef ref) { - final theme = ref.watch(themePortProvider); - - return MaterialApp( - home: Scaffold( - body: Center( - child: Container( - color: theme.getColorFor(ThemeCode.backgroundPrimary), - padding: const EdgeInsets.all(20), - child: SizedBox( - child: Stepper( - controlsBuilder: - (BuildContext context, ControlsDetails controls) { - return Row( - children: [ - Expanded( - child: OutlinedButton( - onPressed: controls.onStepCancel, - child: const Text('Atrás'), - ), - ), - Expanded( - child: FilledButton( - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - theme.getColorFor(ThemeCode.buttonSecondary), - ), - ), - onPressed: controls.onStepContinue, - child: const Text('Siguiente'), - ), - ), - ], - ); - }, - type: StepperType.horizontal, - currentStep: currentStep, - onStepCancel: () => currentStep == 0, - // ? null - // : setState(() { - // currentStep -= 1; - // }), - onStepContinue: () { - bool isLastStep = (currentStep == getSteps().length - 1); - if (isLastStep) { - Navigator.pushReplacement( - context, - MaterialPageRoute(builder: (_) => AccountCreatedScreen()), - ); - } else { - // setState(() { - // currentStep += 1; - // }); - } - }, - steps: getSteps(), - ), - ), - ), - ), - ), - ); + void initState() { + super.initState(); + currentStep = 0; } - List getSteps() { - return [ - Step( - state: currentStep > 0 ? StepState.complete : StepState.indexed, - isActive: currentStep >= 0, - stepStyle: currentStep >= 0 - ? StepStyle( - connectorThickness: 0, - color: Color(0xFF329e95), - indexStyle: TextStyle(color: Colors.transparent), - ) - : StepStyle( - connectorThickness: 0, - color: Colors.transparent, - boxShadow: BoxShadow(spreadRadius: 5), - indexStyle: TextStyle(color: Colors.transparent), - ), - title: const Text(""), - content: SignupPersonalScreen(), + @override + Widget build(BuildContext context) { + return getSteps()[currentStep]; + } + + List getSteps() { + return [ + FormStepLayout( + title: "Crea tu usuario", + subtitle: "Nos aseguraremos de que la cuenta esté a nombre del adulto responsable", + supertitle: "Datos personales", + currentStep: 1, + numSteps: 3, + body: [SignupPersonalScreen()], + nextStep: ()=>{setState(() { + currentStep++; + })}, + previousStep: ()=>{}, ), - Step( - state: currentStep > 1 ? StepState.complete : StepState.indexed, - isActive: currentStep >= 1, - stepStyle: currentStep >= 1 - ? StepStyle( - connectorThickness: 0, - color: Color(0xFF329e95), - indexStyle: TextStyle(color: Colors.transparent), - ) - : StepStyle( - connectorThickness: 0, - color: Colors.white, - boxShadow: BoxShadow(spreadRadius: 1), - indexStyle: TextStyle(color: Colors.transparent), - ), - title: const Text(""), - content: SignupAddressScreen(), + FormStepLayout( + title: "Tu dirección", + subtitle: "Tu dirección nos ayudará a verificar y mantener la seguridad de tu cuenta", + supertitle: "Domicilio", + currentStep: 2, + numSteps: 3, + body: [SignupAddressScreen()], + nextStep: ()=>{setState(() { + currentStep++; + })}, + previousStep: ()=>{setState(() { + currentStep--; + })}, ), - Step( - state: currentStep > 2 ? StepState.complete : StepState.indexed, - isActive: currentStep >= 2, - stepStyle: currentStep >= 2 - ? StepStyle( - connectorThickness: 0, - color: Color(0xFF329e95), - indexStyle: TextStyle(color: Colors.transparent), - ) - : StepStyle( - connectorThickness: 0, - color: Colors.white, - boxShadow: BoxShadow(spreadRadius: 1), - indexStyle: TextStyle(color: Colors.transparent), - ), - title: const Text(""), - content: SignupUserScreen(), + FormStepLayout( + title: "Identifícate", + subtitle: "Con tu email y tu número podremos mantenerte siempre informado", + supertitle: "Usuario y contacto", + currentStep: 3, + numSteps: 3, + body: [SignupUserScreen()], + nextStep: ()=>{GetIt.I().pushTo('/device_signup')}, + previousStep: ()=>{setState(() { + currentStep--; + })}, ), ]; } diff --git a/modules/auth/lib/src/sign_up/signup_user_screen.dart b/modules/auth/lib/src/sign_up/signup_user_screen.dart index 1b9da81f..b152964a 100644 --- a/modules/auth/lib/src/sign_up/signup_user_screen.dart +++ b/modules/auth/lib/src/sign_up/signup_user_screen.dart @@ -1,3 +1,4 @@ +import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; class SignupUserScreen extends StatefulWidget{ @@ -13,62 +14,17 @@ class SignupUserScreenState extends State{ @override Widget build(BuildContext context) { return Column( - spacing: 30, + spacing: 24, children: [ - Text("Usuario y contacto"), - Text("Crea tu usuario", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30)), - Text("Con tu email y tu número podremos mantenerte siempre informado"), - TextField(decoration: InputDecoration(labelText: "Correo electrónico", hintText: "Correo electrónico", border: OutlineInputBorder())), - Row(children: [ - DropdownMenu( - initialSelection: "es", - dropdownMenuEntries: List.generate(3, (int index){ - return DropdownMenuEntry(labelWidget: Icon(Icons.outlined_flag), label: "es", value: "es"); - }) - ), - Expanded(child: TextField( - decoration: InputDecoration(labelText: "Teléfono móvil", hintText: "Teléfono", border: OutlineInputBorder()), - keyboardType: TextInputType.number) - ) - ]), - TextField( - obscureText: passwordVisible, - enableSuggestions: false, - autocorrect: false, - decoration: InputDecoration( - labelText: "Contraseña", - hintText: "********", - border: OutlineInputBorder(), - suffixIcon: IconButton( - icon: Icon(passwordVisible - ? Icons.visibility - : Icons.visibility_off), - onPressed: () { - setState(() { - passwordVisible = !passwordVisible; - }); - }, - ), - ) + CustomTextField( + showPassword: passwordVisible, + label: "Contraseña", + hint: "********" ), - TextField(obscureText: passwordVisible, - enableSuggestions: false, - autocorrect: false, - decoration: InputDecoration( - labelText: "Repetir contraseña", - hintText: "*******", - border: OutlineInputBorder(), - suffixIcon: IconButton( - icon: Icon(passwordVisible - ? Icons.visibility - : Icons.visibility_off), - onPressed: () { - setState(() { - passwordVisible = !passwordVisible; - }); - }, - ), - ) + CustomTextField( + showPassword: passwordVisible, + label: "Repetir contraseña", + hint: "*******" ), ], ); diff --git a/modules/auth/lib/src/widgets/layouts/form_step_layout.dart b/modules/auth/lib/src/widgets/layouts/form_step_layout.dart new file mode 100644 index 00000000..90ac4668 --- /dev/null +++ b/modules/auth/lib/src/widgets/layouts/form_step_layout.dart @@ -0,0 +1,91 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class FormStepLayout extends ConsumerWidget{ + final int currentStep; + final int numSteps; + final String? supertitle; + final String title; + final String? subtitle; + final List body; + final List? footer; + final VoidCallback nextStep; + final VoidCallback previousStep; + + const FormStepLayout({ + super.key, + required this.title, + this.subtitle, + this.supertitle, + required this.currentStep, + required this.numSteps, + required this.body, + this.footer, + required this.nextStep, + required this.previousStep + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.watch(themePortProvider); + + return Scaffold( + body: SafeArea(child: SingleChildScrollView(child: Container( + color: theme.getColorFor(ThemeCode.backgroundPrimary), + padding: EdgeInsets.only(top: 40, left: 24, right: 24), + child: Column( + spacing: 32, + children: [ + Column( + spacing: 24, + children: [ + StepIndicator( + max: numSteps, + current: currentStep, + color: theme.getColorFor(ThemeCode.buttonPrimary) + ), + if (supertitle!=null) Text(supertitle!, textAlign: TextAlign.center, style: TextStyle(fontSize: 18)), + Text(title, textAlign: TextAlign.center, style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold)), + if (subtitle!=null) Text(subtitle!, textAlign: TextAlign.center, style: TextStyle(fontSize: 18)), + ] + ), + Column( + spacing: 40, + children: [ + ...body, + if (footer==null) navigationButtons(currentStep, numSteps, nextStep, previousStep, theme), + ...(footer?? []) + ] + ) + ] + ) + ))) + ); + } + + Widget navigationButtons(int currentStep, int numSteps, VoidCallback nextStep, VoidCallback previousStep, ThemePort theme) { + if (currentStep == numSteps){ + return FilledButton( + onPressed: nextStep, + style: ButtonStyle(backgroundColor: WidgetStatePropertyAll(theme.getColorFor(ThemeCode.buttonPrimary))), + child: Expanded(child: Center(child: Text("Continuar"))) + ); + } else { + return Row( + spacing: 16, + children: [ + Expanded(child: OutlinedButton( + onPressed: previousStep, + child: Expanded(child: Center(child: Text("Atrás"))) + )), + Expanded(child: FilledButton( + onPressed: nextStep, + style: ButtonStyle(backgroundColor: WidgetStatePropertyAll(theme.getColorFor(ThemeCode.buttonSecondary))), + child: Expanded(child: Center(child: Text("Siguiente"))) + )) + ] + ); + } + } +} \ No newline at end of file diff --git a/modules/home/lib/src/presentation/deposit_screen.dart b/modules/home/lib/src/presentation/deposit_screen.dart index cc6f80e0..f5ea4be9 100644 --- a/modules/home/lib/src/presentation/deposit_screen.dart +++ b/modules/home/lib/src/presentation/deposit_screen.dart @@ -1,6 +1,5 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:home/src/presentation/wallet_management_layout.dart'; import 'package:sf_shared/sf_shared.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -56,14 +55,10 @@ class DepositScreen extends ConsumerWidget { "Ingresar dinero en el wallet", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), ), - TextField( - decoration: InputDecoration( - labelText: "Cantidad", - hintText: "0€", - border: OutlineInputBorder(), - ), - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], + CustomTextField( + numeric: true, + label: "Cantidad", + hint: "0€", ), Align( alignment: Alignment.topLeft, @@ -130,16 +125,11 @@ class DepositScreen extends ConsumerWidget { }, activeColor: theme.getColorFor(ThemeCode.buttonPrimary), ), - TextField( - minLines: 3, - maxLines: 3, - maxLength: 150, - decoration: InputDecoration( - labelText: - "Escribir mensaje a ${kid.name} del motivo del ingreso", - hintText: "Escribe tu mensaje", - border: OutlineInputBorder(), - ), + CustomTextField( + lines: 3, + length: 150, + label: "Escribir mensaje a ${kid.name} del motivo del ingreso", + hint: "Escribe tu mensaje", ), Align( alignment: Alignment.topLeft, @@ -184,11 +174,11 @@ class DepositScreen extends ConsumerWidget { }, activeColor: theme.getColorFor(ThemeCode.buttonPrimary), ), - if (program) TextField(), + if (program) CustomTextField(), ], ), ), ], ); } -} +} \ No newline at end of file diff --git a/modules/home/lib/src/presentation/home_screen.dart b/modules/home/lib/src/presentation/home_screen.dart index 8047b036..ae75259c 100644 --- a/modules/home/lib/src/presentation/home_screen.dart +++ b/modules/home/lib/src/presentation/home_screen.dart @@ -1,29 +1,31 @@ -import 'package:auth/auth.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:home/src/presentation/deposit_screen.dart'; -import 'package:home/src/presentation/kid_wallet_screen.dart'; -import 'package:home/src/presentation/money_text.dart'; +import 'package:get_it/get_it.dart'; +import 'package:home/src/presentation/wallet_item.dart'; import 'package:sf_shared/sf_shared.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:navigation/navigation.dart'; class HomeScreen extends ConsumerWidget { final String name = "Juan"; final double total = 95.03; final List kids = [ - Kid(name: "Carlos", balance: 25.47), - Kid(name: "Ana", balance: 25.47), + Kid(name: "Carlos", balance: 25.47, savings: 8.32), + Kid(name: "Ana", balance: 25.47, savings: 8.32), ]; late final double available = double.parse( kids.fold(total, (t, e) => t - e.balance).toStringAsFixed(2), ); + late final double savings = double.parse( + kids.fold(0.0, (t, e) => t + e.savings).toStringAsFixed(2), + ); HomeScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final theme = ref.watch(themePortProvider); + final NavigationContract navigationContract = GetIt.I(); return SafeArea( child: SingleChildScrollView( @@ -51,10 +53,7 @@ class HomeScreen extends ConsumerWidget { Align( alignment: Alignment.topLeft, child: TextButton( - onPressed: () => Navigator.push( - context, - MaterialPageRoute(builder: (_) => CreateProfileScreen()), - ), + onPressed: () => navigationContract.pushTo('/device_signup'), child: Text( "+ Añadir otro peque", style: TextStyle( @@ -64,66 +63,7 @@ class HomeScreen extends ConsumerWidget { ), ), ), - Container( - padding: EdgeInsets.all(20), - decoration: BoxDecoration( - color: theme.getColorFor(ThemeCode.backgroundPrimary), - borderRadius: BorderRadius.all(Radius.circular(20)), - ), - child: Column( - spacing: 5, - children: [ - Row( - children: [ - Text( - "Wallet", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 20, - ), - ), - Spacer(), - MoneyText( - text: "$total€ total", - size: 25, - resize: true, - color: theme.getColorFor(ThemeCode.textPrimary), - ), - ], - ), - Stack( - children: [ - LinearProgressIndicator( - value: available / total, - minHeight: 70, - borderRadius: BorderRadius.all(Radius.circular(16)), - color: theme.getColorFor(ThemeCode.buttonPrimary), - backgroundColor: theme.getColorFor( - ThemeCode.backgroundTertiary, - ), - ), - FractionallySizedBox( - widthFactor: available / total, - child: Container( - padding: EdgeInsets.symmetric(vertical: 10), - child: Center( - child: MoneyText( - text: "$available€", - size: 35, - resize: true, - color: theme.getColorFor( - ThemeCode.textSecondary, - ), - ), - ), - ), - ), - ], - ), - Center(child: Text("Disponible")), - ], - ), - ), + WalletBalanceBlock(max: total, value: total - available, savings: savings), DepositBlock(max: 150 - total), ], ), @@ -133,141 +73,11 @@ class HomeScreen extends ConsumerWidget { } Widget walletsList(BuildContext context, List kids, WidgetRef ref) { - final theme = ref.watch(themePortProvider); return Column( spacing: 20, children: List.generate(kids.length, (int index) { - return GestureDetector( - onTap: () => { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => KidWalletScreen(kid: kids[index]), - ), - ), - }, - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(16.0)), - child: Container( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 8), - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: theme.getCardColorFor(index), - ), - ), - child: Column( - children: [ - Align( - alignment: Alignment.topLeft, - child: Text( - kids[index].name, - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 25, - color: theme.getColorFor(ThemeCode.textSecondary), - ), - ), - ), - Row( - spacing: 10, - children: [ - SizedBox( - height: 60, - width: 60, - child: SvgPicture.asset("assets/images/ui/face.svg"), - ), - Spacer(), - Column( - children: [ - MoneyText( - text: "${kids[index].balance}€", - size: 50, - resize: true, - color: theme.getColorFor(ThemeCode.textSecondary), - ), - Text( - "en su hucha", - style: TextStyle( - color: theme.getColorFor(ThemeCode.textSecondary), - ), - ), - ], - ), - ], - ), - Row( - children: [ - TextButton( - onPressed: () => showDialog( - context: context, - builder: (BuildContext context) => Dialog( - child: Container( - height: 100, - width: double.infinity, - child: Column( - children: [ - FilledButton( - onPressed: () => {}, - child: Text("Cámara"), - ), - OutlinedButton( - onPressed: () => {}, - child: Text("Galería de fotos"), - ), - ], - ), - ), - ), - ), - child: Row( - spacing: 10, - children: [ - Icon( - Icons.edit, - color: theme.getColorFor(ThemeCode.textSecondary), - ), - Text( - "Editar", - style: TextStyle( - color: theme.getColorFor( - ThemeCode.textSecondary, - ), - ), - ), - ], - ), - ), - Spacer(), - FilledButton( - onPressed: () => Navigator.push( - context, - MaterialPageRoute( - builder: (_) => DepositScreen(kid: kids[index]), - ), - ), - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - theme.getColorFor(ThemeCode.buttonSecondary), - ), - ), - child: Container( - padding: EdgeInsets.symmetric( - horizontal: 0, - vertical: 10, - ), - child: Text("+ Añadir dinero"), - ), - ), - ], - ), - ], - ), - ), - ), - ); + return WalletItem(context, kids[index], index); }), ); } diff --git a/modules/home/lib/src/presentation/kid_wallet_screen.dart b/modules/home/lib/src/presentation/kid_wallet_screen.dart index 7519dc04..72cf2cba 100644 --- a/modules/home/lib/src/presentation/kid_wallet_screen.dart +++ b/modules/home/lib/src/presentation/kid_wallet_screen.dart @@ -4,7 +4,6 @@ import 'package:flutter_svg/svg.dart'; import 'package:home/src/presentation/deposit_screen.dart'; import 'package:sf_shared/sf_shared.dart'; import 'package:home/src/presentation/limits_screen.dart'; -import 'package:home/src/presentation/money_text.dart'; import 'package:home/src/presentation/wage_screen.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -16,6 +15,7 @@ class KidWalletScreen extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final theme = ref.watch(themePortProvider); + final bool locked = false; return Scaffold( backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary), @@ -23,19 +23,19 @@ class KidWalletScreen extends ConsumerWidget { children: [ DecoratedBox( decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(30)), + borderRadius: const BorderRadius.only(bottomRight: Radius.circular(24), bottomLeft: Radius.circular(24)), gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, - colors: theme.getCardColorFor(0), - ), + colors: locked? theme.getDisabledCardColors() : theme.getCardColorFor(0) + ) ), - child: SizedBox(width: double.infinity, height: 300), + child: SizedBox(width: double.infinity, height: 420) ), Container( margin: EdgeInsets.symmetric(vertical: 50, horizontal: 20), child: Column( - spacing: 15, + spacing: 24, children: [ Row( spacing: 7, @@ -45,191 +45,237 @@ class KidWalletScreen extends ConsumerWidget { icon: Icon( Icons.arrow_back_ios_new_outlined, color: theme.getColorFor(ThemeCode.backgroundPrimary), - ), + size: 24 + ) ), SizedBox( height: 50, - child: SvgPicture.asset("assets/images/ui/face.svg"), + child: SvgPicture.asset("assets/images/ui/face.svg") ), Text( kid.name, style: TextStyle( color: theme.getColorFor(ThemeCode.backgroundPrimary), fontWeight: FontWeight.bold, - fontSize: 20, - ), + fontSize: 20 + ) ), Spacer(), SizedBox( height: 30, - child: SvgPicture.asset("assets/images/ui/face.svg"), + child: SvgPicture.asset("assets/images/ui/face.svg") + ) + ] + ), + Column( + spacing: 16, + children: [ + MoneyText( + text: "${kid.balance.toString()}€", + size: 60, + secondarySize: 24, + color: theme.getColorFor(ThemeCode.textSecondary) ), - ], - ), - MoneyText( - text: "${kid.balance.toString()}€", - size: 60, - resize: true, - color: theme.getColorFor(ThemeCode.textSecondary), - ), - Text( - "Saldo disponible", - style: TextStyle( - color: theme.getColorFor(ThemeCode.backgroundPrimary), - ), - ), - LinearProgressIndicator( - value: 0.7, - color: theme.getColorFor(ThemeCode.backgroundPrimary), - backgroundColor: theme - .getColorFor(ThemeCode.backgroundPrimary) - .withAlpha(0x4C), - minHeight: 10, - borderRadius: BorderRadius.all(Radius.circular(5)), - ), - Container( - padding: EdgeInsets.all(10), - margin: EdgeInsets.only(top: 30), - decoration: BoxDecoration( - color: theme.getColorFor(ThemeCode.backgroundPrimary), - borderRadius: BorderRadius.all(Radius.circular(20)), - ), - child: Expanded( - child: Center( + Text( + "Saldo disponible", + style: TextStyle( + color: theme.getColorFor(ThemeCode.backgroundPrimary) + ) + ), + LinearProgressIndicator( + value: 0.7, + color: theme.getColorFor(ThemeCode.backgroundPrimary), + backgroundColor: theme + .getColorFor(ThemeCode.backgroundPrimary) + .withAlpha(0x4C), + minHeight: 10, + borderRadius: BorderRadius.all(Radius.circular(5)), + ), + TextButton( + style: ButtonStyle(padding: WidgetStatePropertyAll(EdgeInsets.all(0))), + onPressed: ()=>{}, child: Row( spacing: 10, children: [ - TextButton( - onPressed: () => Navigator.push( - context, - MaterialPageRoute( - builder: (_) => DepositScreen(kid: kid), + Icon(Icons.lock_outline, size: 24, color: theme.getColorFor(ThemeCode.textSecondary)), + locked ? + Text("Desbloquear tarjeta", style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16, color: theme.getColorFor(ThemeCode.textSecondary))) : + Text("Bloquear tarjeta", style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16, color: theme.getColorFor(ThemeCode.textSecondary))) + ] + ) + ) + ] + ), + Column( + spacing: 16, + children: [ + Container( + padding: EdgeInsets.all(10), + margin: EdgeInsets.only(top: 30), + decoration: BoxDecoration( + color: theme.getColorFor(ThemeCode.backgroundPrimary), + borderRadius: BorderRadius.all(Radius.circular(20)) + ), + child: Expanded( + child: Center( + child: Row( + children: [ + TextButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => DepositScreen(kid: kid) + ) + ), + child: Column( + spacing: 10, + children: [ + Icon( + Icons.add_circle_outline, + color: theme.getColorFor( + ThemeCode.textPrimary, + ) + ), + Text( + "Añadir", + style: TextStyle( + color: theme.getColorFor( + ThemeCode.textPrimary + ) + ) + ) + ] + ) ), - ), - child: Column( - spacing: 10, - children: [ - Icon( - Icons.add_circle_outline, - color: theme.getColorFor( - ThemeCode.textPrimary, + Spacer(), + TextButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => WageScreen(kid: kid), ), ), - Text( - "Añadir", - style: TextStyle( - color: theme.getColorFor( - ThemeCode.textPrimary, + child: Column( + spacing: 10, + children: [ + Icon( + Icons.account_balance_wallet_outlined, + color: theme.getColorFor( + ThemeCode.textPrimary, + ), ), - ), + Text( + "Paga", + style: TextStyle( + color: theme.getColorFor( + ThemeCode.textPrimary, + ), + ), + ), + ], ), - ], - ), - ), - Spacer(), - TextButton( - onPressed: () => Navigator.push( - context, - MaterialPageRoute( - builder: (_) => WageScreen(kid: kid), ), - ), - child: Column( - spacing: 10, - children: [ - Icon( - Icons.account_balance_wallet_outlined, - color: theme.getColorFor( - ThemeCode.textPrimary, + Spacer(), + TextButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => LimitsScreen(kid: kid), ), ), - Text( - "Paga", - style: TextStyle( - color: theme.getColorFor( - ThemeCode.textPrimary, + child: Column( + spacing: 10, + children: [ + Icon( + Icons.list_alt_outlined, + color: theme.getColorFor( + ThemeCode.textPrimary, + ), ), - ), + Text( + "Límites", + style: TextStyle( + color: theme.getColorFor( + ThemeCode.textPrimary, + ), + ), + ), + ], ), - ], - ), - ), - Spacer(), - TextButton( - onPressed: () => Navigator.push( - context, - MaterialPageRoute( - builder: (_) => LimitsScreen(kid: kid), ), - ), - child: Column( - spacing: 10, - children: [ - Icon( - Icons.list_alt_outlined, - color: theme.getColorFor( - ThemeCode.textPrimary, - ), - ), - Text( - "Límites", - style: TextStyle( - color: theme.getColorFor( - ThemeCode.textPrimary, + Spacer(), + TextButton( + onPressed: () => {}, + child: Column( + spacing: 10, + children: [ + Icon( + Icons.emoji_events_outlined, + color: theme.getColorFor( + ThemeCode.textPrimary, + ), ), - ), - ), - ], - ), - ), - Spacer(), - TextButton( - onPressed: () => {}, - child: Column( - spacing: 10, - children: [ - Icon( - Icons.emoji_events_outlined, - color: theme.getColorFor( - ThemeCode.textPrimary, - ), - ), - Text( - "Metas", - style: TextStyle( - color: theme.getColorFor( - ThemeCode.textPrimary, + Text( + "Metas", + style: TextStyle( + color: theme.getColorFor( + ThemeCode.textPrimary, + ), + ), ), - ), + ], ), - ], - ), + ), + Spacer(), + TextButton( + onPressed: () => {}, + child: Column( + spacing: 10, + children: [ + Icon( + Icons.cancel_outlined, + color: theme.getColorFor( + ThemeCode.textPrimary, + ), + ), + Text( + "Quitar", + style: TextStyle( + color: theme.getColorFor( + ThemeCode.textPrimary, + ), + ), + ), + ], + ), + ), + ], ), - ], + ), ), ), - ), - ), - Container( - padding: EdgeInsets.all(15), - height: 400, - decoration: BoxDecoration( - color: theme.getColorFor(ThemeCode.backgroundPrimary), - borderRadius: BorderRadius.all(Radius.circular(20)), - ), - child: Column( - children: [ - Text("Últimos movimientos"), - activityList(context, theme), - TextButton(onPressed: () => {}, child: Text("Ver todos")), - ], - ), - ), - ], - ), - ), - ], - ), + Container( + padding: EdgeInsets.all(15), + height: 400, + decoration: BoxDecoration( + color: theme.getColorFor(ThemeCode.backgroundPrimary), + borderRadius: BorderRadius.all(Radius.circular(20)) + ), + child: Expanded(child: Column( + spacing: 24, + children: [ + Text("Últimos movimientos", style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500)), + activityList(context, theme) + ] + )) + ) + ] + ) + ] + ) + ) + ] + ) ); } @@ -251,58 +297,61 @@ class KidWalletScreen extends ConsumerWidget { return Expanded( child: ListView( - children: List.generate(activity.length, (int index) { - return Column( - spacing: 20, - children: [ - Text(activity[index]["date"].toString()), - Column( - spacing: 15, - children: List.generate( - (activity[index]["payments"] as List).length, - (int i) { - //var a = (activity[index]["payments"] as List)[i]; - return Row( - spacing: 7, - children: [ - Container( - padding: EdgeInsets.all(9), - decoration: BoxDecoration( - color: theme.getColorFor( - ThemeCode.backgroundTertiary, + padding: EdgeInsets.symmetric(vertical: 0), + children: [ + ...List.generate(activity.length, (int index) { + return Column( + spacing: 16, + children: [ + Text(activity[index]["date"].toString(), style: TextStyle(fontSize: 18)), + Column( + spacing: 16, + children: List.generate( + (activity[index]["payments"] as List).length, + (int i) { + return Row( + spacing: 12, + children: [ + Container( + padding: EdgeInsets.all(9), + decoration: BoxDecoration( + color: theme.getColorFor( + ThemeCode.backgroundTertiary, + ), + borderRadius: BorderRadius.all(Radius.circular(16)) ), - borderRadius: BorderRadius.all(Radius.circular(16)), + child: Icon( + Icons.local_pizza_outlined, + color: theme.getColorFor(ThemeCode.buttonPrimary) + ) ), - child: Icon( - Icons.local_pizza_outlined, - color: theme.getColorFor(ThemeCode.buttonPrimary), + Column( + children: [ + Text( + "Vips", + style: TextStyle(fontWeight: FontWeight.bold) + ), + Text("20:15") + ] ), - ), - Column( - children: [ - Text( - "Vips", - style: TextStyle(fontWeight: FontWeight.bold), - ), - Text("20:15"), - ], - ), - Spacer(), - MoneyText( - text: "5.1€", - size: 20, - resize: true, - color: theme.getColorFor(ThemeCode.textPrimary), - ), - ], - ); - }, - ), - ), - ], - ); - }), - ), + Spacer(), + MoneyText( + text: "5.1€", + size: 16, + secondarySize: 12, + color: theme.getColorFor(ThemeCode.textPrimary), + ) + ] + ); + } + ) + ) + ] + ); + }), + TextButton(onPressed: () => {}, child: Text("Ver todos")), + ] + ) ); } -} +} \ No newline at end of file diff --git a/modules/home/lib/src/presentation/limits_screen.dart b/modules/home/lib/src/presentation/limits_screen.dart index 7b0866f9..3f61160f 100644 --- a/modules/home/lib/src/presentation/limits_screen.dart +++ b/modules/home/lib/src/presentation/limits_screen.dart @@ -103,7 +103,7 @@ class LimitsScreenState extends ConsumerState { ), ], ), - if (dailyLimits[index]["edit"]) TextField(), + if (dailyLimits[index]["edit"]) CustomTextField(), ], ); }), @@ -144,7 +144,7 @@ class LimitsScreenState extends ConsumerState { ), ], ), - if (timeLimits[index]["edit"]) TextField(), + if (timeLimits[index]["edit"]) CustomTextField(), ], ); }), diff --git a/modules/home/lib/src/presentation/wage_screen.dart b/modules/home/lib/src/presentation/wage_screen.dart index eff4788f..641d31d6 100644 --- a/modules/home/lib/src/presentation/wage_screen.dart +++ b/modules/home/lib/src/presentation/wage_screen.dart @@ -1,6 +1,5 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:home/src/presentation/wallet_management_layout.dart'; import 'package:sf_shared/sf_shared.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -61,14 +60,10 @@ class WageScreen extends ConsumerWidget { "Paga automática", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), ), - TextField( - decoration: InputDecoration( - labelText: "Cantidad", - hintText: "0€", - border: OutlineInputBorder(), - ), - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], + CustomTextField( + numeric: true, + label: "Cantidad", + hint: "0€", ), Text("Saldo total disponible después: 30 €"), ], @@ -154,16 +149,12 @@ class WageScreen extends ConsumerWidget { return DropdownMenuEntry(value: index, label: "$index:00"); }), ), - TextField( - minLines: 3, - maxLines: 3, - maxLength: 150, - decoration: InputDecoration( - labelText: - "Escribir mensaje a ${kid.name} del motivo del ingreso", - hintText: "Escribe tu mensaje", - border: OutlineInputBorder(), - ), + CustomTextField( + lines: 3, + length: 150, + label: + "Escribir mensaje a ${kid.name} del motivo del ingreso", + hint: "Escribe tu mensaje", ), Align( alignment: Alignment.topLeft, diff --git a/modules/home/lib/src/presentation/wallet_item.dart b/modules/home/lib/src/presentation/wallet_item.dart new file mode 100644 index 00000000..f85934e4 --- /dev/null +++ b/modules/home/lib/src/presentation/wallet_item.dart @@ -0,0 +1,248 @@ +import 'dart:ui' as ui; + +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:home/src/presentation/deposit_screen.dart'; +import 'package:home/src/presentation/kid_wallet_screen.dart'; +import 'package:sf_shared/sf_shared.dart'; + + +class WalletItem extends ConsumerWidget{ + final BuildContext context; + final Kid kid; + final int index; + + WalletItem(this.context, this.kid, this.index); + + @override + Widget build(BuildContext context, WidgetRef ref){ + final theme = ref.watch(themePortProvider); + + return GestureDetector( + onTap: ()=>{Navigator.push(context, MaterialPageRoute(builder: (_)=>KidWalletScreen(kid: kid)))}, + child: SizedBox( + height: 227, + width: 382, + child: Stack( + children: [ + SizedBox(height:227, width:382, child: CustomPaint(painter: WalletPainter(ref, index))), + Column( + children: [ + Container( + padding: EdgeInsets.only(left: 24, right: 24, top: 10, bottom: 0), + child: Column( + children: [ + Align( + alignment: Alignment.topLeft, + child: Text(kid.name, + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 30, + color: theme.getColorFor(ThemeCode.textSecondary), + ) + ) + ), + Row( + children: [ + SizedBox( + height: 100, + child: Align( + alignment: Alignment.topLeft, + child: SizedBox( + height: 70, + width: 70, + child: SvgPicture.asset("assets/images/ui/face.svg"), + ) + ) + ), + Spacer(), + Column(children: [ + MoneyText( + text: "${kid.balance}€", + size: 50, + secondarySize: 30, + color: theme.getColorFor(ThemeCode.textSecondary), + height: 0 + ), + Text("en su hucha", style: TextStyle(fontSize: 16, color: theme.getColorFor(ThemeCode.textSecondary), height: 0)), + Text("Ahorrado ${kid.savings}€", style: TextStyle(fontSize: 14, color: theme.getColorFor(ThemeCode.textSecondary), height: 0)) + ]) + ] + ), + ], + ), + ), + Spacer(), + Row(children: [ + TextButton( + onPressed: ()=>showModalBottomSheet( + context: context, + builder: (BuildContext context) => PhotoDialog() + ), + child: Row( + spacing: 8, + children: [ + Icon(Icons.edit_outlined, size: 24, color: theme.getColorFor(ThemeCode.textSecondary)), + Text("Editar", style: TextStyle(fontSize: 16, color: theme.getColorFor(ThemeCode.textSecondary))) + ] + ) + ), + Spacer(), + SizedBox( + width: 169, + height: 60, + child: FilledButton( + onPressed: ()=>Navigator.push(context, MaterialPageRoute(builder: (_)=>DepositScreen(kid: kid))), + style: ButtonStyle( + shape: WidgetStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ) + ), + backgroundColor: WidgetStatePropertyAll(theme.getColorFor(ThemeCode.buttonSecondary)), + padding: WidgetStatePropertyAll(EdgeInsets.all(0)) + ), + child: Center( + child: Text( + "+ Añadir dinero", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500 + ) + ) + ) + ) + ) + ]) + ] + ) + ] + ) + ) + ); + } +} + +class PhotoDialog extends ConsumerWidget{ + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.watch(themePortProvider); + + return Container( + height: 247, + width: double.infinity, + padding: EdgeInsets.all(24), + decoration: BoxDecoration( + color: theme.getColorFor(ThemeCode.backgroundPrimary), + borderRadius: BorderRadius.only( + topRight: Radius.circular(16), topLeft: Radius.circular(16)) + ), + child: Column( + spacing: 24, + children: [ + Column( + spacing: 16, + children: [ + FilledButton( + onPressed: () => {}, + child: Expanded( + child: Center( + child: Text( + "Cámara", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: theme.getColorFor( + ThemeCode.textSecondary) + ) + ) + ) + ) + ), + OutlinedButton( + onPressed: () => {}, + child: Expanded( + child: Center( + child: Text( + "Galería de fotos", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: theme.getColorFor( + ThemeCode.textPrimary) + ) + ) + ) + ) + ) + ] + ), + TextButton( + onPressed: () => {Navigator.pop(context)}, + child: Text( + "Cancelar", + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 16 + ) + ) + ) + ], + ) + ); + } +} + +class WalletPainter extends CustomPainter { + + final WidgetRef ref; + final int index; + + WalletPainter(this.ref, this.index); + + @override + void paint(Canvas canvas, Size size) { + final theme = ref.watch(themePortProvider); + final gradient = theme.getCardColorFor(index); + + final brush = Paint() + ..shader = ui.Gradient.linear( + Offset(0, 0), + Offset(size.width, size.height), + gradient, + List.generate(gradient.length, (int index){ + return index/(gradient.length-1); + }) + ) + ..strokeWidth = 3 + ..strokeCap = StrokeCap.round; + + final double radius = 20; + final double buttonHeight = 60; + final double buttonWidth = 169; + + final path = Path() + ..moveTo(radius, 0) + ..lineTo(size.width-radius, 0) + ..arcToPoint(Offset(size.width, radius), radius: Radius.circular(radius)) + ..lineTo(size.width, size.height-buttonHeight-1.5*radius) + ..arcToPoint(Offset(size.width-radius, size.height-buttonHeight-0.5*radius), radius: Radius.circular(radius)) + ..lineTo(size.width-buttonWidth+0.5*radius, size.height-buttonHeight-0.5*radius) + ..arcToPoint(Offset(size.width-buttonWidth-0.5*radius, size.height-buttonHeight+0.5*radius), radius: Radius.circular(radius), clockwise: false) + ..lineTo(size.width-buttonWidth-0.5*radius, size.height-radius) + ..arcToPoint(Offset(size.width-buttonWidth-1.5*radius, size.height), radius: Radius.circular(radius)) + ..lineTo(radius, size.height) + ..arcToPoint(Offset(0, size.height-radius), radius: Radius.circular(radius)) + ..lineTo(0, radius) + ..arcToPoint(Offset(radius, 0), radius: Radius.circular(radius)) + ..close(); + + canvas.drawPath(path, brush); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => false; +} \ No newline at end of file diff --git a/modules/home/lib/src/presentation/wallet_management_layout.dart b/modules/home/lib/src/presentation/wallet_management_layout.dart index a2d0deab..3e9ebacb 100644 --- a/modules/home/lib/src/presentation/wallet_management_layout.dart +++ b/modules/home/lib/src/presentation/wallet_management_layout.dart @@ -19,6 +19,8 @@ class WalletManagementLayout extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final theme = ref.watch(themePortProvider); + final bool locked = false; + final content = [ Container( padding: EdgeInsets.symmetric(horizontal: 15, vertical: 20), @@ -82,11 +84,11 @@ class WalletManagementLayout extends ConsumerWidget { children: [ DecoratedBox( decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(30)), + borderRadius: const BorderRadius.all(Radius.circular(24)), gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, - colors: theme.getCardColorFor(0), + colors: locked? theme.getDisabledCardColors() : theme.getCardColorFor(0) ), ), child: SizedBox(width: double.infinity, height: 200), diff --git a/modules/home/pubspec.yaml b/modules/home/pubspec.yaml index 8cd79e9b..dc31c5d4 100644 --- a/modules/home/pubspec.yaml +++ b/modules/home/pubspec.yaml @@ -23,6 +23,8 @@ dependencies: path: ../../packages/design_system sf_shared: path: ../../packages/sf_shared + fonts: + path: ../../packages/fonts #dependencies go here flutter_svg: ^2.2.1 flutter_riverpod: ^3.0.3 diff --git a/modules/notifications/lib/src/core/activity_list.dart b/modules/notifications/lib/src/core/activity_list.dart index 686a86b5..a1ad9dda 100644 --- a/modules/notifications/lib/src/core/activity_list.dart +++ b/modules/notifications/lib/src/core/activity_list.dart @@ -25,82 +25,65 @@ class ActivityListState extends ConsumerState { Widget build(BuildContext context) { final theme = ref.watch(themePortProvider); - final colors = [ - Colors.cyan, - Colors.pinkAccent, - Colors.deepOrangeAccent, - Colors.red, - ]; - final icons = { - "wage": Icons.wallet, - "goal": Icons.emoji_events_outlined, - "lock": Icons.lock_outline, - "reload": Icons.attach_money_outlined, - }; - final titles = { - "wage": "Entrega de paga", - "goal": "¡Objetivo cumplido!", - "lock": "Bloqueo de pago", - "reload": "Recarga familiar", - }; + final colors = [Colors.cyan, Colors.pinkAccent, Colors.deepOrangeAccent, Colors.red]; + final icons = {"wage": Icons.wallet, "goal": Icons.emoji_events_outlined, "lock": Icons.lock_outline, "reload": Icons.attach_money_outlined}; + final titles = {"wage": "Entrega de paga", "goal": "¡Objetivo cumplido!", "lock": "Bloqueo de pago", "reload": "Recarga familiar"}; return Column( - spacing: 20, + spacing: 32, children: List.generate(widget.activity.length, (int index) { var logItem = Container( - padding: EdgeInsets.all(20), + padding: EdgeInsets.all(24), decoration: BoxDecoration( color: theme.getColorFor(ThemeCode.backgroundPrimary), - borderRadius: BorderRadius.all(Radius.circular(20)), - border: BoxBorder.fromLTRB( - left: BorderSide(color: colors[index % colors.length], width: 5), - ), + borderRadius: BorderRadius.all(Radius.circular(24)), + border: BoxBorder.fromLTRB(left: BorderSide( + color: colors[index % colors.length], width: 8)) ), child: Column( - spacing: 15, + spacing: 24, children: [ Row( + spacing: 8, children: [ - Icon( - icons[widget.activity[index]["type"]], + Icon(icons[widget.activity[index]["type"]], color: colors[index % colors.length], + size: 20 ), - Text( - titles[widget.activity[index]["type"]]!, - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), - ), - Spacer(), - Text("14/01/2005"), - ], + Expanded(child: Text(titles[widget.activity[index]["type"]]!, + style: TextStyle(fontWeight: FontWeight.w500, fontSize: 18, letterSpacing: 0) + )), + Text("14/01/2005", style: TextStyle(fontSize: 14, letterSpacing: 0)) + ] ), Align( alignment: Alignment.topLeft, - child: Text("Ana ya tiene su paga de 5€ en el reloj"), - ), - ], - ), + child: Text("Ana ya tiene su paga de 5€ en el reloj", style: TextStyle(fontSize: 14, letterSpacing: 0)), + ) + ] + ) ); if (widget.edit) { return Row( + spacing: 10, children: [ Checkbox( value: values[index], onChanged: (value) => { setState(() { values[index] = !values[index]; - }), - }, + })}, activeColor: theme.getColorFor(ThemeCode.buttonPrimary), - semanticLabel: "Eliminar", + semanticLabel: "Eliminar" ), - Expanded(child: logItem), - ], + Expanded(child: logItem) + ] ); } else { return logItem; } - }), + }) ); } -} +} \ No newline at end of file diff --git a/modules/notifications/lib/src/presentation/activity_screen.dart b/modules/notifications/lib/src/presentation/activity_screen.dart index c0fe189d..43a7c26c 100644 --- a/modules/notifications/lib/src/presentation/activity_screen.dart +++ b/modules/notifications/lib/src/presentation/activity_screen.dart @@ -32,7 +32,7 @@ class ActivityScreen extends ConsumerWidget { TextButton(onPressed: () => {}, child: Text("Mes")), ], ), - SizedBox(height: 200, child: LineGraph()), + LineGraph(), ActivityList(activity: activity, edit: false), ]; diff --git a/modules/notifications/lib/src/presentation/alert_screen.dart b/modules/notifications/lib/src/presentation/alert_screen.dart index 4f46d168..86406541 100644 --- a/modules/notifications/lib/src/presentation/alert_screen.dart +++ b/modules/notifications/lib/src/presentation/alert_screen.dart @@ -47,7 +47,11 @@ class AlertScreenState extends ConsumerState { ), ], ), - ActivityList(activity: activity, edit: edit), + Expanded( + child: SingleChildScrollView( + child: ActivityList(activity: activity, edit: edit) + ) + ) ], ), ), diff --git a/modules/profile/lib/src/core/kid_line_chart.dart b/modules/profile/lib/src/core/kid_line_chart.dart new file mode 100644 index 00000000..d6eaf940 --- /dev/null +++ b/modules/profile/lib/src/core/kid_line_chart.dart @@ -0,0 +1,178 @@ +import 'package:design_system/design_system.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:sf_shared/sf_shared.dart'; + +class KidLineChart extends ConsumerWidget{ + final int index; + final Kid kid; + + final weekDays = const ["L", "M", "X", "J", "V", "S", "D"]; + + const KidLineChart({ + super.key, + required this.kid, + this.index = 0 + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.watch(themePortProvider); + + return Container( + width: 183, + padding: EdgeInsets.all(24), + decoration: BoxDecoration( + gradient: LinearGradient(colors: theme.getCardColorFor(index)), + borderRadius: BorderRadius.all(Radius.circular(16)), + ), + child: Column( + spacing: 16, + children: [ + Row( + spacing: 16, + children: [ + SizedBox( + height: 50, + width: 50, + child: SvgPicture.asset("assets/images/ui/face.svg"), + ), + Column(children: [ + Text( + kid.name, + textAlign: TextAlign.left, + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 16, + color: theme.getColorFor(ThemeCode.textSecondary) + ) + ), + MoneyText( + text: "${kid.balance}€", + size: 26, + secondarySize: 16, + color: theme.getColorFor(ThemeCode.textSecondary) + ) + ]) + ], + ), + SizedBox( + height: 93, + child: LineChart(LineChartData( + gridData: FlGridData( + show: true, + drawHorizontalLine: false, + drawVerticalLine: true, + verticalInterval: 1, + getDrawingVerticalLine: (value)=>FlLine(strokeWidth: 12, color: Colors.black26) + ), + titlesData: FlTitlesData( + bottomTitles: AxisTitles( + sideTitles: SideTitles( + interval: 1, + showTitles: true, + reservedSize: 29, + getTitlesWidget: (double value, TitleMeta meta){ + String text = weekDays[value.toInt()]; + + return SideTitleWidget( + space: 4, + meta: meta, + child: Expanded( + child: Center( + child: Text( + text, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + color: theme.getColorFor(ThemeCode.textSecondary) + ) + ) + ) + ), + ); + }, + ), + ), + leftTitles: AxisTitles(), + topTitles: AxisTitles(), + rightTitles: AxisTitles() + ), + lineTouchData: LineTouchData( + touchTooltipData: LineTouchTooltipData( + tooltipBorderRadius: BorderRadius.all(Radius.circular(7)), + getTooltipColor: (spot) => theme.getColorFor(ThemeCode.buttonSecondary), + getTooltipItems: (List touchedSpots) { + return touchedSpots.map((LineBarSpot touchedSpot) { + return LineTooltipItem( + "${touchedSpot.y}€", + TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + color: theme.getColorFor(ThemeCode.textSecondary) + ), + ); + }).toList(); + }, + ), + getTouchedSpotIndicator: ( + _, + indicators, + ) { + return indicators + .map((int index) => const TouchedSpotIndicatorData( + FlLine(color: Colors.transparent), + FlDotData(show: false), + )) + .toList(); + }, + touchSpotThreshold: 25, + distanceCalculator: + (Offset touchPoint, Offset spotPixelCoordinates) => + (touchPoint - spotPixelCoordinates).distance, + ), + borderData: FlBorderData( + show: true, + border: Border( + bottom: BorderSide( + color: Colors.lightBlue.withValues(alpha: 0.2), + width: 4 + ), + left: const BorderSide(color: Colors.transparent), + right: const BorderSide(color: Colors.transparent), + top: const BorderSide(color: Colors.transparent), + ), + ), + lineBarsData: [ + LineChartBarData( + isCurved: true, + color: Colors.white, + barWidth: 4, + isStrokeCapRound: true, + dotData: const FlDotData(show: false), + belowBarData: BarAreaData(show: false), + spots: List.generate(weekDays.length, (int index){ + return FlSpot(index.toDouble(), ((index+1)%2)/2+0.25); + }) + ) + ], + minX: 0, + maxX: weekDays.length-1, + maxY: 1, + minY: 0, + )) + ), + Row( + spacing: 16, + children: [ + Text("Editar hucha", style: TextStyle(color: theme.getColorFor(ThemeCode.textSecondary), fontSize: 14)), + Icon(Icons.edit_outlined, color: theme.getColorFor(ThemeCode.textSecondary), size: 13) + ] + ) + ] + ) + ); + } +} \ No newline at end of file diff --git a/modules/profile/lib/src/presentation/profile_screen.dart b/modules/profile/lib/src/presentation/profile_screen.dart index 71af4c7d..031607d5 100644 --- a/modules/profile/lib/src/presentation/profile_screen.dart +++ b/modules/profile/lib/src/presentation/profile_screen.dart @@ -1,6 +1,7 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:notifications/notifications.dart'; +import 'package:profile/src/core/kid_line_chart.dart'; import 'package:profile/src/settings_screen.dart'; import 'package:sf_shared/sf_shared.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -49,53 +50,45 @@ class ProfileScreen extends ConsumerWidget { ), ], ), + WalletBalanceBlock(max: total, value: available, savings: savings, savingsPlan: savingsPlan), + LineGraph(), + DepositBlock(max: 150 - total), Container( padding: EdgeInsets.all(20), decoration: BoxDecoration( color: theme.getColorFor(ThemeCode.backgroundPrimary), borderRadius: BorderRadius.all(Radius.circular(20)), ), - child: Column( - spacing: 5, - children: [ - Row( - children: [ - Text("Wallet", style: TextStyle(fontWeight: FontWeight.bold)), - Spacer(), - Text("$total€"), - ], - ), - Stack( - children: [ - LinearProgressIndicator( - value: available / total, - minHeight: 70, - borderRadius: BorderRadius.all(Radius.circular(16)), - ), - FractionallySizedBox( - widthFactor: available / total, - child: Container( - padding: EdgeInsets.symmetric(vertical: 20), - child: Center( - child: Text( - "$available€", - style: TextStyle( - color: theme.getColorFor(ThemeCode.textSecondary), - fontSize: 20, - ), - ), - ), - ), - ), - ], - ), - Center(child: Text("Disponible")), - ], + child: TextButton( + onPressed: ()=>{}, + child: Row( + spacing: 10, + children: [ + Icon(Icons.output_outlined, + size: 24, + color: theme.getColorFor(ThemeCode.textPrimary) + ), + Text( + "Retirar dinero del wallet", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w500, + color: theme.getColorFor(ThemeCode.textPrimary) + ) + ) + ], + ) + ) + ), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + spacing: 16, + children: List.generate(kids.length, (int index){ + return KidLineChart(kid: kids[index], index: index); + }) ), ), - SizedBox(height: 200, child: LineGraph()), - DepositBlock(max: 150 - total), - Row(), ActivityList(activity: activity, edit: false), ]; diff --git a/modules/profile/lib/src/profile_builder.dart b/modules/profile/lib/src/profile_builder.dart index e314bd25..27308ff0 100644 --- a/modules/profile/lib/src/profile_builder.dart +++ b/modules/profile/lib/src/profile_builder.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:notifications/notifications.dart'; import 'package:profile/profile.dart'; class ProfileBuilder { diff --git a/modules/profile/pubspec.yaml b/modules/profile/pubspec.yaml index bddcae03..55d1a18f 100644 --- a/modules/profile/pubspec.yaml +++ b/modules/profile/pubspec.yaml @@ -25,6 +25,8 @@ dependencies: flutter_riverpod: ^3.0.3 get_it: ^9.0.5 go_router: ^17.0.0 + flutter_svg: ^2.2.2 + fl_chart: ^1.1.1 dev_dependencies: flutter_test: diff --git a/packages/design_system/lib/design_system.dart b/packages/design_system/lib/design_system.dart index 11d0bc22..96b767ff 100644 --- a/packages/design_system/lib/design_system.dart +++ b/packages/design_system/lib/design_system.dart @@ -1,2 +1,7 @@ export 'src/theme/theme_port.dart'; export 'src/theme/theme_sf_adapter.dart'; +export 'src/steps/step_indicator.dart'; +export 'src/texts/money_text.dart'; +export 'src/progress_bars/progress_bar.dart'; +export 'src/inputs/textfields.dart'; +export 'src/snackbars/snackbar.dart'; \ No newline at end of file diff --git a/packages/design_system/lib/src/inputs/textfields.dart b/packages/design_system/lib/src/inputs/textfields.dart new file mode 100644 index 00000000..c4d5e011 --- /dev/null +++ b/packages/design_system/lib/src/inputs/textfields.dart @@ -0,0 +1,72 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class CustomTextField extends ConsumerStatefulWidget{ + bool? showPassword; + final bool numeric; + final String hint; + final String label; + final int? lines; + final ValueChanged? onChanged; + final int? length; + + CustomTextField({ + super.key, + this.showPassword, + this.numeric = false, + this.hint = '', + this.label = '', + this.lines, + this.length, + this.onChanged, + }); + + @override + ConsumerState createState() => CustomTextFieldState(); +} + +class CustomTextFieldState extends ConsumerState{ + + @override + Widget build(BuildContext context) { + final theme = ref.watch(themePortProvider); + + return TextFormField( + keyboardType: widget.numeric? TextInputType.number : TextInputType.text, + obscureText: !(widget.showPassword ?? true), + enableSuggestions: widget.showPassword ?? true, + autocorrect: !(widget.showPassword ?? false), + style: TextStyle(color: theme.getColorFor(ThemeCode.buttonSecondary)), + inputFormatters: widget.numeric? [ + FilteringTextInputFormatter.digitsOnly + ] : [], + decoration: InputDecoration( + counterText: "", + hintText: widget.hint, + labelText: widget.label, + floatingLabelBehavior: FloatingLabelBehavior.always, + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(12)), + borderSide: BorderSide(color: theme.getColorFor(ThemeCode.textPrimary)), + gapPadding: 16 + ), + suffixIcon: widget.showPassword!=null ? IconButton( + icon: Icon(widget.showPassword! + ? Icons.visibility_off + : Icons.visibility), + onPressed: () { + setState(() { + widget.showPassword = !widget.showPassword!; + }); + }, + ) : null, + ), + minLines: widget.lines ?? 1, + maxLines: widget.lines ?? 1, + maxLength: widget.length, + onChanged: widget.onChanged ?? (_)=>{}, + ); + } +} \ No newline at end of file diff --git a/packages/design_system/lib/src/progress_bars/progress_bar.dart b/packages/design_system/lib/src/progress_bars/progress_bar.dart new file mode 100644 index 00000000..93251da8 --- /dev/null +++ b/packages/design_system/lib/src/progress_bars/progress_bar.dart @@ -0,0 +1,56 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class ProgressBar extends ConsumerWidget{ + final double max; + final double value; + final double height; + final double textSize; + final double textSecondarySize; + final Color backgroundColor; + final Color foregroundColor; + final Color textColor; + + const ProgressBar( + this.max, + this.value, + this.height, + this.textSize, + this.textSecondarySize, + this.backgroundColor, + this.foregroundColor, + this.textColor, + ); + + @override + Widget build(BuildContext context, WidgetRef ref) { + + return + Stack( + children: [ + LinearProgressIndicator( + value: value / max, + minHeight: height, + borderRadius: BorderRadius.all(Radius.circular(24)), + color: foregroundColor, + backgroundColor: backgroundColor + ), + FractionallySizedBox( + widthFactor: value / max, + child: SizedBox( + height: height, + child: Center( + child: MoneyText( + text: "$value€", + size: textSize, + secondarySize: textSecondarySize, + color: textColor, + ), + ) + ), + ), + ] + ); + } +} \ No newline at end of file diff --git a/packages/design_system/lib/src/snackbars/snackbar.dart b/packages/design_system/lib/src/snackbars/snackbar.dart new file mode 100644 index 00000000..e393bae2 --- /dev/null +++ b/packages/design_system/lib/src/snackbars/snackbar.dart @@ -0,0 +1,65 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +enum MessageType { + info, + error, + warning, + success +} + +class CustomSnackBar extends ConsumerWidget{ + final MessageType type; + final String message; + + CustomSnackBar({ + super.key, + this.type = MessageType.info, + required this.message, + }); + + @override + SnackBar build(BuildContext context, WidgetRef ref) { + final theme = ref.watch(themePortProvider); + + late final Color foregroundColor; + late final Color backgroundColor; + late final IconData icon; + + switch (type){ + case MessageType.info: + backgroundColor = Color(0xFFE3EFFD); + foregroundColor = Color(0xFF1F4ECF); + icon = Icons.info; + case MessageType.error: + backgroundColor = Color(0xFFFBEDE9); + foregroundColor = Color(0xFFD12D00); + icon = Icons.cancel; + case MessageType.warning: + backgroundColor = Color(0xFFFBF3E2); + foregroundColor = Color(0xFFE34B04); + icon = Icons.warning_outlined; + case MessageType.success: + backgroundColor = Color(0xFFE2F4E8); + foregroundColor = Color(0xFF00713D); + icon = Icons.check_circle; + } + + return SnackBar( + behavior: SnackBarBehavior.floating, + backgroundColor: backgroundColor, + shape: RoundedRectangleBorder( + side: BorderSide(color: foregroundColor, width: 1), + borderRadius: BorderRadius.all(Radius.circular(10)) + ), + content: Row( + spacing: 8, + children: [ + Icon(icon, color: foregroundColor), + Expanded(child: Text(message, style: TextStyle(color: theme.getColorFor(ThemeCode.textPrimary), fontSize: 14))) + ], + ), + ); + } +} \ No newline at end of file diff --git a/packages/design_system/lib/src/steps/step_indicator.dart b/packages/design_system/lib/src/steps/step_indicator.dart new file mode 100644 index 00000000..9123a50a --- /dev/null +++ b/packages/design_system/lib/src/steps/step_indicator.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; + +class StepIndicator extends StatelessWidget{ + final int max; + final int current; + final Color color; + + const StepIndicator({super.key, required this.max, required this.current, required this.color}); + + @override + Widget build(BuildContext context) { + return Row( + spacing: 12, + children: [ + Spacer(), + ...List.generate(max, (int index){ + return DecoratedBox( + decoration: ShapeDecoration( + shape: CircleBorder(side: BorderSide(color: color)), + color: (index < current)? color: Colors.transparent + ), + child: SizedBox(width: 16, height: 16), + ); + }), + Spacer() + ] + ); + } +} \ No newline at end of file diff --git a/modules/home/lib/src/presentation/money_text.dart b/packages/design_system/lib/src/texts/money_text.dart similarity index 66% rename from modules/home/lib/src/presentation/money_text.dart rename to packages/design_system/lib/src/texts/money_text.dart index 842b644d..d3c7cb66 100644 --- a/modules/home/lib/src/presentation/money_text.dart +++ b/packages/design_system/lib/src/texts/money_text.dart @@ -3,10 +3,18 @@ import 'package:flutter/material.dart'; class MoneyText extends StatelessWidget { final String text; final double size; - final bool resize; + final double? secondarySize; final Color color; + final double height; - const MoneyText({super.key, required this.text, required this.size, required this.resize, required this.color}); + const MoneyText({ + super.key, + required this.text, + required this.size, + this.secondarySize, + required this.color, + this.height = 1, + }); @override Widget build(BuildContext context) { @@ -18,18 +26,18 @@ class MoneyText extends StatelessWidget { style: TextStyle( fontWeight: FontWeight.bold, fontSize: size, - color: color + color: color, + height: height ), children: [ TextSpan( text: cents, style: TextStyle( fontWeight: FontWeight.normal, - fontSize: resize ? size/2 : size + fontSize: secondarySize ?? size, ) ) ] )); } - } \ No newline at end of file diff --git a/packages/design_system/lib/src/theme/theme_port.dart b/packages/design_system/lib/src/theme/theme_port.dart index 19b06231..ea346c1f 100644 --- a/packages/design_system/lib/src/theme/theme_port.dart +++ b/packages/design_system/lib/src/theme/theme_port.dart @@ -25,6 +25,7 @@ enum ThemeCode { abstract class ThemePort { late Map theme; late List> cardColors; + late List disabledCardColors; Color getColorFor(ThemeCode code) { Color? c = theme[code]; @@ -37,4 +38,8 @@ abstract class ThemePort { List getCardColorFor(int index) { return cardColors[index % cardColors.length]; } + + List getDisabledCardColors() { + return disabledCardColors; + } } diff --git a/packages/design_system/lib/src/theme/theme_sf_adapter.dart b/packages/design_system/lib/src/theme/theme_sf_adapter.dart index 846e3a90..c4a68ed4 100644 --- a/packages/design_system/lib/src/theme/theme_sf_adapter.dart +++ b/packages/design_system/lib/src/theme/theme_sf_adapter.dart @@ -25,4 +25,11 @@ class ThemeSfAdapter extends ThemePort { @override List> get cardColors => _cardColors; + + final List _disabledCardColors = [ + Color(0xFF989797), Color(0xFF797676), Color(0xFF5F5A5A) + ]; + + @override + List get disabledCardColors => _disabledCardColors; } diff --git a/packages/design_system/pubspec.yaml b/packages/design_system/pubspec.yaml index ea02fd3a..6167ab2d 100644 --- a/packages/design_system/pubspec.yaml +++ b/packages/design_system/pubspec.yaml @@ -13,6 +13,8 @@ dependencies: sdk: flutter flutter_riverpod: ^3.0.3 get_it: ^9.0.5 + fonts: + path: ../../packages/fonts dev_dependencies: flutter_test: diff --git a/packages/fonts/pubspec.yaml b/packages/fonts/pubspec.yaml new file mode 100644 index 00000000..349b983a --- /dev/null +++ b/packages/fonts/pubspec.yaml @@ -0,0 +1,20 @@ +name: fonts +# resolution: workspace +description: "A new Flutter package project." +version: 0.0.1 + +environment: + sdk: ^3.9.2 + +dependencies: + flutter: + sdk: flutter + +flutter: + uses-material-design: true + fonts: + - family: Stolzl + fonts: + - asset: lib/fonts/stolzl_regular.otf + - asset: lib/fonts/stolzl_bold.otf + weight: 500 \ No newline at end of file diff --git a/packages/sf_shared/lib/sf_shared.dart b/packages/sf_shared/lib/sf_shared.dart index 513ec32e..b9db2b43 100644 --- a/packages/sf_shared/lib/sf_shared.dart +++ b/packages/sf_shared/lib/sf_shared.dart @@ -4,3 +4,4 @@ export 'src/widgets/deposit_block.dart'; export 'src/screens/connection_error_screen.dart'; export 'src/screens/server_error_screen.dart'; export 'src/screens/no_plan_error_screen.dart'; +export 'src/widgets/wallet_balance_block.dart'; diff --git a/packages/sf_shared/lib/src/models/kid.dart b/packages/sf_shared/lib/src/models/kid.dart index 7e3d2720..75838220 100644 --- a/packages/sf_shared/lib/src/models/kid.dart +++ b/packages/sf_shared/lib/src/models/kid.dart @@ -1,10 +1,11 @@ class Kid { final String name; final double balance; + final double savings; const Kid({ required this.name, required this.balance, - + required this.savings, }); } \ No newline at end of file diff --git a/packages/sf_shared/lib/src/widgets/deposit_block.dart b/packages/sf_shared/lib/src/widgets/deposit_block.dart index b4f564a6..c534092f 100644 --- a/packages/sf_shared/lib/src/widgets/deposit_block.dart +++ b/packages/sf_shared/lib/src/widgets/deposit_block.dart @@ -1,6 +1,5 @@ import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class DepositBlock extends ConsumerWidget { @@ -20,40 +19,56 @@ class DepositBlock extends ConsumerWidget { ), margin: EdgeInsets.only(top: 10), child: Column( + spacing: 24, children: [ - Text( - "Ingresar dinero en el wallet", - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + Align( + alignment: Alignment.centerLeft, + child: Text( + "Ingresar dinero en el wallet", + style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20), + ), ), - Row( - spacing: 10, + Column( + spacing: 16, children: [ - Expanded( - child: TextField( - decoration: InputDecoration( - labelText: "Cantidad", - hintText: "0€", - border: OutlineInputBorder(), + Row( + spacing: 10, + children: [ + Expanded( + child: CustomTextField( + label: "Cantidad", + hint: "0€", + numeric: true, + ), ), - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - ), + FilledButton( + onPressed: () => {}, + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll( + theme.getColorFor(ThemeCode.buttonPrimary), + ), + shape: WidgetStatePropertyAll(RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(18)) + )) + ), + child: SizedBox( + height: 60, + child: Center(child: Text("Ingresar", style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500)))), + ), + ], ), - FilledButton( - onPressed: () => {}, - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll( - theme.getColorFor(ThemeCode.buttonPrimary), - ), - ), - child: Text("Ingresar"), + Align( + alignment: Alignment.topLeft, + child: Row( + spacing: 4, + children: [ + Icon(Icons.info_outline, size: 16), + Text("Máximo que puedes añadir: $max€"), + ], + ) ), ], - ), - Align( - alignment: Alignment.topLeft, - child: Text("Máximo que puedes añadir: $max€"), - ), + ) ], ), ); diff --git a/packages/sf_shared/lib/src/widgets/line_graph.dart b/packages/sf_shared/lib/src/widgets/line_graph.dart index 4e42da11..3e764b41 100644 --- a/packages/sf_shared/lib/src/widgets/line_graph.dart +++ b/packages/sf_shared/lib/src/widgets/line_graph.dart @@ -4,10 +4,7 @@ import 'package:fl_chart/fl_chart.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class LineGraph extends ConsumerStatefulWidget { - final lines = [ - [0, 1, 0, 1, 0, 1, 0], - [1, 0, 1, 0, 1, 0, 1], - ]; + final lines = [[0,1,0,1,0,1,0],[1,0,1,0,1,0,1]]; LineGraph({super.key}); @@ -16,7 +13,8 @@ class LineGraph extends ConsumerStatefulWidget { } class LineGraphState extends ConsumerState { - final weekDays = ["L", "M", "X", "J", "V", "S", "D"]; + + final weekDays = ["L", "M", "X", "J", "V", "S", "D"]; String? timeSpan; late var days = weekDays; @@ -31,148 +29,146 @@ class LineGraphState extends ConsumerState { final theme = ref.watch(themePortProvider); return Container( - padding: EdgeInsets.all(15), + padding: EdgeInsets.all(20), decoration: BoxDecoration( - border: BoxBorder.fromLTRB( - left: BorderSide(color: Colors.cyan, width: 5), - ), - borderRadius: BorderRadius.all(Radius.circular(20)), - color: theme.getColorFor(ThemeCode.backgroundPrimary), + border: BoxBorder.fromLTRB(left: BorderSide(color: Colors.cyan, width: 5)), + borderRadius: BorderRadius.all(Radius.circular(13)), + color: theme.getColorFor(ThemeCode.backgroundPrimary) ), child: Column( - spacing: 10, + spacing: 32, children: [ - Row( - children: [ - Text("Gastos", style: TextStyle(fontWeight: FontWeight.bold)), - Spacer(), - Container( - padding: EdgeInsets.symmetric(horizontal: 10), - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - color: theme.getColorFor(ThemeCode.backgroundSecondary), - ), - child: DropdownButton( - underline: Container(), - value: timeSpan, - onChanged: (String? value) { - setState(() { - timeSpan = value; - }); + Row(children: [ + Text("Gastos", style: TextStyle(fontWeight: FontWeight.w500, fontSize: 18)), + Spacer(), + Container( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 0), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8)), + color: theme.getColorFor(ThemeCode.backgroundSecondary), + ), + child: DropdownButton( + underline: Container(), + value: timeSpan, + onChanged: (String? value) { + setState(() { + timeSpan = value; + }); + }, + dropdownColor: theme.getColorFor(ThemeCode.backgroundPrimary), + items: [ + DropdownMenuItem(value: "day", child: Text("Hoy", style: TextStyle(fontSize: 14, letterSpacing: 0))), + DropdownMenuItem(value: "week", child: Text("Esta semana", style: TextStyle(fontSize: 14, letterSpacing: 0))), + DropdownMenuItem(value: "month", child: Text("Este mes", style: TextStyle(fontSize: 14, letterSpacing: 0))), + ] + ), + ) + ]), + SizedBox( + height: 160, + child: LineChart(LineChartData( + gridData: FlGridData( + show: true, + drawHorizontalLine: false, + drawVerticalLine: true, + verticalInterval: 1, + getDrawingVerticalLine: (value)=>FlLine(strokeWidth: 43, color: theme.getColorFor(ThemeCode.backgroundSecondary)) + ), + titlesData: FlTitlesData( + bottomTitles: AxisTitles( + sideTitles: SideTitles( + interval: 1, + showTitles: true, + reservedSize: 40, + getTitlesWidget: (double value, TitleMeta meta){ + String text = weekDays[value.toInt()]; + + return SideTitleWidget( + space: 4, + meta: meta, + child: Expanded(child: Center(child: Text(text, style: TextStyle(fontSize: 12)))), + ); }, - dropdownColor: theme.getColorFor(ThemeCode.backgroundPrimary), - items: [ - DropdownMenuItem(value: "day", child: Text("Hoy")), - DropdownMenuItem(value: "week", child: Text("Esta semana")), - DropdownMenuItem(value: "month", child: Text("Este mes")), - ], ), ), - ], - ), - Expanded( - child: LineChart( - LineChartData( - gridData: FlGridData( - show: true, - drawHorizontalLine: false, - drawVerticalLine: true, - verticalInterval: 1, + leftTitles: AxisTitles(), + topTitles: AxisTitles(), + rightTitles: AxisTitles() + ), + lineTouchData: LineTouchData( + touchTooltipData: LineTouchTooltipData( + tooltipBorderRadius: BorderRadius.all(Radius.circular(7)), + getTooltipColor: (spot) => theme.getColorFor(ThemeCode.buttonSecondary), + getTooltipItems: (List touchedSpots) { + return touchedSpots.map((LineBarSpot touchedSpot) { + return LineTooltipItem( + "${touchedSpot.y}€", + TextStyle(fontWeight: FontWeight.w500, fontSize: 14, color: theme.getColorFor(ThemeCode.textSecondary)), + ); + }).toList(); + }, + ), + getTouchedSpotIndicator: ( + _, + indicators, + ) { + return indicators + .map((int index) => const TouchedSpotIndicatorData( + FlLine(color: Colors.transparent), + FlDotData(show: false), + )) + .toList(); + }, + touchSpotThreshold: 25, + distanceCalculator: + (Offset touchPoint, Offset spotPixelCoordinates) => + (touchPoint - spotPixelCoordinates).distance, + ), + borderData: FlBorderData( + show: true, + border: Border( + bottom: BorderSide( + color: Colors.lightBlue.withValues(alpha: 0.2), + width: 4 ), - titlesData: FlTitlesData( - //show: false, - bottomTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - reservedSize: 40, - getTitlesWidget: (double value, TitleMeta meta) => - SideTitleWidget( - space: 4, - meta: meta, - /*fitInside: fitInsideBottomTitle - ? SideTitleFitInsideData.fromTitleMeta(meta, distanceFromEdge: 0) - : SideTitleFitInsideData.disable(),*/ - child: Text(weekDays[value.toInt()]), - ), - ), - ), - leftTitles: AxisTitles(), - topTitles: AxisTitles(), - rightTitles: AxisTitles(), - ), - lineTouchData: LineTouchData( - touchTooltipData: LineTouchTooltipData( - getTooltipColor: (touchedSpot) => - theme.getColorFor(ThemeCode.buttonSecondary), - getTooltipItems: (List touchedBarSpots) { - return touchedBarSpots.map((barSpot) { - return LineTooltipItem( - "${barSpot.y} €", - TextStyle( - color: theme.getColorFor(ThemeCode.textSecondary), - ), - ); - }).toList(); - }, - ), - ), - borderData: FlBorderData( - show: true, - border: Border( - bottom: BorderSide( - color: Colors.lightBlue.withValues(alpha: 0.2), - width: 4, - ), - left: const BorderSide(color: Colors.transparent), - right: const BorderSide(color: Colors.transparent), - top: const BorderSide(color: Colors.transparent), - ), - ), - lineBarsData: [ - LineChartBarData( - isCurved: true, - color: Colors.pink, - barWidth: 5, - isStrokeCapRound: true, - dotData: const FlDotData(show: false), - belowBarData: BarAreaData(show: false), - spots: const [ - FlSpot(0, 1), - FlSpot(1, 0), - FlSpot(2, 1), - FlSpot(3, 0), - FlSpot(4, 1), - FlSpot(5, 0), - FlSpot(6, 1), - ], - ), - LineChartBarData( - isCurved: true, - color: Colors.cyan, - barWidth: 5, - isStrokeCapRound: true, - dotData: const FlDotData(show: false), - belowBarData: BarAreaData(show: false), - spots: const [ - FlSpot(0, 0), - FlSpot(1, 1), - FlSpot(2, 0), - FlSpot(3, 1), - FlSpot(4, 0), - FlSpot(5, 1), - FlSpot(6, 0), - ], - ), - ], - minX: 0, - maxX: days.length - 1, - maxY: 1, - minY: 0, + left: const BorderSide(color: Colors.transparent), + right: const BorderSide(color: Colors.transparent), + top: const BorderSide(color: Colors.transparent), ), ), - ), + lineBarsData: [ + LineChartBarData( + isCurved: true, + color: Colors.pink, + barWidth: 5, + isStrokeCapRound: true, + dotData: const FlDotData(show: false), + belowBarData: BarAreaData(show: false), + spots: List.generate(days.length, (int index){ + return FlSpot(index.toDouble(), (index+1)%2); + }) + ), + LineChartBarData( + isCurved: true, + color: Colors.cyan, + barWidth: 5, + isStrokeCapRound: true, + dotData: const FlDotData(show: false), + belowBarData: BarAreaData(show: false), + spots: List.generate(days.length, (int index){ + return FlSpot(index.toDouble(), index%2); + }) + ), + ], + minX: 0, + maxX: days.length-1, + maxY: 1, + minY: 0, + )) + ) ], ), ); } -} + +} \ No newline at end of file diff --git a/packages/sf_shared/lib/src/widgets/wallet_balance_block.dart b/packages/sf_shared/lib/src/widgets/wallet_balance_block.dart new file mode 100644 index 00000000..823ca7c7 --- /dev/null +++ b/packages/sf_shared/lib/src/widgets/wallet_balance_block.dart @@ -0,0 +1,59 @@ +import 'package:design_system/design_system.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class WalletBalanceBlock extends ConsumerWidget { + final double max; + final double value; + final double savings; + final double savingsPlan; + + const WalletBalanceBlock({ + super.key, + required this.max, + required this.value, + required this.savings, + this.savingsPlan = 30.0 + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = ref.watch(themePortProvider); + + return Container( + padding: EdgeInsets.all(20), + decoration: BoxDecoration( + color: theme.getColorFor(ThemeCode.backgroundPrimary), + borderRadius: BorderRadius.all(Radius.circular(20)), + ), + child: Column( + spacing: 16, + children: [ + Row( + children: [ + Text( + "Wallet", + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20), + ), + Spacer(), + MoneyText( + text: "$max€ total", + size: 26, + secondarySize: 16, + color: theme.getColorFor(ThemeCode.textPrimary), + ), + ], + ), + Row(children: [ + Text("Objetivos de ahorro"), + Spacer(), + Text("$savingsPlan€") + ]), + ProgressBar(savingsPlan, savings, 24, 16, 12, theme.getColorFor(ThemeCode.backgroundSecondary), theme.getColorFor(ThemeCode.backgroundTertiary), theme.getColorFor(ThemeCode.textPrimary)), + ProgressBar(max, value, 83, 40, 24, theme.getColorFor(ThemeCode.backgroundTertiary), theme.getColorFor(ThemeCode.buttonPrimary), theme.getColorFor(ThemeCode.textSecondary)), + Center(child: Text("Disponible")), + ], + ), + ); + } +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index cccb07d7..47595022 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,7 +13,7 @@ environment: # - modules/notifications # - modules/dashboard_shell dev_dependencies: - melos: ^6.3.3 + melos: 6.3.3 dependencies: flutter_secure_storage: ^9.2.4 dependency_overrides: