- 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.
This commit is contained in:
@@ -21,6 +21,8 @@ class PlatformApp extends ConsumerWidget {
|
||||
return MaterialApp.router(
|
||||
title: 'SaveFamily',
|
||||
theme: ThemeData(
|
||||
fontFamily: 'Stolzl',
|
||||
package: 'fonts',
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Color(0xFF329E95)),
|
||||
),
|
||||
routerConfig: appRouter,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -51,6 +51,8 @@ dependencies:
|
||||
path: ../../packages/navigation
|
||||
design_system:
|
||||
path: ../../packages/design_system
|
||||
fonts:
|
||||
path: ../../packages/fonts
|
||||
|
||||
#dependencies go here
|
||||
cupertino_icons: ^1.0.8
|
||||
|
||||
@@ -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';
|
||||
@@ -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!"),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<void> buildPage(BuildContext context, GoRouterState state) {
|
||||
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
|
||||
|
||||
return MaterialPage<void>(
|
||||
key: state.pageKey,
|
||||
child: DeviceSignupScreen(navigationContract: navigationContract),
|
||||
);
|
||||
}
|
||||
}
|
||||
101
modules/auth/lib/src/device_sign_up/device_signup_screen.dart
Normal file
101
modules/auth/lib/src/device_sign_up/device_signup_screen.dart
Normal file
@@ -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<DeviceSignupScreen> createState() => DeviceSignupScreenState(navigationContract);
|
||||
}
|
||||
|
||||
class DeviceSignupScreenState extends ConsumerState<DeviceSignupScreen>{
|
||||
late int currentStep;
|
||||
final NavigationContract navigationContract;
|
||||
|
||||
DeviceSignupScreenState(this.navigationContract);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
currentStep = 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return getSteps()[currentStep];
|
||||
}
|
||||
|
||||
List<Widget> 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)
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
return Column(
|
||||
spacing: 24,
|
||||
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<Color>(
|
||||
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(),
|
||||
CustomTextField(
|
||||
label: "Nombre",
|
||||
hint: "Nombre",
|
||||
),
|
||||
CustomTextField(
|
||||
label: "Apellidos",
|
||||
hint: "Apellidos",
|
||||
),
|
||||
Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
label: Text("Fecha de nacimiento"),
|
||||
hintText: "DD",
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
child: CustomTextField(
|
||||
numeric: true,
|
||||
label: "Fecha de nacimiento",
|
||||
hint: "DD",
|
||||
length: 2,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
hintText: "MM",
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
child: CustomTextField(
|
||||
numeric: true,
|
||||
hint: "MM",
|
||||
length: 2,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
hintText: "AAAA",
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
child: CustomTextField(
|
||||
numeric: true,
|
||||
hint: "AAAA",
|
||||
length: 4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
labelText: "Dirección completa",
|
||||
hintText: "Nombre de la calle",
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
CustomTextField(
|
||||
label: "Dirección completa",
|
||||
hint: "Nombre de la calle",
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => {},
|
||||
child: Text(
|
||||
"Cambiar dirección",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: theme.getColorFor(ThemeCode.textPrimary)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
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.",
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
]
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<LinkWatchScreen> createState() => LinkWatchScreenState();
|
||||
}
|
||||
|
||||
class LinkWatchScreenState extends ConsumerState<LinkWatchScreen>{
|
||||
|
||||
@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<Color>(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"))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
),
|
||||
),
|
||||
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(
|
||||
hint: "Nombre de usuario",
|
||||
label: "Nombre de usuario",
|
||||
),
|
||||
CustomTextField(
|
||||
showPassword: passwordVisible,
|
||||
label: "Contraseña",
|
||||
hint: "********"
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () =>
|
||||
|
||||
@@ -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<PhoneCodeScreen> {
|
||||
final focusNodes = List<FocusNode>.generate(6, (int i) {
|
||||
|
||||
@@ -1,73 +1,133 @@
|
||||
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<ConsumerStatefulWidget> 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(
|
||||
body: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Center(
|
||||
child: Column(
|
||||
spacing: 48,
|
||||
children: [
|
||||
Spacer(),
|
||||
Expanded(
|
||||
child: CarouselView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemExtent: double.infinity,
|
||||
itemSnapping: true,
|
||||
shrinkExtent: 400,
|
||||
children: generateSteps(),
|
||||
),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () => navigationContract.goTo('/link_phone'),
|
||||
child: const Text('Continuar'),
|
||||
),
|
||||
Spacer(),
|
||||
],
|
||||
),
|
||||
generateSteps()[currentStep],
|
||||
Column(
|
||||
spacing: 24,
|
||||
children: [
|
||||
StepIndicator(max: 3, current: currentStep+1, color: theme.getColorFor(ThemeCode.buttonSecondary)),
|
||||
generateButtons(theme, 3, currentStep+1)
|
||||
]
|
||||
),
|
||||
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<Color>(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<Color>(theme.getColorFor(ThemeCode.buttonSecondary))
|
||||
),
|
||||
onPressed: ()=>setState(() {
|
||||
currentStep++;
|
||||
}),
|
||||
child: Expanded(child: Center( child: Text("Siguiente")))
|
||||
),
|
||||
TextButton(
|
||||
onPressed: ()=>navigationContract.goTo('/link_phone'),
|
||||
child: Text("Omitir")
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
List<Widget> generateSteps() {
|
||||
return [
|
||||
Column(
|
||||
spacing: 30,
|
||||
spacing: 48,
|
||||
children: [
|
||||
SvgPicture.asset("assets/images/ui/bienvenida_paso1.svg"),
|
||||
Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
Text(
|
||||
"Aprende a gestionar su dinero",
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 30)
|
||||
),
|
||||
Text("Tu peque crea hábitos y se divierte mientras lo hace"),
|
||||
],
|
||||
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)),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<NewPasswordScreen> {
|
||||
void initState() {
|
||||
passwordVisible = false;
|
||||
equalPasswords = false;
|
||||
String password = "";
|
||||
password = "";
|
||||
securityChecks = {
|
||||
"min": false,
|
||||
"capital": false,
|
||||
@@ -36,6 +38,7 @@ class NewPasswordScreenState extends ConsumerState<NewPasswordScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
|
||||
final theme = ref.watch(themePortProvider);
|
||||
|
||||
return Scaffold(
|
||||
@@ -50,25 +53,10 @@ class NewPasswordScreenState extends ConsumerState<NewPasswordScreen> {
|
||||
"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<NewPasswordScreen> {
|
||||
}),
|
||||
},
|
||||
),
|
||||
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<NewPasswordScreen> {
|
||||
),
|
||||
Spacer(flex: 1),
|
||||
FilledButton(
|
||||
onPressed: () => {},
|
||||
onPressed: ()=>{navigationContract.pushTo('/main/home')},
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.all(20),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"),
|
||||
),
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
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(),
|
||||
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,
|
||||
)),
|
||||
]
|
||||
),
|
||||
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<DropdownMenuEntry>.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"),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
)),
|
||||
],
|
||||
),
|
||||
CustomTextField(label: "Nombre", hint: "Nombre"),
|
||||
CustomTextField(label: "Apellidos", hint: "Apellidos"),
|
||||
CustomTextField(label: "DNI", hint: "DNI"),
|
||||
Row(children: [
|
||||
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"),
|
||||
],
|
||||
initialSelection: "es",
|
||||
dropdownMenuEntries: List<DropdownMenuEntry>.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"),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<SignupScreen> createState() => SignupScreenState();
|
||||
}
|
||||
|
||||
class SignupScreenState extends ConsumerState<SignupScreen> {
|
||||
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: <Widget>[
|
||||
Expanded(
|
||||
child: OutlinedButton(
|
||||
onPressed: controls.onStepCancel,
|
||||
child: const Text('Atrás'),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: FilledButton(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStatePropertyAll<Color>(
|
||||
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<Step> getSteps() {
|
||||
return <Step>[
|
||||
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),
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return getSteps()[currentStep];
|
||||
}
|
||||
|
||||
List<Widget> 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: ()=>{},
|
||||
),
|
||||
title: const Text(""),
|
||||
content: SignupPersonalScreen(),
|
||||
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 > 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(),
|
||||
),
|
||||
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<NavigationContract>().pushTo('/device_signup')},
|
||||
previousStep: ()=>{setState(() {
|
||||
currentStep--;
|
||||
})},
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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<SignupUserScreen>{
|
||||
@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<DropdownMenuEntry>.generate(3, (int index){
|
||||
return DropdownMenuEntry(labelWidget: Icon(Icons.outlined_flag), label: "es", value: "es");
|
||||
})
|
||||
CustomTextField(
|
||||
showPassword: passwordVisible,
|
||||
label: "Contraseña",
|
||||
hint: "********"
|
||||
),
|
||||
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;
|
||||
});
|
||||
},
|
||||
),
|
||||
)
|
||||
),
|
||||
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: "*******"
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
91
modules/auth/lib/src/widgets/layouts/form_step_layout.dart
Normal file
91
modules/auth/lib/src/widgets/layouts/form_step_layout.dart
Normal file
@@ -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<Widget> body;
|
||||
final List<Widget>? 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<Color>(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<Color>(theme.getColorFor(ThemeCode.buttonSecondary))),
|
||||
child: Expanded(child: Center(child: Text("Siguiente")))
|
||||
))
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,7 +174,7 @@ class DepositScreen extends ConsumerWidget {
|
||||
},
|
||||
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
|
||||
),
|
||||
if (program) TextField(),
|
||||
if (program) CustomTextField(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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<Kid> 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<NavigationContract>();
|
||||
|
||||
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<Kid> kids, WidgetRef ref) {
|
||||
final theme = ref.watch(themePortProvider);
|
||||
|
||||
return Column(
|
||||
spacing: 20,
|
||||
children: List<Widget>.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<Color>(
|
||||
theme.getColorFor(ThemeCode.buttonSecondary),
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 0,
|
||||
vertical: 10,
|
||||
),
|
||||
child: Text("+ Añadir dinero"),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return WalletItem(context, kids[index], index);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,38 +45,42 @@ 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,
|
||||
resize: true,
|
||||
color: theme.getColorFor(ThemeCode.textSecondary),
|
||||
secondarySize: 24,
|
||||
color: theme.getColorFor(ThemeCode.textSecondary)
|
||||
),
|
||||
Text(
|
||||
"Saldo disponible",
|
||||
style: TextStyle(
|
||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||
),
|
||||
color: theme.getColorFor(ThemeCode.backgroundPrimary)
|
||||
)
|
||||
),
|
||||
LinearProgressIndicator(
|
||||
value: 0.7,
|
||||
@@ -87,24 +91,41 @@ class KidWalletScreen extends ConsumerWidget {
|
||||
minHeight: 10,
|
||||
borderRadius: BorderRadius.all(Radius.circular(5)),
|
||||
),
|
||||
TextButton(
|
||||
style: ButtonStyle(padding: WidgetStatePropertyAll(EdgeInsets.all(0))),
|
||||
onPressed: ()=>{},
|
||||
child: Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
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)),
|
||||
borderRadius: BorderRadius.all(Radius.circular(20))
|
||||
),
|
||||
child: Expanded(
|
||||
child: Center(
|
||||
child: Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (_) => DepositScreen(kid: kid),
|
||||
),
|
||||
builder: (_) => DepositScreen(kid: kid)
|
||||
)
|
||||
),
|
||||
child: Column(
|
||||
spacing: 10,
|
||||
@@ -113,18 +134,18 @@ class KidWalletScreen extends ConsumerWidget {
|
||||
Icons.add_circle_outline,
|
||||
color: theme.getColorFor(
|
||||
ThemeCode.textPrimary,
|
||||
),
|
||||
)
|
||||
),
|
||||
Text(
|
||||
"Añadir",
|
||||
style: TextStyle(
|
||||
color: theme.getColorFor(
|
||||
ThemeCode.textPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
ThemeCode.textPrimary
|
||||
)
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
),
|
||||
Spacer(),
|
||||
TextButton(
|
||||
@@ -205,6 +226,29 @@ class KidWalletScreen extends ConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -215,21 +259,23 @@ class KidWalletScreen extends ConsumerWidget {
|
||||
height: 400,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.getColorFor(ThemeCode.backgroundPrimary),
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
borderRadius: BorderRadius.all(Radius.circular(20))
|
||||
),
|
||||
child: Column(
|
||||
child: Expanded(child: Column(
|
||||
spacing: 24,
|
||||
children: [
|
||||
Text("Últimos movimientos"),
|
||||
activityList(context, theme),
|
||||
TextButton(onPressed: () => {}, child: Text("Ver todos")),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text("Últimos movimientos", style: TextStyle(fontSize: 20, fontWeight: FontWeight.w500)),
|
||||
activityList(context, theme)
|
||||
]
|
||||
))
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -251,19 +297,20 @@ class KidWalletScreen extends ConsumerWidget {
|
||||
|
||||
return Expanded(
|
||||
child: ListView(
|
||||
children: List<Widget>.generate(activity.length, (int index) {
|
||||
return Column(
|
||||
spacing: 20,
|
||||
padding: EdgeInsets.symmetric(vertical: 0),
|
||||
children: [
|
||||
Text(activity[index]["date"].toString()),
|
||||
...List<Widget>.generate(activity.length, (int index) {
|
||||
return Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
Text(activity[index]["date"].toString(), style: TextStyle(fontSize: 18)),
|
||||
Column(
|
||||
spacing: 15,
|
||||
spacing: 16,
|
||||
children: List<Widget>.generate(
|
||||
(activity[index]["payments"] as List<Object>).length,
|
||||
(int i) {
|
||||
//var a = (activity[index]["payments"] as List<Object>)[i];
|
||||
return Row(
|
||||
spacing: 7,
|
||||
spacing: 12,
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.all(9),
|
||||
@@ -271,38 +318,40 @@ class KidWalletScreen extends ConsumerWidget {
|
||||
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),
|
||||
),
|
||||
color: theme.getColorFor(ThemeCode.buttonPrimary)
|
||||
)
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
"Vips",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
style: TextStyle(fontWeight: FontWeight.bold)
|
||||
),
|
||||
Text("20:15"),
|
||||
],
|
||||
Text("20:15")
|
||||
]
|
||||
),
|
||||
Spacer(),
|
||||
MoneyText(
|
||||
text: "5.1€",
|
||||
size: 20,
|
||||
resize: true,
|
||||
size: 16,
|
||||
secondarySize: 12,
|
||||
color: theme.getColorFor(ThemeCode.textPrimary),
|
||||
),
|
||||
],
|
||||
)
|
||||
]
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
}
|
||||
)
|
||||
)
|
||||
]
|
||||
);
|
||||
}),
|
||||
),
|
||||
TextButton(onPressed: () => {}, child: Text("Ver todos")),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ class LimitsScreenState extends ConsumerState<LimitsScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (dailyLimits[index]["edit"]) TextField(),
|
||||
if (dailyLimits[index]["edit"]) CustomTextField(),
|
||||
],
|
||||
);
|
||||
}),
|
||||
@@ -144,7 +144,7 @@ class LimitsScreenState extends ConsumerState<LimitsScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (timeLimits[index]["edit"]) TextField(),
|
||||
if (timeLimits[index]["edit"]) CustomTextField(),
|
||||
],
|
||||
);
|
||||
}),
|
||||
|
||||
@@ -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:
|
||||
CustomTextField(
|
||||
lines: 3,
|
||||
length: 150,
|
||||
label:
|
||||
"Escribir mensaje a ${kid.name} del motivo del ingreso",
|
||||
hintText: "Escribe tu mensaje",
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
hint: "Escribe tu mensaje",
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.topLeft,
|
||||
|
||||
248
modules/home/lib/src/presentation/wallet_item.dart
Normal file
248
modules/home/lib/src/presentation/wallet_item.dart
Normal file
@@ -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<void>(
|
||||
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>(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
)
|
||||
),
|
||||
backgroundColor: WidgetStatePropertyAll<Color>(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<double>.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;
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,82 +25,65 @@ class ActivityListState extends ConsumerState<ActivityList> {
|
||||
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<Widget>.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;
|
||||
}
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ class ActivityScreen extends ConsumerWidget {
|
||||
TextButton(onPressed: () => {}, child: Text("Mes")),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 200, child: LineGraph()),
|
||||
LineGraph(),
|
||||
ActivityList(activity: activity, edit: false),
|
||||
];
|
||||
|
||||
|
||||
@@ -47,7 +47,11 @@ class AlertScreenState extends ConsumerState<AlertScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
ActivityList(activity: activity, edit: edit),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: ActivityList(activity: activity, edit: edit)
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
178
modules/profile/lib/src/core/kid_line_chart.dart
Normal file
178
modules/profile/lib/src/core/kid_line_chart.dart
Normal file
@@ -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<LineBarSpot> 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<FlSpot>.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)
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
child: TextButton(
|
||||
onPressed: ()=>{},
|
||||
child: Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text("Wallet", style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
Spacer(),
|
||||
Text("$total€"),
|
||||
],
|
||||
Icon(Icons.output_outlined,
|
||||
size: 24,
|
||||
color: theme.getColorFor(ThemeCode.textPrimary)
|
||||
),
|
||||
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€",
|
||||
Text(
|
||||
"Retirar dinero del wallet",
|
||||
style: TextStyle(
|
||||
color: theme.getColorFor(ThemeCode.textSecondary),
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
fontWeight: FontWeight.w500,
|
||||
color: theme.getColorFor(ThemeCode.textPrimary)
|
||||
)
|
||||
)
|
||||
],
|
||||
)
|
||||
)
|
||||
),
|
||||
Center(child: Text("Disponible")),
|
||||
],
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
spacing: 16,
|
||||
children: List<Widget>.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),
|
||||
];
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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';
|
||||
72
packages/design_system/lib/src/inputs/textfields.dart
Normal file
72
packages/design_system/lib/src/inputs/textfields.dart
Normal file
@@ -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<String>? onChanged;
|
||||
final int? length;
|
||||
|
||||
CustomTextField({
|
||||
super.key,
|
||||
this.showPassword,
|
||||
this.numeric = false,
|
||||
this.hint = '',
|
||||
this.label = '',
|
||||
this.lines,
|
||||
this.length,
|
||||
this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<CustomTextField> createState() => CustomTextFieldState();
|
||||
}
|
||||
|
||||
class CustomTextFieldState extends ConsumerState<CustomTextField>{
|
||||
|
||||
@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 ?? (_)=>{},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
65
packages/design_system/lib/src/snackbars/snackbar.dart
Normal file
65
packages/design_system/lib/src/snackbars/snackbar.dart
Normal file
@@ -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)))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
29
packages/design_system/lib/src/steps/step_indicator.dart
Normal file
29
packages/design_system/lib/src/steps/step_indicator.dart
Normal file
@@ -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<Widget>.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()
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
)
|
||||
]
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,7 @@ enum ThemeCode {
|
||||
abstract class ThemePort {
|
||||
late Map<ThemeCode, Color> theme;
|
||||
late List<List<Color>> cardColors;
|
||||
late List<Color> disabledCardColors;
|
||||
|
||||
Color getColorFor(ThemeCode code) {
|
||||
Color? c = theme[code];
|
||||
@@ -37,4 +38,8 @@ abstract class ThemePort {
|
||||
List<Color> getCardColorFor(int index) {
|
||||
return cardColors[index % cardColors.length];
|
||||
}
|
||||
|
||||
List<Color> getDisabledCardColors() {
|
||||
return disabledCardColors;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,4 +25,11 @@ class ThemeSfAdapter extends ThemePort {
|
||||
|
||||
@override
|
||||
List<List<Color>> get cardColors => _cardColors;
|
||||
|
||||
final List<Color> _disabledCardColors = [
|
||||
Color(0xFF989797), Color(0xFF797676), Color(0xFF5F5A5A)
|
||||
];
|
||||
|
||||
@override
|
||||
List<Color> get disabledCardColors => _disabledCardColors;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
20
packages/fonts/pubspec.yaml
Normal file
20
packages/fonts/pubspec.yaml
Normal file
@@ -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
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
@@ -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,23 +19,26 @@ class DepositBlock extends ConsumerWidget {
|
||||
),
|
||||
margin: EdgeInsets.only(top: 10),
|
||||
child: Column(
|
||||
spacing: 24,
|
||||
children: [
|
||||
Text(
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
"Ingresar dinero en el wallet",
|
||||
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
|
||||
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 20),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
Row(
|
||||
spacing: 10,
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
labelText: "Cantidad",
|
||||
hintText: "0€",
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
child: CustomTextField(
|
||||
label: "Cantidad",
|
||||
hint: "0€",
|
||||
numeric: true,
|
||||
),
|
||||
),
|
||||
FilledButton(
|
||||
@@ -45,16 +47,29 @@ class DepositBlock extends ConsumerWidget {
|
||||
backgroundColor: WidgetStatePropertyAll<Color>(
|
||||
theme.getColorFor(ThemeCode.buttonPrimary),
|
||||
),
|
||||
shape: WidgetStatePropertyAll(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(18))
|
||||
))
|
||||
),
|
||||
child: Text("Ingresar"),
|
||||
child: SizedBox(
|
||||
height: 60,
|
||||
child: Center(child: Text("Ingresar", style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500)))),
|
||||
),
|
||||
],
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Text("Máximo que puedes añadir: $max€"),
|
||||
child: Row(
|
||||
spacing: 4,
|
||||
children: [
|
||||
Icon(Icons.info_outline, size: 16),
|
||||
Text("Máximo que puedes añadir: $max€"),
|
||||
],
|
||||
)
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,6 +13,7 @@ class LineGraph extends ConsumerStatefulWidget {
|
||||
}
|
||||
|
||||
class LineGraphState extends ConsumerState<LineGraph> {
|
||||
|
||||
final weekDays = ["L", "M", "X", "J", "V", "S", "D"];
|
||||
String? timeSpan;
|
||||
late var days = weekDays;
|
||||
@@ -31,25 +29,22 @@ class LineGraphState extends ConsumerState<LineGraph> {
|
||||
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)),
|
||||
Row(children: [
|
||||
Text("Gastos", style: TextStyle(fontWeight: FontWeight.w500, fontSize: 18)),
|
||||
Spacer(),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 0),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10)),
|
||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
||||
color: theme.getColorFor(ThemeCode.backgroundSecondary),
|
||||
),
|
||||
child: DropdownButton(
|
||||
@@ -62,66 +57,79 @@ class LineGraphState extends ConsumerState<LineGraph> {
|
||||
},
|
||||
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")),
|
||||
],
|
||||
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))),
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
)
|
||||
]),
|
||||
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(
|
||||
//show: false,
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
interval: 1,
|
||||
showTitles: true,
|
||||
reservedSize: 40,
|
||||
getTitlesWidget: (double value, TitleMeta meta) =>
|
||||
SideTitleWidget(
|
||||
getTitlesWidget: (double value, TitleMeta meta){
|
||||
String text = weekDays[value.toInt()];
|
||||
|
||||
return SideTitleWidget(
|
||||
space: 4,
|
||||
meta: meta,
|
||||
/*fitInside: fitInsideBottomTitle
|
||||
? SideTitleFitInsideData.fromTitleMeta(meta, distanceFromEdge: 0)
|
||||
: SideTitleFitInsideData.disable(),*/
|
||||
child: Text(weekDays[value.toInt()]),
|
||||
),
|
||||
child: Expanded(child: Center(child: Text(text, style: TextStyle(fontSize: 12)))),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
leftTitles: AxisTitles(),
|
||||
topTitles: AxisTitles(),
|
||||
rightTitles: AxisTitles(),
|
||||
rightTitles: AxisTitles()
|
||||
),
|
||||
lineTouchData: LineTouchData(
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
getTooltipColor: (touchedSpot) =>
|
||||
theme.getColorFor(ThemeCode.buttonSecondary),
|
||||
getTooltipItems: (List<LineBarSpot> touchedBarSpots) {
|
||||
return touchedBarSpots.map((barSpot) {
|
||||
tooltipBorderRadius: BorderRadius.all(Radius.circular(7)),
|
||||
getTooltipColor: (spot) => theme.getColorFor(ThemeCode.buttonSecondary),
|
||||
getTooltipItems: (List<LineBarSpot> touchedSpots) {
|
||||
return touchedSpots.map((LineBarSpot touchedSpot) {
|
||||
return LineTooltipItem(
|
||||
"${barSpot.y} €",
|
||||
TextStyle(
|
||||
color: theme.getColorFor(ThemeCode.textSecondary),
|
||||
),
|
||||
"${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,
|
||||
width: 4
|
||||
),
|
||||
left: const BorderSide(color: Colors.transparent),
|
||||
right: const BorderSide(color: Colors.transparent),
|
||||
@@ -136,15 +144,9 @@ class LineGraphState extends ConsumerState<LineGraph> {
|
||||
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),
|
||||
],
|
||||
spots: List<FlSpot>.generate(days.length, (int index){
|
||||
return FlSpot(index.toDouble(), (index+1)%2);
|
||||
})
|
||||
),
|
||||
LineChartBarData(
|
||||
isCurved: true,
|
||||
@@ -153,26 +155,20 @@ class LineGraphState extends ConsumerState<LineGraph> {
|
||||
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),
|
||||
],
|
||||
spots: List<FlSpot>.generate(days.length, (int index){
|
||||
return FlSpot(index.toDouble(), index%2);
|
||||
})
|
||||
),
|
||||
],
|
||||
minX: 0,
|
||||
maxX: days.length-1,
|
||||
maxY: 1,
|
||||
minY: 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
))
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
59
packages/sf_shared/lib/src/widgets/wallet_balance_block.dart
Normal file
59
packages/sf_shared/lib/src/widgets/wallet_balance_block.dart
Normal file
@@ -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")),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user