- create auth, main shell, home, profile, notifications and settings modules.

- added navigation, utils, design system and shared packages
- implemented go router in entiered app
- implemented flutter riverpod instead provider
This commit is contained in:
AlcalaJulian
2025-11-13 15:16:00 +01:00
parent 75beafd771
commit 5ca37d2822
332 changed files with 7759 additions and 3452 deletions

31
modules/auth/.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.flutter-plugins-dependencies
/build/
/coverage/

10
modules/auth/.metadata Normal file
View File

@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
channel: "stable"
project_type: package

View File

@@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

1
modules/auth/LICENSE Normal file
View File

@@ -0,0 +1 @@
TODO: Add your license here.

39
modules/auth/README.md Normal file
View File

@@ -0,0 +1,39 @@
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/to/develop-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart
const like = 'sample';
```
## Additional information
TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.

View File

@@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@@ -0,0 +1,6 @@
export 'src/device_sign_up/link_watch/create_profile_screen.dart';
export 'src/onboarding/onboarding_builder.dart';
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';

View File

@@ -0,0 +1,57 @@
import 'package:auth/src/device_sign_up/link_watch/create_profile_screen.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
// import 'package:sf_app_platform/payments/view/screens/core/dashboard_screen.dart';
// import 'package:sf_app_platform/payments/view/screens/link_watch/create_profile_screen.dart';
class AddKidScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.all(30),
child: Column(
spacing: 15,
children: [
Spacer(flex: 6),
Text("Añade a tu peque"),
Text(
"Controla su gasto a la vez que aprende hábitos financieros responsables",
),
Container(
margin: EdgeInsets.symmetric(vertical: 30, horizontal: 50),
child: Row(
children: [
Column(children: [Text("1"), Text("2"), Text("3")]),
Column(
children: [
Text("Crea su perfil"),
Text("Vincula su correa y su reloj"),
Text("Carga su hucha"),
],
),
],
),
),
Text("¡Y todo listo para que tenga su dinero!"),
Text("Recuerda que necesitas tener un Plan SaveFamily"),
Text(
"Si aún no lo tienes, puedes conseguirlo a través de nuestra web",
),
Spacer(flex: 8),
Container(
width: double.infinity,
child: FilledButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => CreateProfileScreen()),
),
child: Text("¡Empezar!"),
),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,79 @@
import 'package:flutter/material.dart';
class ContactScreen extends StatelessWidget {
const ContactScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
margin: EdgeInsets.all(30),
child: Center(
child: Column(
spacing: 10,
children: [
Text(
"Contáctanos",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
),
Text(
"Trasládanos tus dudas e intentaremos responderte lo antes posible",
),
DropdownMenu(
initialSelection: "es",
label: Text("País"),
dropdownMenuEntries: [
DropdownMenuEntry(value: "es", label: "España"),
DropdownMenuEntry(value: "fr", label: "Francia"),
DropdownMenuEntry(value: "pt", label: "Portugal"),
],
),
DropdownMenu(
initialSelection: "online",
label: Text("Canal de compra"),
dropdownMenuEntries: [
DropdownMenuEntry(value: "online", label: "SF online shop"),
],
),
Expanded(
child: TextField(
decoration: InputDecoration(
labelText: "Nombre",
hintText: "Nombre y apellidos",
border: OutlineInputBorder(),
),
),
),
Expanded(
child: TextField(
decoration: InputDecoration(
labelText: "Correo electrónico",
hintText: "Correo electrónico",
border: OutlineInputBorder(),
),
),
),
Expanded(
child: TextField(
minLines: 3,
maxLines: 3,
decoration: InputDecoration(
labelText: "Asunto del mensaje",
hintText: "Escribe tu mensaje",
border: OutlineInputBorder(),
),
),
),
Expanded(
child: FilledButton(
onPressed: () => Navigator.pop(context),
child: Text("Enviar"),
),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,87 @@
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"),
),
],
),
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,304 @@
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 {
CreateProfileScreen({super.key});
int currentStep = 0;
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: Container(
margin: EdgeInsets.all(30),
child: Center(
child: Column(
spacing: 10,
children: [
Stepper(
type: StepperType.horizontal,
currentStep: currentStep,
onStepCancel: () => currentStep == 0,
// ? null
// :
// setState(() {
// currentStep -= 1;
// }),
controlsBuilder:
(BuildContext context, ControlsDetails controls) {
return FilledButton(
style: ButtonStyle(
backgroundColor: WidgetStatePropertyAll<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(),
),
),
Row(
spacing: 10,
children: [
Expanded(
child: TextField(
decoration: InputDecoration(
label: Text("Fecha de nacimiento"),
hintText: "DD",
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
),
),
Expanded(
child: TextField(
decoration: InputDecoration(
hintText: "MM",
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
),
),
Expanded(
child: TextField(
decoration: InputDecoration(
hintText: "AAAA",
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
),
),
],
),
TextField(
decoration: InputDecoration(
labelText: "Dirección completa",
hintText: "Nombre de la calle",
border: OutlineInputBorder(),
),
),
TextButton(
onPressed: () => {},
child: Text(
"Cambiar dirección",
style: TextStyle(fontWeight: FontWeight.bold),
),
),
],
),
),
Step(
state: currentStep > 1
? StepState.complete
: StepState.indexed,
isActive: currentStep >= 1,
stepStyle: currentStep >= 1
? StepStyle(
connectorThickness: 0,
color: Color(0xFF329e95),
indexStyle: TextStyle(color: Colors.transparent),
)
: StepStyle(
connectorThickness: 0,
color: Colors.transparent,
boxShadow: BoxShadow(spreadRadius: 5),
indexStyle: TextStyle(color: Colors.transparent),
),
title: Text(""),
content: Column(
spacing: 10,
children: [
Text(
"Vincula su correa y su reloj",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
SvgPicture.asset("assets/images/ui/formulario.svg"),
Row(
spacing: 10,
children: [
Text("1"),
Column(
children: [
Text("Escanea la correa"),
Text("El peque podrá realizar pagos"),
],
),
],
),
Row(
spacing: 10,
children: [
Text("2"),
Column(
children: [
Text("Escanea el reloj"),
Text("Visualizarás los gastos que se hagan"),
],
),
],
),
],
),
),
Step(
state: currentStep > 2
? StepState.complete
: StepState.indexed,
isActive: currentStep >= 2,
stepStyle: currentStep >= 2
? StepStyle(
connectorThickness: 0,
color: Color(0xFF329e95),
indexStyle: TextStyle(color: Colors.transparent),
)
: StepStyle(
connectorThickness: 0,
color: Colors.transparent,
boxShadow: BoxShadow(spreadRadius: 5),
indexStyle: TextStyle(color: Colors.transparent),
),
title: Text(""),
content: Column(
spacing: 10,
children: [
Text(
"¡Dale su primera paga!",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
Text(
"Enséñales a gestionar su dinero recargando su reloj",
),
TextField(
decoration: InputDecoration(
labelText: "Cantidad de dinero de la paga",
hintText: "0€",
border: OutlineInputBorder(),
),
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
),
Text("Cantidad mínima: 10€"),
Text(
"Por seguridad sólo se puede disponer de un máximo de 150€ por wallet",
),
Text("Método de ingreso"),
Row(
spacing: 20,
children: [
OutlinedButton(
onPressed: () => {},
child: SvgPicture.asset(
"assets/images/ui/visa.svg",
),
),
OutlinedButton(
onPressed: () => {},
child: SvgPicture.asset(
"assets/images/ui/paypal.svg",
),
),
OutlinedButton(
onPressed: () => {},
child: Row(
children: [
SvgPicture.asset(
"assets/images/ui/banco.svg",
),
Text("Transferencia"),
],
),
),
],
),
Row(
children: [
Icon(Icons.lock_outline),
Text(
"EL pago en esta app es seguro y cumple la normativa europea. Sólo se usará el dinero que decidas para las huchas de tus hijos.",
),
],
),
],
),
),
],
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,18 @@
import 'package:auth/src/login/presentation/link_phone_screen.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
import 'package:navigation/navigation.dart';
class LinkPhoneBuilder {
const LinkPhoneBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
return MaterialPage<void>(
key: state.pageKey,
child: LinkPhoneScreen(navigationContract: navigationContract),
);
}
}

View File

@@ -0,0 +1,18 @@
import 'package:auth/src/login/presentation/login_screen.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
import 'package:navigation/navigation.dart';
class LoginBuilder {
const LoginBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
return MaterialPage<void>(
key: state.pageKey,
child: LoginScreen(navigationContract: navigationContract),
);
}
}

View File

@@ -0,0 +1,18 @@
import 'package:auth/src/login/presentation/phone_code_screen.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
import 'package:navigation/navigation.dart';
class PhoneCodeBuilder {
const PhoneCodeBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
return MaterialPage<void>(
key: state.pageKey,
child: PhoneCodeScreen(navigationContract: navigationContract),
);
}
}

View File

@@ -0,0 +1,75 @@
import 'package:flutter/material.dart';
import 'package:navigation/navigation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class LinkPhoneScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const LinkPhoneScreen({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
TextEditingController phoneController = TextEditingController();
// String? phone;
return Scaffold(
body: Container(
margin: EdgeInsets.all(30),
child: Expanded(
child: Center(
child: Column(
spacing: 10,
children: [
Text(
"¡Nos alegra mucho tenerte por aquí!",
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
),
Text(
"Para poder entrar de forma segura, te vamos a enviar un código al teléfono",
),
Row(
spacing: 10,
children: [
DropdownMenu(
initialSelection: "es",
dropdownMenuEntries: List<DropdownMenuEntry>.generate(3, (
int index,
) {
return DropdownMenuEntry(
labelWidget: Icon(Icons.outlined_flag),
label: "es",
value: "es",
);
}),
),
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,
),
),
],
),
SizedBox(
width: double.infinity,
child: FilledButton(
onPressed: () => navigationContract.pushTo('/phone_code'),
child: Text("Siguiente"),
),
),
],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
// import 'package:flutter_svg/flutter_svg.dart';
class LoadingGoogleScreen extends StatelessWidget {
const LoadingGoogleScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Expanded(
child: Center(
child: Column(
spacing: 50,
children: [
Spacer(flex: 8),
Text(
"Continuar con Google",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
),
CircularProgressIndicator(),
Text("Redirigiendo a Google"),
Spacer(flex: 10),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
class LoadingScreen extends StatelessWidget{
const LoadingScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Expanded(
child: Center(
child: Column(
spacing: 50,
children: [
Spacer(flex: 8),
SvgPicture.asset("assets/images/ui/logo_sf.svg"),
CircularProgressIndicator(),
Spacer(flex: 10)
],
),
)
),
);
}
}

View File

@@ -0,0 +1,107 @@
import 'package:auth/src/login/presentation/loading_google_screen.dart';
import 'package:auth/src/recover_password/presentation/restore_password_screen.dart';
import 'package:auth/src/sign_up/signup_screen.dart';
import 'package:dashboard_shell/dashboard_shell.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/navigation.dart';
// import 'package:sf_app_platform/payments/view/screens/core/dashboard_screen.dart';
// import 'package:sf_app_platform/payments/view/screens/loading_google_screen.dart';
// import 'package:sf_app_platform/payments/view/screens/restore_password/restore_password_screen.dart';
// import 'package:sf_app_platform/payments/view/screens/signup/signup_screen.dart';
class LoginScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const LoginScreen({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
bool passwordVisible = true;
return Scaffold(
body: Expanded(
child: Center(
child: Container(
margin: EdgeInsets.all(30),
child: Column(
spacing: 10,
children: [
Icon(Icons.check, color: Color(0xFF329e95), size: 50),
Text(
"¡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;
// });
},
),
),
),
TextButton(
onPressed: () =>
navigationContract.pushTo('/recover_password'),
child: Text("¿Has olvidado la contraseña?"),
),
FilledButton(
onPressed: () =>
navigationContract.pushTo('/dashboard_shell'),
child: Text("Iniciar sesión"),
),
Stack(children: [Divider(), Text("o continúa con")]),
Row(
spacing: 20,
children: [
OutlinedButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => LoadingGoogleScreen(),
),
),
child: Text("Google", semanticsLabel: "Google"),
),
OutlinedButton(
onPressed: () => {},
child: Icon(Icons.apple, semanticLabel: "Apple"),
),
],
),
Text("¿No tienes cuenta?"),
TextButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => SignupScreen()),
),
child: Text("Crear una ahora"),
),
],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,89 @@
import 'package:auth/src/login/presentation/login_screen.dart';
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) {
return FocusNode();
});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
body: Container(
margin: EdgeInsets.all(30),
child: Expanded(
child: Center(
child: Column(
spacing: 15,
children: [
Spacer(flex: 8),
Text(
"Conéctate",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
),
Text.rich(
TextSpan(
text: "Hemos enviado el código al ",
children: [
TextSpan(
// text: widget.phone,
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
Text("Introduce el código aquí"),
Row(
spacing: 20,
children: List<Widget>.generate(6, (int i) {
return Expanded(
child: TextField(
focusNode: focusNodes[i],
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: "0",
counterText: "",
border: OutlineInputBorder(),
),
maxLength: 1,
onChanged: (String value) => {
value != ""
? focusNodes[i + 1].requestFocus()
: focusNodes[i - 1].requestFocus(),
},
),
);
}),
),
FilledButton(
onPressed: () => {navigationContract.pushTo('/login')},
child: Text("Entrar"),
),
Text("¿No lo has recibido?"),
TextButton(
onPressed: () => {},
child: Text(
"Volver a intentarlo",
style: TextStyle(fontWeight: FontWeight.bold),
),
),
Spacer(flex: 10),
],
),
),
),
),
);
}
}

View File

@@ -0,0 +1,18 @@
import 'package:auth/src/onboarding/presentation/welcome_screen.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
import 'package:navigation/navigation.dart';
class OnboardingBuilder {
const OnboardingBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
return MaterialPage<void>(
key: state.pageKey,
child: WelcomeScreen(navigationContract: navigationContract),
);
}
}

View File

@@ -0,0 +1,80 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/navigation.dart';
// import 'package:sf_app_platform/payments/view/screens/link_phone_screen.dart';
// import 'package:sf_app_platform/payments/view/screens/signup/signup_screen.dart';
// import '../../../../../apps/mobile_app/lib/payments/view/screens/core/dashboard_screen.dart';
class WelcomeScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const WelcomeScreen({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
body: Center(
child: Column(
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(),
],
),
),
);
}
void jumpToNext(BuildContext context) {
// Navigator.pushReplacement(
// context,
// MaterialPageRoute(builder: (_) => LinkPhoneScreen()),
// );
return;
}
List<Widget> generateSteps() {
return [
Column(
spacing: 30,
children: [
SvgPicture.asset("assets/images/ui/bienvenida_paso1.svg"),
Text(
"Aprende a gestionar su dinero",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Text("Tu peque crea hábitos y se divierte mientras lo hace"),
],
),
Column(
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"),
],
),
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"),
],
),
];
}
}

View File

@@ -0,0 +1,92 @@
import 'package:auth/src/recover_password/presentation/new_password_screen.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// import 'package:sf_app_platform/payments/domain/ports/theme_port.dart';
// import 'package:sf_app_platform/payments/view/screens/restore_password/new_password_screen.dart';
class EmailSentScreen extends StatefulWidget {
final String email;
const EmailSentScreen({super.key, required this.email});
@override
State<StatefulWidget> createState() => EmailSentScreenState();
}
class EmailSentScreenState extends State<EmailSentScreen> {
@override
Widget build(BuildContext context) {
final theme = context.read<ThemePort>();
return Scaffold(
body: Container(
margin: EdgeInsets.all(30),
child: Center(
child: Column(
spacing: 20,
children: [
Spacer(flex: 8),
Text(
"Recuperar contraseña",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
),
Spacer(flex: 1),
Row(
spacing: 10,
children: [
Icon(
Icons.check,
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
Text(
"Correo enviado correctamente",
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
Spacer(flex: 1),
Text(
"Revisa tu email y haz clic en el enlace para crear una nueva contraseña",
),
Text(
"Si no recibes el correo en unos minutos, revisa tu carpeta de spam o pulsa \"Reenviar correo\"",
),
Row(
spacing: 10,
children: [
Expanded(
child: OutlinedButton(
onPressed: () => {},
child: Text("Reenviar correo"),
),
),
Expanded(
child: FilledButton(
onPressed: () => {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => NewPasswordScreen(),
),
),
},
style: ButtonStyle(
backgroundColor: WidgetStatePropertyAll<Color>(
theme.getColorFor(ThemeCode.buttonSecondary),
),
),
child: Text("Continuar"),
),
),
],
),
Spacer(flex: 10),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,191 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// import '../../../domain/ports/theme_port.dart';
class NewPasswordScreen extends StatefulWidget {
const NewPasswordScreen({super.key});
@override
State<StatefulWidget> createState() => NewPasswordScreenState();
}
class NewPasswordScreenState extends State<NewPasswordScreen> {
bool passwordVisible = false;
bool equalPasswords = false;
String password = "";
var securityChecks = {
"min": false,
"capital": false,
"number": false,
"special": false,
};
@override
void initState() {
passwordVisible = false;
equalPasswords = false;
String password = "";
securityChecks = {
"min": false,
"capital": false,
"number": false,
"special": false,
};
super.initState();
}
@override
Widget build(BuildContext context) {
final theme = context.read<ThemePort>();
return Scaffold(
body: Container(
margin: EdgeInsets.all(30),
child: Center(
child: Column(
spacing: 10,
children: [
Spacer(flex: 4),
Text(
"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;
});
},
),
),
onChanged: (value) => {
setState(() {
password = value;
securityChecks = checkSecurity(value);
}),
},
),
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;
});
},
),
),
onChanged: (value) => {
setState(() {
equalPasswords = password == value;
}),
},
),
Row(
children: [
securityChecks["min"]!
? Icon(
Icons.check,
color: theme.getColorFor(ThemeCode.buttonPrimary),
)
: Icon(
Icons.cancel_outlined,
color: theme.getColorFor(ThemeCode.buttonSecondary),
),
Text("Al menos 8 caracteres"),
],
),
Row(
children: [
securityChecks["capital"]!
? Icon(
Icons.check,
color: theme.getColorFor(ThemeCode.buttonPrimary),
)
: Icon(
Icons.cancel_outlined,
color: theme.getColorFor(ThemeCode.buttonSecondary),
),
Text("Una mayúscula"),
],
),
Row(
children: [
securityChecks["number"]!
? Icon(
Icons.check,
color: theme.getColorFor(ThemeCode.buttonPrimary),
)
: Icon(
Icons.cancel_outlined,
color: theme.getColorFor(ThemeCode.buttonSecondary),
),
Text("Un número"),
],
),
Row(
children: [
securityChecks["special"]!
? Icon(
Icons.check,
color: theme.getColorFor(ThemeCode.buttonPrimary),
)
: Icon(
Icons.cancel_outlined,
color: theme.getColorFor(ThemeCode.buttonSecondary),
),
Text("Un carácter especial"),
],
),
Spacer(flex: 1),
FilledButton(
onPressed: () => {},
child: Container(
width: double.infinity,
padding: EdgeInsets.all(20),
child: Text("Aceptar"),
),
),
Spacer(flex: 4),
],
),
),
),
);
}
//TODO: Extraer de la vista
Map<String, bool> checkSecurity(String value) {
Map<String, bool> checks = {};
checks["min"] = value.length >= 8;
checks["capital"] = RegExp(r'[A-Z]').hasMatch(value);
checks["number"] = RegExp(r'[0-9]').hasMatch(value);
checks["special"] = RegExp(r'[^A-Za-z0-9]').hasMatch(value);
return checks;
}
}

View File

@@ -0,0 +1,80 @@
import 'package:auth/src/recover_password/presentation/email_sent_screen.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:navigation/navigation.dart';
// import 'package:provider/provider.dart';
import 'package:navigation/navigation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// import 'package:sf_app_platform/payments/domain/ports/theme_port.dart';
// import 'package:sf_app_platform/payments/view/screens/restore_password/email_sent_screen.dart';
class RestorePasswordScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const RestorePasswordScreen({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
// ThemePort theme = context.read<ThemePort>();
final theme = ref.watch(themePortProvider);
return Scaffold(
body: Container(
margin: EdgeInsets.all(30),
child: Center(
child: Column(
spacing: 30,
children: [
Spacer(flex: 8),
Text(
"Recuperar contaseña",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
),
Text(
"Introduce tu email para enviarte un enlace de recuperación",
),
TextField(
decoration: InputDecoration(
labelText: "Correo electrónico",
hintText: "Correo electrónico",
border: OutlineInputBorder(),
),
),
Row(
spacing: 20,
children: [
Expanded(
child: OutlinedButton(
onPressed: () => {Navigator.pop(context)},
child: Text("Volver"),
),
),
Expanded(
child: FilledButton(
onPressed: () => {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => EmailSentScreen(email: ""),
),
),
},
style: ButtonStyle(
backgroundColor: WidgetStatePropertyAll<Color>(
theme.getColorFor(ThemeCode.buttonSecondary),
),
),
child: Text("Enviar"),
),
),
],
),
Spacer(flex: 10),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,18 @@
import 'package:auth/src/recover_password/presentation/restore_password_screen.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
import 'package:navigation/navigation.dart';
class RecoverPasswordBuilder {
const RecoverPasswordBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
return MaterialPage<void>(
key: state.pageKey,
child: RestorePasswordScreen(navigationContract: navigationContract),
);
}
}

View File

@@ -0,0 +1,78 @@
import 'package:auth/src/device_sign_up/add_kid_screen.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// import 'package:sf_app_platform/payments/view/screens/add_kid_screen.dart';
// import 'package:sf_app_platform/payments/view/screens/core/dashboard_screen.dart';
// import '../../../../../apps/mobile_app/lib/payments/domain/ports/theme_port.dart';
class AccountCreatedScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
ThemePort theme = context.read<ThemePort>();
final email = "usuario@example.com";
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: 10),
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.rich(
TextSpan(
text: "Hemos enviado un email de verificación a:\n",
children: [
TextSpan(
text: email,
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
Text(
"Crea la cuenta de tu peque e ingresa su \nprimera paga para utilizarla con su reloj",
),
FilledButton(
onPressed: () => {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => AddKidScreen()),
),
},
child: Text("Continuar"),
),
Spacer(flex: 8),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
class SignupAddressScreen extends StatelessWidget{
@override
Widget build(BuildContext context) {
return Column(
spacing: 30,
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())),
DropdownMenu(
dropdownMenuEntries: List<DropdownMenuEntry>.generate(3, (int index) {
return DropdownMenuEntry(value: "España", label: "España");
}),
hintText: "País",
width: double.infinity,
),
TextField(decoration: InputDecoration(hintText: "Nacionalidad", border: OutlineInputBorder()))
],
);
}
}

View File

@@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class SignupPersonalScreen extends StatelessWidget{
const SignupPersonalScreen({super.key});
@override
Widget build(BuildContext context) {
return Column(
spacing: 30,
children: [
Text("Datos personales"),
Text("Identifícate", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30)),
Text("Nos aseguraremos de que la cuenta está a nombre del adulto responsable"),
TextField(decoration: InputDecoration(labelText: "Nombre", hintText: "Nombre", border: OutlineInputBorder())),
TextField(decoration: InputDecoration(labelText: "Apellidos", hintText: "Apellidos", border: OutlineInputBorder())),
Row(
children: [
Expanded( child: TextField(
decoration: InputDecoration(label: Text("Fecha de nacimiento"), hintText: "DD", border: OutlineInputBorder()),
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
)),
Expanded( child: TextField(
decoration: InputDecoration(hintText: "MM", border: OutlineInputBorder()),
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
)),
Expanded( child: TextField(
decoration: InputDecoration(hintText: "AAAA", border: OutlineInputBorder()),
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
)),
],
),
DropdownMenu(
width: double.infinity,
label: Text("¿Qué familiar eres?"),
dropdownMenuEntries: [
DropdownMenuEntry(label: "Padre", value: "Padre"),
DropdownMenuEntry(label: "Madre", value: "Madre"),
DropdownMenuEntry(label: "Tutor", value: "Tutor"),
],
),
],
);
}
}

View File

@@ -0,0 +1,140 @@
import 'package:auth/src/sign_up/account_created_screen.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
// import 'package:sf_app_platform/payments/view/screens/account_created_screen.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 '../../../../../apps/mobile_app/lib/payments/domain/ports/theme_port.dart';
class SignupScreen extends ConsumerWidget {
SignupScreen({super.key});
int currentStep = 0;
@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(),
),
),
),
),
),
);
}
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),
),
title: const Text(""),
content: SignupPersonalScreen(),
),
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(),
),
];
}
}

View File

@@ -0,0 +1,77 @@
import 'package:flutter/material.dart';
class SignupUserScreen extends StatefulWidget{
const SignupUserScreen({super.key});
@override
State<SignupUserScreen> createState() => SignupUserScreenState();
}
class SignupUserScreenState extends State<SignupUserScreen>{
bool passwordVisible=false;
@override
Widget build(BuildContext context) {
return Column(
spacing: 30,
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");
})
),
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;
});
},
),
)
),
],
);
}
}

69
modules/auth/pubspec.yaml Normal file
View File

@@ -0,0 +1,69 @@
name: auth
# resolution: workspace
description: "A new Flutter package project."
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 0.0.1
homepage:
environment:
sdk: ^3.9.2
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
#modules dependencies go here
dashboard_shell:
path: ../../modules/dashboard_shell
#packages dependencies go here
design_system:
path: ../../packages/design_system
navigation:
path: ../../packages/navigation
#dependencies go here
flutter_svg: ^2.2.1
get_it: ^9.0.5
go_router: ^17.0.0
flutter_riverpod: ^3.0.3
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/to/asset-from-package
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/to/font-from-package

View File

@@ -0,0 +1,16 @@
# melos_managed_dependency_overrides: dashboard_shell,design_system,home,notifications,profile,sf_shared,navigation
dependency_overrides:
dashboard_shell:
path: ../dashboard_shell
design_system:
path: ../../packages/design_system
home:
path: ../home
navigation:
path: ../../packages/navigation
notifications:
path: ../notifications
profile:
path: ../profile
sf_shared:
path: ../../packages/sf_shared

31
modules/dashboard_shell/.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.flutter-plugins-dependencies
/build/
/coverage/

View File

@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
channel: "stable"
project_type: package

View File

@@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

View File

@@ -0,0 +1 @@
TODO: Add your license here.

View File

@@ -0,0 +1,39 @@
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/to/develop-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart
const like = 'sample';
```
## Additional information
TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.

View File

@@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@@ -0,0 +1,18 @@
import 'package:dashboard_shell/dashboard_shell.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
import 'package:navigation/navigation.dart';
class DashboardBuilder {
const DashboardBuilder();
Widget build(BuildContext context, StatefulNavigationShell navShell) {
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
return DashboardScreen(
navigationShell: navShell,
navigationContract: navigationContract,
);
}
}

View File

@@ -0,0 +1,2 @@
export 'src/presentation/dashboard_screen.dart';
export 'dashboard_builder.dart';

View File

@@ -0,0 +1,59 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:home/home.dart';
import 'package:notifications/notifications.dart';
import 'package:profile/profile.dart';
// import 'package:provider/provider.dart';
import 'package:navigation/navigation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class DashboardScreen extends ConsumerWidget {
final NavigationContract navigationContract;
DashboardScreen({super.key, required this.navigationContract});
int currentPageIndex = 3;
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final bodies = [
HomeScreen(),
ActivityScreen(),
AlertScreen(),
ProfileScreen(),
];
return Scaffold(
bottomNavigationBar: NavigationBar(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
onDestinationSelected: (int index) {
// setState(() {
// currentPageIndex = index;
// });
},
selectedIndex: currentPageIndex,
destinations: [
NavigationDestination(
icon: Icon(Icons.home_outlined),
label: "Inicio",
),
NavigationDestination(
icon: Icon(Icons.watch_outlined),
label: "Movimientos",
),
NavigationDestination(
icon: Icon(Icons.notifications_outlined),
label: "Alertas",
),
NavigationDestination(
icon: Icon(Icons.person_outline_outlined),
label: "Mi perfil",
),
],
),
body: bodies[currentPageIndex],
);
}
}

View File

@@ -0,0 +1,73 @@
name: dashboard_shell
resoluction: workspace
description: "A new Flutter package project."
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 0.0.1
homepage:
environment:
sdk: ^3.9.2
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
#modules dependencies go here
home:
path: ../../modules/home
notifications:
path: ../../modules/notifications
profile:
path: ../../modules/profile
#packages dependencies go here
design_system:
path: ../../packages/design_system
#dependencies go here
navigation: ^0.0.1
get_it: ^9.0.5
go_router: ^17.0.0
flutter_riverpod: ^3.0.3
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/to/asset-from-package
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/to/font-from-package

View File

@@ -0,0 +1,16 @@
# melos_managed_dependency_overrides: auth,design_system,home,notifications,profile,sf_shared,navigation
dependency_overrides:
auth:
path: ../auth
design_system:
path: ../../packages/design_system
home:
path: ../home
navigation:
path: ../../packages/navigation
notifications:
path: ../notifications
profile:
path: ../profile
sf_shared:
path: ../../packages/sf_shared

31
modules/home/.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.flutter-plugins-dependencies
/build/
/coverage/

10
modules/home/.metadata Normal file
View File

@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
channel: "stable"
project_type: package

View File

@@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

1
modules/home/LICENSE Normal file
View File

@@ -0,0 +1 @@
TODO: Add your license here.

39
modules/home/README.md Normal file
View File

@@ -0,0 +1,39 @@
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/to/develop-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart
const like = 'sample';
```
## Additional information
TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.

View File

@@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@@ -0,0 +1 @@
export 'src/presentation/home_screen.dart';

View File

@@ -0,0 +1,193 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class DepositScreen extends ConsumerWidget {
final Kid kid;
DepositScreen({super.key, required this.kid});
String reason = "other";
bool program = false;
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
return WalletManagementLayout(
kid: kid,
footer: Container(
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
padding: EdgeInsets.all(10),
child: Column(
children: [
FilledButton(
onPressed: () => {},
child: Container(
width: double.infinity,
padding: EdgeInsets.all(20),
child: Center(child: Text("Añadir dinero")),
),
),
TextButton(
onPressed: () => Navigator.pop(context),
child: Text("Cancelar"),
),
],
),
),
children: [
Container(
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
padding: EdgeInsets.all(10),
child: Column(
spacing: 10,
children: [
Text(
"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],
),
Align(
alignment: Alignment.topLeft,
child: Text("Saldo total disponible después: 30 €"),
),
],
),
),
Container(
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
padding: EdgeInsets.all(10),
child: Column(
spacing: 10,
children: [
Text(
"Motivo",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Text("Este dato aparecerá en el reloj del peque"),
CheckboxListTile(
title: Text('Paga semanal'),
controlAffinity: ListTileControlAffinity.leading,
value: reason == "weekly",
onChanged: (value) {
// setState(() {
// reason = "weekly";
// });
},
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
),
CheckboxListTile(
title: Text('Objetivo semanal cumplido'),
controlAffinity: ListTileControlAffinity.leading,
value: reason == "goal",
onChanged: (value) {
// setState(() {
// reason = "goal";
// });
},
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
),
CheckboxListTile(
title: Text('Gastos extraordinarios'),
controlAffinity: ListTileControlAffinity.leading,
value: reason == "extraordinary",
onChanged: (value) {
// setState(() {
// reason = "extraordinary";
// });
},
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
),
CheckboxListTile(
title: Text('Otro'),
controlAffinity: ListTileControlAffinity.leading,
value: reason == "other",
onChanged: (value) {
// setState(() {
// reason = "other";
// });
},
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(),
),
),
Align(
alignment: Alignment.topLeft,
child: Text("Máximo 150 caracteres"),
),
],
),
),
Container(
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
padding: EdgeInsets.all(10),
child: Column(
spacing: 10,
children: [
Text(
"Cuándo se envía el dinero",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Text("Este dato aparecerá en el reloj del peque"),
CheckboxListTile(
title: Text('Ahora'),
controlAffinity: ListTileControlAffinity.leading,
value: program == false,
onChanged: (value) {
// setState(() {
// program = false;
// });
},
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
),
CheckboxListTile(
title: Text('Programar'),
controlAffinity: ListTileControlAffinity.leading,
value: program == true,
onChanged: (value) {
// setState(() {
// program = true;
// });
},
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
),
if (program) TextField(),
],
),
),
],
);
}
}

View File

@@ -0,0 +1,274 @@
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:sf_shared/sf_shared.dart';
import 'package:flutter_riverpod/flutter_riverpod.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),
];
late final double available = double.parse(
kids.fold(total, (t, e) => t - e.balance).toStringAsFixed(2),
);
HomeScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
return SafeArea(
child: SingleChildScrollView(
child: Container(
color: theme.getColorFor(ThemeCode.backgroundSecondary),
margin: EdgeInsets.all(30),
child: Column(
children: [
Align(
alignment: Alignment.topLeft,
child: Text.rich(
TextSpan(
text: "Hola, ",
style: TextStyle(fontSize: 25),
children: <TextSpan>[
TextSpan(
text: name,
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
),
walletsList(context, kids, ref),
Align(
alignment: Alignment.topLeft,
child: TextButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => CreateProfileScreen()),
),
child: Text(
"+ Añadir otro peque",
style: TextStyle(
fontWeight: FontWeight.bold,
color: theme.getColorFor(ThemeCode.textPrimary),
),
),
),
),
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")),
],
),
),
DepositBlock(max: 150 - total),
],
),
),
),
);
}
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"),
),
),
],
),
],
),
),
),
);
}),
);
}
}

View File

@@ -0,0 +1,308 @@
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: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';
class KidWalletScreen extends ConsumerWidget {
final Kid kid;
const KidWalletScreen({super.key, required this.kid});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
body: Stack(
children: [
DecoratedBox(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(30)),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: theme.getCardColorFor(0),
),
),
child: SizedBox(width: double.infinity, height: 300),
),
Container(
margin: EdgeInsets.symmetric(vertical: 50, horizontal: 20),
child: Column(
spacing: 15,
children: [
Row(
spacing: 7,
children: [
IconButton(
onPressed: () => Navigator.pop(context),
icon: Icon(
Icons.arrow_back_ios_new_outlined,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
),
SizedBox(
height: 50,
child: SvgPicture.asset("assets/images/ui/face.svg"),
),
Text(
kid.name,
style: TextStyle(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
Spacer(),
SizedBox(
height: 30,
child: SvgPicture.asset("assets/images/ui/face.svg"),
),
],
),
MoneyText(
text: "${kid.balance.toString()}",
size: 60,
resize: true,
color: theme.getColorFor(ThemeCode.textSecondary),
),
Text(
"Saldo disponible",
style: TextStyle(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
),
LinearProgressIndicator(
value: 0.7,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
backgroundColor: theme
.getColorFor(ThemeCode.backgroundPrimary)
.withAlpha(0x4C),
minHeight: 10,
borderRadius: BorderRadius.all(Radius.circular(5)),
),
Container(
padding: EdgeInsets.all(10),
margin: EdgeInsets.only(top: 30),
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Expanded(
child: Center(
child: Row(
spacing: 10,
children: [
TextButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => DepositScreen(kid: kid),
),
),
child: Column(
spacing: 10,
children: [
Icon(
Icons.add_circle_outline,
color: theme.getColorFor(
ThemeCode.textPrimary,
),
),
Text(
"Añadir",
style: TextStyle(
color: theme.getColorFor(
ThemeCode.textPrimary,
),
),
),
],
),
),
Spacer(),
TextButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => WageScreen(kid: kid),
),
),
child: Column(
spacing: 10,
children: [
Icon(
Icons.account_balance_wallet_outlined,
color: theme.getColorFor(
ThemeCode.textPrimary,
),
),
Text(
"Paga",
style: TextStyle(
color: theme.getColorFor(
ThemeCode.textPrimary,
),
),
),
],
),
),
Spacer(),
TextButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => LimitsScreen(kid: kid),
),
),
child: Column(
spacing: 10,
children: [
Icon(
Icons.list_alt_outlined,
color: theme.getColorFor(
ThemeCode.textPrimary,
),
),
Text(
"Límites",
style: TextStyle(
color: theme.getColorFor(
ThemeCode.textPrimary,
),
),
),
],
),
),
Spacer(),
TextButton(
onPressed: () => {},
child: Column(
spacing: 10,
children: [
Icon(
Icons.emoji_events_outlined,
color: theme.getColorFor(
ThemeCode.textPrimary,
),
),
Text(
"Metas",
style: TextStyle(
color: theme.getColorFor(
ThemeCode.textPrimary,
),
),
),
],
),
),
],
),
),
),
),
Container(
padding: EdgeInsets.all(15),
height: 400,
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Column(
children: [
Text("Últimos movimientos"),
activityList(context, theme),
TextButton(onPressed: () => {}, child: Text("Ver todos")),
],
),
),
],
),
),
],
),
);
}
Widget activityList(BuildContext context, theme) {
final activity = [
{
"date": "10/05",
"payments": [1, 2, 3],
},
{
"date": "10/04",
"payments": [1, 2],
},
{
"date": "10/02",
"payments": [1, 2, 3, 4],
},
];
return Expanded(
child: ListView(
children: List<Widget>.generate(activity.length, (int index) {
return Column(
spacing: 20,
children: [
Text(activity[index]["date"].toString()),
Column(
spacing: 15,
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,
children: [
Container(
padding: EdgeInsets.all(9),
decoration: BoxDecoration(
color: theme.getColorFor(
ThemeCode.backgroundTertiary,
),
borderRadius: BorderRadius.all(Radius.circular(16)),
),
child: Icon(
Icons.local_pizza_outlined,
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
),
Column(
children: [
Text(
"Vips",
style: TextStyle(fontWeight: FontWeight.bold),
),
Text("20:15"),
],
),
Spacer(),
MoneyText(
text: "5.1€",
size: 20,
resize: true,
color: theme.getColorFor(ThemeCode.textPrimary),
),
],
);
},
),
),
],
);
}),
),
);
}
}

View File

@@ -0,0 +1,156 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class LimitsScreen extends ConsumerStatefulWidget {
final Kid kid;
const LimitsScreen({super.key, required this.kid});
@override
ConsumerState<LimitsScreen> createState() => LimitsScreenState();
}
class LimitsScreenState extends ConsumerState<LimitsScreen> {
late List dailyLimits;
late List timeLimits;
late List conditions;
late List blocks;
@override
void initState() {
super.initState();
dailyLimits = [
{"title": "Diario L-V", "limit": "5", "edit": false},
{"title": "Fines de semana", "limit": "8", "edit": false},
{"title": "Semanal", "limit": "30", "edit": false},
{"title": "Mensual", "limit": "1200", "edit": false},
];
timeLimits = [
{
"title": "Lunes a Viernes",
"start": "08:00",
"end": "20:00",
"edit": false,
},
{
"title": "Fines de semana",
"start": "10:00",
"end": "21:00",
"edit": false,
},
{"title": "Vacaciones", "start": "09:00", "end": "22:00", "edit": false},
];
conditions = [
{"title": "Alimentación", "limit": "10", "edit": false},
{"title": "Transporte", "limit": "10", "edit": false},
{"title": "Alimentación", "limit": "10", "edit": false},
];
blocks = [];
}
@override
Widget build(BuildContext context) {
final theme = ref.watch(themePortProvider);
return WalletManagementLayout(
kid: widget.kid,
footer: Column(
children: [
FilledButton(
onPressed: () => {},
child: SizedBox(
width: double.infinity,
child: Center(child: Text("Guardar límites")),
),
),
],
),
children: [
Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
child: Column(
spacing: 10,
children: [
Text(
"Pon límite de gastos",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Text("Libertad para ellos, tranquilidad para ti"),
...List<Widget>.generate(dailyLimits.length, (int index) {
return Column(
children: [
Row(
children: [
Text(
"${dailyLimits[index]["title"]}: ${dailyLimits[index]["limit"]}",
),
Spacer(),
TextButton(
onPressed: () => {
setState(() {
dailyLimits[index]["edit"] =
!dailyLimits[index]["edit"];
}),
},
child: Text("Editar"),
),
],
),
if (dailyLimits[index]["edit"]) TextField(),
],
);
}),
],
),
),
Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
child: Column(
spacing: 10,
children: [
Text(
"Horarios permitidos",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Text("Controla cuándo pueden comprar"),
...List<Widget>.generate(timeLimits.length, (int index) {
return Column(
children: [
Row(
children: [
Text(
"${timeLimits[index]["title"]}: ${timeLimits[index]["start"]} - ${timeLimits[index]["end"]}",
),
Spacer(),
TextButton(
onPressed: () => {
setState(() {
timeLimits[index]["edit"] =
!timeLimits[index]["edit"];
}),
},
child: Text("Editar"),
),
],
),
if (timeLimits[index]["edit"]) TextField(),
],
);
}),
],
),
),
],
);
}
}

View File

@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
class MoneyText extends StatelessWidget {
final String text;
final double size;
final bool resize;
final Color color;
const MoneyText({super.key, required this.text, required this.size, required this.resize, required this.color});
@override
Widget build(BuildContext context) {
final units = text.split(".")[0];
final cents = ",${text.split(".")[1]}";
return Text.rich(TextSpan(
text: units,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: size,
color: color
),
children: [
TextSpan(
text: cents,
style: TextStyle(
fontWeight: FontWeight.normal,
fontSize: resize ? size/2 : size
)
)
]
));
}
}

View File

@@ -0,0 +1,228 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class WageScreen extends ConsumerWidget {
final Kid kid;
WageScreen({super.key, required this.kid});
String frequence = "weekly";
var conditions = {
"weeklyLimits": false,
"incidences": false,
"holidays": false,
};
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
return WalletManagementLayout(
kid: kid,
footer: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
child: Column(
spacing: 10,
children: [
FilledButton(
onPressed: () => {},
child: Container(
width: double.infinity,
padding: EdgeInsets.all(20),
child: Center(child: Text("Activar paga automática")),
),
),
TextButton(onPressed: () => {}, child: Text("Cancelar")),
],
),
),
children: [
Container(
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
padding: EdgeInsets.all(10),
child: Column(
spacing: 10,
children: [
Text(
"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],
),
Text("Saldo total disponible después: 30 €"),
],
),
),
Container(
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
padding: EdgeInsets.all(10),
child: Column(
spacing: 10,
children: [
Text(
"Frecuencia",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Text("Cuándo se envía el dinero"),
CheckboxListTile(
title: Text('Semanal'),
controlAffinity: ListTileControlAffinity.leading,
value: frequence == "weekly",
onChanged: (value) {
// setState(() {
// frequence = "weekly";
// });
},
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
),
CheckboxListTile(
title: Text('Cada dos semanas'),
controlAffinity: ListTileControlAffinity.leading,
value: frequence == "biweekly",
onChanged: (value) {
// setState(() {
// frequence = "biweekly";
// });
},
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
),
CheckboxListTile(
title: Text('Mensual'),
controlAffinity: ListTileControlAffinity.leading,
value: frequence == "monthly",
onChanged: (value) {
// setState(() {
// frequence = "monthly";
// });
},
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
),
Container(
width: double.infinity,
child: DropdownMenu(
label: Text("Día de la semana"),
initialSelection: "Domingo",
dropdownMenuEntries: List<DropdownMenuEntry>.generate(7, (
int index,
) {
final days = [
"Lunes",
"Martes",
"Miércoles",
"Jueves",
"Viernes",
"Sábado",
"Domingo",
];
return DropdownMenuEntry(
value: days[index],
label: days[index],
);
}),
),
),
DropdownMenu(
label: Text("Hora del día"),
initialSelection: 9,
dropdownMenuEntries: List<DropdownMenuEntry>.generate(24, (
int index,
) {
return DropdownMenuEntry(value: index, label: "$index:00");
}),
),
TextField(
minLines: 3,
maxLines: 3,
maxLength: 150,
decoration: InputDecoration(
labelText:
"Escribir mensaje a ${kid.name} del motivo del ingreso",
hintText: "Escribe tu mensaje",
border: OutlineInputBorder(),
),
),
Align(
alignment: Alignment.topLeft,
child: Text("Máximo 150 caracteres"),
),
],
),
),
Container(
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
padding: EdgeInsets.all(10),
child: Column(
spacing: 10,
children: [
Text(
"Condiciones",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Text("Este dato aparecerá en el reloj del peque"),
CheckboxListTile(
title: Text('Sólo si cumple límites semanales'),
controlAffinity: ListTileControlAffinity.leading,
value: conditions["weeklyLimits"],
onChanged: (value) {
// setState(() {
// conditions["weeklyLimits"] = !conditions["weeklyLimits"]!;
// });
},
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
),
CheckboxListTile(
title: Text('Sólo si no ha tenido incidencias'),
controlAffinity: ListTileControlAffinity.leading,
value: conditions["incidences"],
onChanged: (value) {
// setState(() {
// conditions["incidences"] = !conditions["incidences"]!;
// });
},
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
),
CheckboxListTile(
title: Text('Pausar durante vacaciones'),
controlAffinity: ListTileControlAffinity.leading,
value: conditions["holidays"],
onChanged: (value) {
// setState(() {
// conditions["holidays"] = !conditions["holidays"]!;
// });
},
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
),
],
),
),
],
);
}
}

70
modules/home/pubspec.yaml Normal file
View File

@@ -0,0 +1,70 @@
name: home
# resolution: workspace
description: "A new Flutter package project."
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 0.0.1
homepage:
environment:
sdk: ^3.9.2
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
#modules dependencies go here
auth:
path: ../../modules/auth
notifications:
path: ../../modules/notifications
#packages dependencies go here
design_system:
path: ../../packages/design_system
sf_shared:
path: ../../packages/sf_shared
#dependencies go here
flutter_svg: ^2.2.1
flutter_riverpod: ^3.0.3
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/to/asset-from-package
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/to/font-from-package

View File

@@ -0,0 +1,16 @@
# melos_managed_dependency_overrides: auth,dashboard_shell,design_system,notifications,profile,sf_shared,navigation
dependency_overrides:
auth:
path: ../auth
dashboard_shell:
path: ../dashboard_shell
design_system:
path: ../../packages/design_system
navigation:
path: ../../packages/navigation
notifications:
path: ../notifications
profile:
path: ../profile
sf_shared:
path: ../../packages/sf_shared

31
modules/notifications/.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.flutter-plugins-dependencies
/build/
/coverage/

View File

@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
channel: "stable"
project_type: package

View File

@@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

View File

@@ -0,0 +1 @@
TODO: Add your license here.

View File

@@ -0,0 +1,39 @@
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/to/develop-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart
const like = 'sample';
```
## Additional information
TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.

View File

@@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@@ -0,0 +1,3 @@
export 'src/presentation/alert_screen.dart';
export 'src/presentation/activity_screen.dart';
export 'src/core/activity_list.dart';

View File

@@ -0,0 +1,106 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class ActivityList extends ConsumerStatefulWidget {
final List activity;
bool edit = false;
ActivityList({super.key, required this.activity, required this.edit});
@override
ConsumerState<ActivityList> createState() => ActivityListState();
}
class ActivityListState extends ConsumerState<ActivityList> {
late List<bool> values;
@override
void initState() {
values = List<bool>.generate(widget.activity.length, (_) => false);
super.initState();
}
@override
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",
};
return Column(
spacing: 20,
children: List<Widget>.generate(widget.activity.length, (int index) {
var logItem = Container(
padding: EdgeInsets.all(20),
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),
),
),
child: Column(
spacing: 15,
children: [
Row(
children: [
Icon(
icons[widget.activity[index]["type"]],
color: colors[index % colors.length],
),
Text(
titles[widget.activity[index]["type"]]!,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
Spacer(),
Text("14/01/2005"),
],
),
Align(
alignment: Alignment.topLeft,
child: Text("Ana ya tiene su paga de 5€ en el reloj"),
),
],
),
);
if (widget.edit) {
return Row(
children: [
Checkbox(
value: values[index],
onChanged: (value) => {
setState(() {
values[index] = !values[index];
}),
},
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
semanticLabel: "Eliminar",
),
Expanded(child: logItem),
],
);
} else {
return logItem;
}
}),
);
}
}

View File

@@ -0,0 +1,57 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:notifications/src/core/activity_list.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_shared/sf_shared.dart';
class ActivityScreen extends ConsumerWidget {
ActivityScreen({super.key});
final activity = [
{"type": "goal"},
{"type": "wage", "amount": 5},
{"type": "lock"},
{"type": "lock"},
];
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final content = [
Text(
"Movimientos recientes",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
),
Row(
spacing: 20,
children: [
FilledButton(onPressed: () => {}, child: Text("Hoy")),
Spacer(),
TextButton(onPressed: () => {}, child: Text("Última semana")),
TextButton(onPressed: () => {}, child: Text("Mes")),
],
),
SizedBox(height: 200, child: LineGraph()),
ActivityList(activity: activity, edit: false),
];
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
body: Container(
margin: EdgeInsets.fromLTRB(30, 30, 30, 0),
child: Center(
child: ListView.separated(
itemBuilder: (BuildContext context, int index) {
return content[index];
},
separatorBuilder: (BuildContext context, int index) {
return Divider(color: Colors.transparent, height: 30);
},
itemCount: content.length,
),
),
),
);
}
}

View File

@@ -0,0 +1,56 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:notifications/src/core/activity_list.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class AlertScreen extends ConsumerStatefulWidget {
const AlertScreen({super.key});
@override
ConsumerState<AlertScreen> createState() => AlertScreenState();
}
class AlertScreenState extends ConsumerState<AlertScreen> {
final activity = [
{"type": "goal"},
{"type": "wage", "amount": 5},
{"type": "lock"},
{"type": "lock"},
];
bool edit = false;
@override
void initState() {
edit = false;
super.initState();
}
@override
Widget build(BuildContext context) {
final theme = ref.watch(themePortProvider);
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
body: Container(
margin: EdgeInsets.all(30),
child: Column(
children: [
Row(
children: [
Text("Alertas"),
Spacer(),
TextButton(
onPressed: () => setState(() {
edit = !edit;
}),
child: Text("Editar"),
),
],
),
ActivityList(activity: activity, edit: edit),
],
),
),
);
}
}

View File

@@ -0,0 +1,65 @@
name: notifications
# resolution: workspace
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
description: "A new Flutter package project."
version: 0.0.1
homepage:
environment:
sdk: ^3.9.2
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
#modules dependencies go here
design_system:
path: ../../packages/design_system
#packages dependencies go here
sf_shared:
path: ../../packages/sf_shared
#dependencies go here
flutter_riverpod: ^3.0.3
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/to/asset-from-package
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/to/font-from-package

View File

@@ -0,0 +1,6 @@
# melos_managed_dependency_overrides: design_system,sf_shared
dependency_overrides:
design_system:
path: ../../packages/design_system
sf_shared:
path: ../../packages/sf_shared

31
modules/profile/.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.flutter-plugins-dependencies
/build/
/coverage/

10
modules/profile/.metadata Normal file
View File

@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
channel: "stable"
project_type: package

View File

@@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

1
modules/profile/LICENSE Normal file
View File

@@ -0,0 +1 @@
TODO: Add your license here.

39
modules/profile/README.md Normal file
View File

@@ -0,0 +1,39 @@
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/to/develop-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart
const like = 'sample';
```
## Additional information
TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.

View File

@@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@@ -0,0 +1 @@
export 'src/presentation/profile_screen.dart';

View File

@@ -0,0 +1,135 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:notifications/notifications.dart';
import 'package:profile/src/settings_screen.dart';
import 'package:sf_shared/sf_shared.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class ProfileScreen extends ConsumerWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final activity = [
{"type": "goal"},
{"type": "wage", "amount": 5},
{"type": "lock"},
{"type": "lock"},
];
final name = "Juan";
final total = 95.03;
final available = 44.09;
final content = [
Row(
children: [
Text(
name,
style: TextStyle(
color: theme.getColorFor(ThemeCode.textSecondary),
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
Spacer(),
TextButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => SettingsScreen()),
),
child: Text(
"Ajustes de la cuenta",
style: TextStyle(
color: theme.getColorFor(ThemeCode.textSecondary),
),
),
),
],
),
Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: Column(
spacing: 5,
children: [
Row(
children: [
Text("Wallet", style: TextStyle(fontWeight: FontWeight.bold)),
Spacer(),
Text("$total"),
],
),
Stack(
children: [
LinearProgressIndicator(
value: available / total,
minHeight: 70,
borderRadius: BorderRadius.all(Radius.circular(16)),
),
FractionallySizedBox(
widthFactor: available / total,
child: Container(
padding: EdgeInsets.symmetric(vertical: 20),
child: Center(
child: Text(
"$available",
style: TextStyle(
color: theme.getColorFor(ThemeCode.textSecondary),
fontSize: 20,
),
),
),
),
),
],
),
Center(child: Text("Disponible")),
],
),
),
SizedBox(height: 200, child: LineGraph()),
DepositBlock(max: 150 - total),
Row(),
ActivityList(activity: activity, edit: false),
];
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
body: Stack(
children: [
DecoratedBox(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(30)),
color: Color(0xFF4B4B4B),
),
child: SizedBox(width: double.infinity, height: 200),
),
Column(
children: [
Expanded(
child: Container(
margin: EdgeInsets.fromLTRB(20, 20, 20, 0),
child: ListView.separated(
itemBuilder: (BuildContext context, int index) {
return content[index];
},
separatorBuilder: (BuildContext context, int index) {
return Divider(color: Colors.transparent, height: 20);
},
itemCount: content.length,
),
),
),
],
),
],
),
);
}
}

View File

@@ -0,0 +1,360 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class SettingsScreen extends ConsumerWidget {
const SettingsScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final name = "Juan";
final balance = 50;
final fullName = "Juan Pérez Cruz";
final birthDate = "08/03/1976";
final relation = "Padre";
final address = "Calle Gran Vía 30 6º, 28013";
final country = "España";
final nationality = "Español";
final email = "juanpcruz@gmail.com";
final phone = "123456789";
final content = [
Center(
child: Column(
children: [
Text(
"Ajustes de la cuenta",
style: TextStyle(
fontSize: 30,
color: theme.getColorFor(ThemeCode.textSecondary),
),
),
Text(
"Saldo: $balance",
style: TextStyle(
color: theme.getColorFor(ThemeCode.textSecondary),
),
),
],
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
child: Column(
children: [
Row(
spacing: 10,
children: [
Text(
name,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Spacer(),
TextButton(onPressed: () => {}, child: Text("Editar wallet")),
Icon(Icons.attach_money),
],
),
Text(relation),
],
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
child: Column(
children: [
Row(
spacing: 10,
children: [
Text(
"Datos personales",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Spacer(),
TextButton(onPressed: () => {}, child: Text("Editar")),
],
),
Text.rich(
TextSpan(
text: "Nombre: ",
style: TextStyle(fontWeight: FontWeight.bold),
children: [
TextSpan(
text: fullName,
style: TextStyle(fontWeight: FontWeight.normal),
),
],
),
),
Text.rich(
TextSpan(
text: "Fecha de nacimiento: ",
style: TextStyle(fontWeight: FontWeight.bold),
children: [
TextSpan(
text: birthDate,
style: TextStyle(fontWeight: FontWeight.normal),
),
],
),
),
Text.rich(
TextSpan(
text: "Familiar: ",
style: TextStyle(fontWeight: FontWeight.bold),
children: [
TextSpan(
text: relation,
style: TextStyle(fontWeight: FontWeight.normal),
),
],
),
),
],
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
child: Column(
children: [
Row(
spacing: 10,
children: [
Text(
"Dirección",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Spacer(),
TextButton(onPressed: () => {}, child: Text("Editar")),
],
),
Text.rich(
TextSpan(
text: "Dirección: ",
style: TextStyle(fontWeight: FontWeight.bold),
children: [
TextSpan(
text: address,
style: TextStyle(fontWeight: FontWeight.normal),
),
],
),
),
Text.rich(
TextSpan(
text: "País: ",
style: TextStyle(fontWeight: FontWeight.bold),
children: [
TextSpan(
text: country,
style: TextStyle(fontWeight: FontWeight.normal),
),
],
),
),
Text.rich(
TextSpan(
text: "Nacionalidad: ",
style: TextStyle(fontWeight: FontWeight.bold),
children: [
TextSpan(
text: nationality,
style: TextStyle(fontWeight: FontWeight.normal),
),
],
),
),
],
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
child: Column(
children: [
Row(
spacing: 10,
children: [
Text(
"Usuario",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Spacer(),
TextButton(onPressed: () => {}, child: Text("Editar")),
],
),
Text.rich(
TextSpan(
text: "Correo: ",
style: TextStyle(fontWeight: FontWeight.bold),
children: [
TextSpan(
text: email,
style: TextStyle(fontWeight: FontWeight.normal),
),
],
),
),
Text.rich(
TextSpan(
text: "Teléfono: ",
style: TextStyle(fontWeight: FontWeight.bold),
children: [
TextSpan(
text: phone,
style: TextStyle(fontWeight: FontWeight.normal),
),
],
),
),
],
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
child: Row(
spacing: 10,
children: [
Text(
"Cambio de contraseña",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Spacer(),
TextButton(onPressed: () => {}, child: Text("Editar")),
],
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
child: Column(
children: [
Row(
spacing: 10,
children: [
Text(
"Método de pago",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Spacer(),
TextButton(onPressed: () => {}, child: Text("Editar")),
],
),
Text("Puedes cambiar el método de pago en cualquier momento"),
],
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: theme.getColorFor(ThemeCode.backgroundTertiary),
),
child: Column(
children: [
Row(
spacing: 10,
children: [
Text(
"Plan anual",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Spacer(),
TextButton(onPressed: () => {}, child: Text("Cambiar Plan")),
],
),
Text("Sin permanencia"),
Text("Llamadas y datos ilimitados"),
Text("2 meses gratis"),
],
),
),
TextButton(onPressed: () => {}, child: Text("Contáctanos")),
TextButton(onPressed: () => {}, child: Text("Preguntas frecuentes")),
];
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundSecondary),
body: Stack(
children: [
DecoratedBox(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(30)),
color: Color(0xFF4B4B4B),
),
child: SizedBox(width: double.infinity, height: 200),
),
Column(
children: [
Expanded(
child: Container(
margin: EdgeInsets.all(20),
child: ListView.separated(
itemBuilder: (BuildContext context, int index) {
return content[index];
},
separatorBuilder: (BuildContext context, int index) {
return Divider(color: Colors.transparent, height: 20);
},
itemCount: content.length,
),
),
),
Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
),
child: Column(
children: [
FilledButton(
onPressed: () => {},
child: Container(
width: double.infinity,
padding: EdgeInsets.all(20),
child: Center(child: Text("Guardar cambios")),
),
),
TextButton(
onPressed: () => Navigator.pop(context),
child: Text("Cancelar"),
),
],
),
),
],
),
],
),
);
}
}

View File

@@ -0,0 +1,67 @@
name: profile
# resolution: workspace
description: "A new Flutter package project."
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 0.0.1
homepage:
environment:
sdk: ^3.9.2
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
#modules dependencies go here
notifications:
path: ../../modules/notifications
#packages dependencies go here
design_system:
path: ../../packages/design_system
sf_shared:
path: ../../packages/sf_shared
#dependencies go here
flutter_riverpod: ^3.0.3
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# To add assets to your package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/to/asset-from-package
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
# To add custom fonts to your package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/to/font-from-package

View File

@@ -0,0 +1,8 @@
# melos_managed_dependency_overrides: design_system,notifications,sf_shared
dependency_overrides:
design_system:
path: ../../packages/design_system
notifications:
path: ../notifications
sf_shared:
path: ../../packages/sf_shared