Compare commits

..

34 Commits

Author SHA1 Message Date
8754884544 sign up parameters fixed change document, documentType and country code 2026-01-05 10:03:18 +01:00
0e3fd8e6d5 sign up feature second version done 2025-12-26 14:47:08 +01:00
4c46b2f498 sign up feature first version done 2025-12-26 00:29:15 +01:00
0b2f1ff869 Added two factor login, otp code widget and intl 2025-12-19 13:47:31 +01:00
AlcalaJulian
076951af24 added login and 2fa login endpoints, providers and states 2025-12-10 14:56:36 +01:00
f8a3b038b6 Merge pull request 'Added linkPhone use cases, auth repository providers and data folder' (#9) from feature/auth-link-phone into develop
Reviewed-on: #9
2025-12-09 09:10:01 +00:00
AlcalaJulian
0f30c7f422 added linkPhone use cases, auth repository providers and data folder 2025-12-09 10:08:36 +01:00
bbe77f6a8a Merge pull request 'added country_code_picker package to link phone feature' (#8) from feature/auth-link-phone into develop
Reviewed-on: #8
2025-12-04 17:22:11 +00:00
AlcalaJulian
760e94ffe9 Merge remote-tracking branch 'origin' into feature/auth-link-phone 2025-12-04 18:01:14 +01:00
AlcalaJulian
ad10ad3b59 added country_code_picker package to link phone feature 2025-12-04 17:32:42 +01:00
cbc991b2fd Merge pull request 'feat(auth,sf_infrastructure): register QuestiaApi/Dio and connect link_phone remote datasource' (#7) from feature/auth-link-phone into develop
Reviewed-on: #7
2025-12-04 15:13:41 +00:00
AlcalaJulian
6b3776f618 feat(auth,sf_infrastructure): register QuestiaApi/Dio and connect link_phone remote datasource 2025-12-04 16:07:02 +01:00
7bfc4039ab Merge pull request 'golden_test' (#6) from golden_test into develop
Reviewed-on: #6
2025-12-04 13:12:09 +00:00
16e3c68d1a Merge pull request 'update step indicator' (#5) from components into develop
Reviewed-on: #5
2025-12-04 13:11:46 +00:00
4869850c43 update step indicator 2025-12-04 12:57:26 +01:00
cb7ff71756 add goldens 2025-12-04 11:40:30 +01:00
99f544e24c Merge pull request 'add fonts package' (#4) from fonts into develop
Reviewed-on: #4
2025-12-04 10:19:46 +00:00
AlcalaJulian
14a925659a update font package 2025-12-04 11:19:11 +01:00
91e47f28fb add fonts package 2025-12-04 11:08:44 +01:00
AlcalaJulian
07a0fd5695 fix 2025-12-04 10:46:23 +01:00
AlcalaJulian
b823b422f0 fix 2025-12-04 10:46:10 +01:00
AlcalaJulian
a64a9a2a32 fixes 2025-12-04 10:45:10 +01:00
AlcalaJulian
9d7f940851 fixing golden test 2025-12-04 10:30:44 +01:00
14d0f1515b Merge pull request 'components' (#2) from components into develop
Reviewed-on: #2
2025-12-04 08:45:43 +00:00
1b0517344e merge develop into components
- modify onboarding screen to use step indicator component
2025-12-04 09:43:05 +01:00
ae0156f3e6 Merge remote-tracking branch 'origin/develop' into components
# Conflicts:
#	modules/auth/lib/src/onboarding/presentation/welcome_screen.dart
2025-12-04 09:01:32 +01:00
34cb4ebf69 Merge pull request 'Refactor to onboarding and add OnboardingContent, dots indicator, view model and view state' (#3) from onboarding_screen into develop
Reviewed-on: #3
2025-12-03 17:53:35 +00:00
AlcalaJulian
6c5da2d492 - refactor to onboarding and add OnboardingContent, dots indicator and view model
- Integrate i18n for onboarding texts
2025-12-03 18:37:07 +01:00
ca7c3755c2 changed savings goal goal type to int 2025-12-03 17:42:26 +01:00
a5dd644a62 merge develop into components 2025-12-03 16:43:24 +01:00
73a2c46999 Merge remote-tracking branch 'origin/develop' into components
# Conflicts:
#	apps/mobile_app/lib/navigation/app_router.dart
#	apps/mobile_app/pubspec.yaml
#	modules/auth/lib/src/device_sign_up/add_kid_screen.dart
#	modules/auth/lib/src/login/presentation/link_phone_screen.dart
#	modules/auth/lib/src/login/presentation/login_screen.dart
#	modules/auth/lib/src/login/presentation/phone_code_screen.dart
#	modules/auth/lib/src/onboarding/presentation/welcome_screen.dart
#	modules/auth/lib/src/recover_password/presentation/new_password_screen.dart
#	modules/auth/lib/src/sign_up/signup_screen.dart
#	modules/home/lib/src/presentation/deposit_screen.dart
#	modules/home/lib/src/presentation/home_screen.dart
#	modules/home/lib/src/presentation/wage_screen.dart
#	modules/notifications/lib/src/core/activity_list.dart
#	packages/design_system/lib/design_system.dart
2025-12-03 16:24:04 +01:00
62ffc9ef7c - created custom text block, dropdown,
- created extract, goals and block card screen.
- generated tests for design system components.
- updated restore password and home screens to 17/11 design.
2025-12-03 15:28:10 +01:00
11c4c5ab07 Merge pull request 'architecture_refactor' (#1) from architecture_refactor into develop
Reviewed-on: #1
2025-12-03 13:05:24 +00:00
8201bff0a7 - created snackbar, step indicator, money text, text field and progress bar components.
- created wallet balance block, wallet item and kid line chart widgets.
- restructured onboarding, signup and device signup screens into layouts and main screens.
- updated signup and kid wallet screens to 17/11 design.
2025-11-21 15:28:46 +01:00
239 changed files with 15624 additions and 2552 deletions

View File

@@ -1,3 +0,0 @@
{
"workspaceRoot": "../.."
}

View File

@@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_secure_storage","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_secure_storage","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_android-2.2.20/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"flutter_secure_storage_macos","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"flutter_secure_storage_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"flutter_secure_storage_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false}],"web":[{"name":"flutter_secure_storage_web","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2025-11-10 13:39:20.696891","version":"3.35.7","swift_package_manager_enabled":{"ios":false,"macos":false}}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_secure_storage","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"flutter_secure_storage","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_android","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_android-2.2.20/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"flutter_secure_storage_macos","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_foundation","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[{"name":"flutter_secure_storage_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_linux","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[],"dev_dependency":false}],"windows":[{"name":"flutter_secure_storage_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/","native_build":true,"dependencies":[],"dev_dependency":false},{"name":"path_provider_windows","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[],"dev_dependency":false}],"web":[{"name":"flutter_secure_storage_web","path":"/Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/","dependencies":[],"dev_dependency":false}]},"dependencyGraph":[{"name":"flutter_secure_storage","dependencies":["flutter_secure_storage_linux","flutter_secure_storage_macos","flutter_secure_storage_web","flutter_secure_storage_windows"]},{"name":"flutter_secure_storage_linux","dependencies":[]},{"name":"flutter_secure_storage_macos","dependencies":[]},{"name":"flutter_secure_storage_web","dependencies":[]},{"name":"flutter_secure_storage_windows","dependencies":["path_provider"]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2025-12-04 12:33:12.174628","version":"3.35.7","swift_package_manager_enabled":{"ios":false,"macos":false}}

2
.idea/modules.xml generated
View File

@@ -5,11 +5,13 @@
<module fileurl="file://$PROJECT_DIR$/modules/auth/melos_auth.iml" filepath="$PROJECT_DIR$/modules/auth/melos_auth.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/dashboard_shell/melos_dashboard_shell.iml" filepath="$PROJECT_DIR$/modules/dashboard_shell/melos_dashboard_shell.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/design_system/melos_design_system.iml" filepath="$PROJECT_DIR$/packages/design_system/melos_design_system.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/fonts/melos_fonts.iml" filepath="$PROJECT_DIR$/packages/fonts/melos_fonts.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/home/melos_home.iml" filepath="$PROJECT_DIR$/modules/home/melos_home.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/navigation/melos_navigation.iml" filepath="$PROJECT_DIR$/packages/navigation/melos_navigation.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/notifications/melos_notifications.iml" filepath="$PROJECT_DIR$/modules/notifications/melos_notifications.iml" />
<module fileurl="file://$PROJECT_DIR$/modules/profile/melos_profile.iml" filepath="$PROJECT_DIR$/modules/profile/melos_profile.iml" />
<module fileurl="file://$PROJECT_DIR$/apps/mobile_app/melos_sf_app_platform.iml" filepath="$PROJECT_DIR$/apps/mobile_app/melos_sf_app_platform.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/sf_infrastructure/melos_sf_infrastructure.iml" filepath="$PROJECT_DIR$/packages/sf_infrastructure/melos_sf_infrastructure.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/sf_localizations/melos_sf_localizations.iml" filepath="$PROJECT_DIR$/packages/sf_localizations/melos_sf_localizations.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/sf_shared/melos_sf_shared.iml" filepath="$PROJECT_DIR$/packages/sf_shared/melos_sf_shared.iml" />
<module fileurl="file://$PROJECT_DIR$/packages/utils/melos_utils.iml" filepath="$PROJECT_DIR$/packages/utils/melos_utils.iml" />

View File

@@ -0,0 +1,7 @@
<!-- Generated by Melos -->
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Flutter Test -&gt; 'sf_infrastructure'" type="FlutterTestConfigType" factoryName="Flutter Test">
<option name="testDir" value="$PROJECT_DIR$/packages/sf_infrastructure/test" />
<method v="2" />
</configuration>
</component>

2
apps/mobile_app/.env Normal file
View File

@@ -0,0 +1,2 @@
API_BASE_URL=https://api-neki-b2b.neki.es/gateway/api/
API_ORIGIN =https://neki-b2b.neki.es

View File

@@ -1,4 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:label="sf_payments"
android:name="${applicationName}"

View File

@@ -0,0 +1,8 @@
import 'package:flutter_dotenv/flutter_dotenv.dart';
class Env {
static String get apiBaseUrl => dotenv.env['API_BASE_URL'] ?? '';
static String get apiOrigin => dotenv.env['API_ORIGIN'] ?? '';
// static String get apiKey => dotenv.env['API_KEY'] ?? '';
}

View File

@@ -0,0 +1,12 @@
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'env.dart';
class QuestiaEnvConfig implements EnvConfig {
@override
String get apiBaseUrl => Env.apiBaseUrl;
@override
String get apiOrigin => Env.apiOrigin;
// @override
// String get apiKey => Env.apiKey;
}

View File

@@ -1,9 +1,13 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:design_system/design_system.dart';
import 'package:sf_app_platform/config/env/questia_env_config.dart';
import 'package:sf_app_platform/navigation/app_router.dart';
import 'package:navigation/navigation_module.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'package:sf_localizations/sf_localizations.dart';
Future<void> main() async {
@@ -11,7 +15,9 @@ Future<void> main() async {
navigationModule();
configureAppRouter();
themePackages();
await dotenv.load(fileName: '.env');
await configureDependencies(QuestiaEnvConfig(), log: kDebugMode);
runApp(const ProviderScope(child: PlatformApp()));
}
@@ -23,11 +29,14 @@ class PlatformApp extends ConsumerWidget {
return MaterialApp.router(
title: 'SaveFamily',
theme: ThemeData(
fontFamily: 'Stolzl',
package: 'fonts',
colorScheme: ColorScheme.fromSeed(seedColor: Color(0xFF329E95)),
),
routerConfig: appRouter,
debugShowCheckedModeBanner: false,
localizationsDelegates: const [
localizationsDelegates: [
// CountryLocalizations.getDelegate(enableLocalization: false),
SFLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,

View File

@@ -15,7 +15,7 @@ late final GoRouter appRouter;
void configureAppRouter() {
appRouter = GoRouter(
navigatorKey: rootNavigatorKey,
initialLocation: AppRoutes.login,
initialLocation: AppRoutes.onboarding,
debugLogDiagnostics: true,
routes: [
GoRoute(
@@ -23,6 +23,11 @@ void configureAppRouter() {
name: 'login',
pageBuilder: LoginBuilder().buildPage,
),
GoRoute(
path: AppRoutes.signup,
name: 'signup',
pageBuilder: SignupBuilder().buildPage,
),
GoRoute(
path: AppRoutes.onboarding,
name: 'onboarding',
@@ -30,20 +35,24 @@ void configureAppRouter() {
),
GoRoute(
path: AppRoutes.linkPhone,
name: 'link_phone',
pageBuilder: LinkPhoneBuilder().buildPage,
name: 'request_link_phone',
pageBuilder: RequestLinkPhoneBuilder().buildPage,
),
GoRoute(
path: AppRoutes.phoneCode,
name: 'phone_code',
pageBuilder: PhoneCodeBuilder().buildPage,
name: 'Verify_link_phone_code',
pageBuilder: VerifyLinkPhoneCodeBuilder().buildPage,
),
GoRoute(
path: AppRoutes.recoverPassword,
name: 'recover_password',
pageBuilder: RecoverPasswordBuilder().buildPage,
),
GoRoute(
path: AppRoutes.deviceSignup,
name: 'device_signup',
pageBuilder: DeviceSignupBuilder().buildPage,
),
StatefulShellRoute.indexedStack(
builder: (context, state, navShell) {
return DashboardBuilder().build(context, navShell);

View File

@@ -168,6 +168,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.2"
country_code_picker:
dependency: "direct main"
description:
name: country_code_picker
sha256: f0411f4833b6f98e8b7215f4fa3813bcc88e50f13925f70a170dbd36e3e447f5
url: "https://pub.dev"
source: hosted
version: "3.4.1"
coverage:
dependency: transitive
description:
@@ -214,6 +222,30 @@ packages:
relative: true
source: path
version: "0.0.1"
diacritic:
dependency: transitive
description:
name: diacritic
sha256: "12981945ec38931748836cd76f2b38773118d0baef3c68404bdfde9566147876"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
dio:
dependency: transitive
description:
name: dio
sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
url: "https://pub.dev"
source: hosted
version: "5.9.0"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
equatable:
dependency: transitive
description:
@@ -259,6 +291,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_dotenv:
dependency: "direct main"
description:
name: flutter_dotenv
sha256: d4130c4a43e0b13fefc593bc3961f2cb46e30cb79e253d4a526b1b5d24ae1ce4
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_lints:
dependency: "direct dev"
description:
@@ -284,10 +324,10 @@ packages:
dependency: "direct main"
description:
name: flutter_svg
sha256: b9c2ad5872518a27507ab432d1fb97e8813b05f0fc693f9d40fad06d073e0678
sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
version: "2.2.3"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -298,6 +338,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
fonts:
dependency: "direct main"
description:
path: "../../packages/fonts"
relative: true
source: path
version: "0.0.1"
freezed:
dependency: transitive
description:
@@ -425,6 +472,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.9.0"
json_serializable:
dependency: transitive
description:
name: json_serializable
sha256: c5b2ee75210a0f263c6c7b9eeea80553dbae96ea1bf57f02484e806a3ffdffa3
url: "https://pub.dev"
source: hosted
version: "6.11.2"
leak_tracker:
dependency: transitive
description:
@@ -590,6 +645,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.3"
sf_infrastructure:
dependency: "direct main"
description:
path: "../../packages/sf_infrastructure"
relative: true
source: path
version: "0.0.1"
sf_localizations:
dependency: "direct main"
description:
@@ -776,6 +838,14 @@ packages:
relative: true
source: path
version: "0.0.1"
uuid:
dependency: transitive
description:
name: uuid
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
url: "https://pub.dev"
source: hosted
version: "4.5.2"
vector_graphics:
dependency: transitive
description:
@@ -874,4 +944,4 @@ packages:
version: "3.1.3"
sdks:
dart: ">=3.9.2 <4.0.0"
flutter: ">=3.29.0"
flutter: ">=3.32.0"

View File

@@ -54,13 +54,19 @@ dependencies:
design_system:
path: ../../packages/design_system
sf_localizations:
path: ../../packages/sf_localizations
path: ../../packages/sf_localizations
fonts:
path: ../../packages/fonts
sf_infrastructure:
path: ../../packages/sf_infrastructure
#dependencies go here
cupertino_icons: ^1.0.8
flutter_svg: ^2.2.1
go_router_builder: ^4.1.1
build_runner: ^2.7.1
flutter_dotenv: ^6.0.0
country_code_picker: ^3.4.1
dev_dependencies:
flutter_test:
@@ -87,7 +93,7 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/ui/
- .env
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images

View File

@@ -1,4 +1,4 @@
# melos_managed_dependency_overrides: auth,dashboard_shell,design_system,home,navigation,notifications,profile,sf_shared,utils,sf_localizations
# melos_managed_dependency_overrides: auth,dashboard_shell,design_system,home,navigation,notifications,profile,sf_shared,utils,sf_localizations,fonts,sf_infrastructure
dependency_overrides:
auth:
path: ../../modules/auth
@@ -6,6 +6,8 @@ dependency_overrides:
path: ../../modules/dashboard_shell
design_system:
path: ../../packages/design_system
fonts:
path: ../../packages/fonts
home:
path: ../../modules/home
navigation:
@@ -14,6 +16,8 @@ dependency_overrides:
path: ../../modules/notifications
profile:
path: ../../modules/profile
sf_infrastructure:
path: ../../packages/sf_infrastructure
sf_localizations:
path: ../../packages/sf_localizations
sf_shared:

View File

@@ -1,28 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
//await tester.pumpWidget(const PaymentsApp(di: di));
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}

View File

@@ -1,6 +1,8 @@
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';
export 'src/features/device_sign_up/link_watch/create_profile_screen.dart';
export 'src/features/onboarding/onboarding_builder.dart';
export 'src/features/link_phone/presentation/request_phone/request_link_phone_builder.dart';
export 'src/features/link_phone/presentation/verify_code/verify_link_phone_code_builder.dart';
export 'src/features/login/login_builder.dart';
export 'src/features/recover_password/recover_password_builder.dart';
export 'src/features/device_sign_up/device_signup_builder.dart';
export 'src/features/sign_up/sign_up_builder.dart';

View File

@@ -0,0 +1,20 @@
import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart';
import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart';
abstract class AuthRemoteDatasource {
Future<void> requestPhoneCode({required String phone});
Future<void> verifyPhoneCode({required String phone, required String code});
Future<String> login({required String email, required String password});
Future<void> twoFALogin({required String token, required String code});
Future<String> signUp({required SignUpRequestEntity request});
Future<TwoFASecretEntity> generateTwoFASignUp({required String token});
Future<void> verifyTwoFACodeSignUp({
required String token,
required String code,
});
}

View File

@@ -0,0 +1,205 @@
import 'dart:convert';
import 'package:auth/src/core/data/models/sign_up_request_model.dart';
import 'package:auth/src/core/data/models/sign_up_response_model.dart';
import 'package:auth/src/core/data/models/two_fa_secret_response_model.dart';
import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart';
import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'auth_remote_datasource.dart';
class AuthRemoteDatasourceImpl implements AuthRemoteDatasource {
AuthRemoteDatasourceImpl(this._repository);
final QuestiaRepository _repository;
@override
Future<void> requestPhoneCode({required String phone}) async {
try {
await _repository.post<void>(
'/auth/link-phone/request-code',
body: <String, dynamic>{'phone': phone},
);
} on DioException catch (error) {
throw _mapDioError(
error,
defaultMessage: error.response?.data ?? 'Error to request phone code',
);
}
}
@override
Future<void> verifyPhoneCode({
required String phone,
required String code,
}) async {
try {
await _repository.post<void>(
'/auth/link-phone/verify-code',
body: <String, dynamic>{'phone': phone, 'code': code},
);
} on DioException catch (error) {
throw _mapDioError(error, defaultMessage: 'Error in verification code');
}
}
@override
Future<String> login({
required String email,
required String password,
}) async {
try {
final response = await _repository.post<Map<String, dynamic>>(
'/auth/login',
body: <String, dynamic>{'email': email, 'password': password},
);
final token = response.data!['token'];
return token;
} on DioException catch (error) {
throw _mapDioError(
error,
defaultMessage: error.message ?? 'Error in login',
);
}
}
@override
Future<String> twoFALogin({
required String token,
required String code,
}) async {
try {
final response = await _repository.post<String>(
'/auth/totp/login',
body: <String, dynamic>{
'token': token,
'code': code,
'rememberMe': true,
},
);
final data = response.data;
if (data == null || data.isEmpty) {
throw Exception('Empty response from /auth/totp/login');
}
return data;
} on DioException catch (error) {
throw _mapDioError(error, defaultMessage: 'Error in twoFALogin');
}
}
@override
Future<String> signUp({required SignUpRequestEntity request}) async {
try {
final body = request.toModel().toJson();
debugPrint(body.toString());
debugPrint(const JsonEncoder.withIndent(' ').convert(body));
final response = await _repository.post<Map<String, dynamic>>(
'/payment-profiles/signup',
body: body,
);
final data = response.data;
if (data == null || data.isEmpty) {
throw Exception('Empty response from /payment-profiles/signup');
}
final parsed = SignUpResponseModel.fromJson(data);
if (!parsed.isCreated) {
throw Exception('Sign up failed: isCreated=false');
}
final token = parsed.item.token.trim();
if (token.isEmpty) {
throw Exception('Sign up response has empty token');
}
return token;
} on DioException catch (error) {
throw _mapDioError(error, defaultMessage: 'Error in signUp');
}
}
@override
Future<TwoFASecretEntity> generateTwoFASignUp({required String token}) async {
try {
final response = await _repository.post<Map<String, dynamic>>(
'/auth/totp/secret',
body: <String, dynamic>{'token': token},
);
final data = response.data;
if (data == null || data.isEmpty) {
throw Exception('Empty response from /auth/totp/secret');
}
final model = TwoFASecretResponseModel.fromJson(data);
return model.toEntity();
} on DioException catch (error) {
throw _mapDioError(error, defaultMessage: 'Error in twoFASignUp');
}
}
@override
Future<String> verifyTwoFACodeSignUp({
required String token,
required String code,
}) async {
try {
final response = await _repository.post<String>(
'/auth/totp/code',
body: <String, dynamic>{'token': token, 'code': code},
);
final data = response.data;
if (data == null || data.isEmpty) {
throw Exception('Empty response from /auth/totp/code');
}
return data;
} on DioException catch (error) {
throw _mapDioError(error, defaultMessage: 'Error in twoFaCodeSignUp');
}
}
}
Exception _mapDioError(DioException error, {required String defaultMessage}) {
final apiMsg = _extractApiMessage(error.response?.data);
final msg = apiMsg ?? error.message ?? defaultMessage;
return Exception(msg);
}
String? _extractApiMessage(Object? data) {
if (data == null) return null;
if (data is Map) {
final errorObj = data['error'];
if (errorObj is Map && errorObj['message'] is String) {
return (errorObj['message'] as String).trim();
}
if (data['message'] is String) {
return (data['message'] as String).trim();
}
return null;
}
if (data is String) {
final raw = data.trim();
if (raw.isEmpty) return null;
try {
final decoded = jsonDecode(raw);
return _extractApiMessage(decoded);
} catch (_) {
return raw;
}
}
return null;
}

View File

@@ -0,0 +1,31 @@
import 'package:auth/src/features/sign_up/domain/entities/address_entity.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'address_model.freezed.dart';
part 'address_model.g.dart';
@freezed
abstract class AddressModel with _$AddressModel {
const factory AddressModel({
required String street,
required String city,
required String province,
required String state,
required String country,
required int postCode,
}) = _AddressModel;
factory AddressModel.fromJson(Map<String, dynamic> json) =>
_$AddressModelFromJson(json);
}
extension AddressModelMapper on AddressEntity {
AddressModel toModel() => AddressModel(
street: street,
city: city,
province: province,
state: state,
country: country,
postCode: postCode,
);
}

View File

@@ -0,0 +1,292 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'address_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$AddressModel {
String get street; String get city; String get province; String get state; String get country; int get postCode;
/// Create a copy of AddressModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$AddressModelCopyWith<AddressModel> get copyWith => _$AddressModelCopyWithImpl<AddressModel>(this as AddressModel, _$identity);
/// Serializes this AddressModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AddressModel&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode);
@override
String toString() {
return 'AddressModel(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)';
}
}
/// @nodoc
abstract mixin class $AddressModelCopyWith<$Res> {
factory $AddressModelCopyWith(AddressModel value, $Res Function(AddressModel) _then) = _$AddressModelCopyWithImpl;
@useResult
$Res call({
String street, String city, String province, String state, String country, int postCode
});
}
/// @nodoc
class _$AddressModelCopyWithImpl<$Res>
implements $AddressModelCopyWith<$Res> {
_$AddressModelCopyWithImpl(this._self, this._then);
final AddressModel _self;
final $Res Function(AddressModel) _then;
/// Create a copy of AddressModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = null,}) {
return _then(_self.copyWith(
street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable
as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable
as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable
as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String,postCode: null == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// Adds pattern-matching-related methods to [AddressModel].
extension AddressModelPatterns on AddressModel {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AddressModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _AddressModel() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AddressModel value) $default,){
final _that = this;
switch (_that) {
case _AddressModel():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AddressModel value)? $default,){
final _that = this;
switch (_that) {
case _AddressModel() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String street, String city, String province, String state, String country, int postCode)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _AddressModel() when $default != null:
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String street, String city, String province, String state, String country, int postCode) $default,) {final _that = this;
switch (_that) {
case _AddressModel():
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String street, String city, String province, String state, String country, int postCode)? $default,) {final _that = this;
switch (_that) {
case _AddressModel() when $default != null:
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _AddressModel implements AddressModel {
const _AddressModel({required this.street, required this.city, required this.province, required this.state, required this.country, required this.postCode});
factory _AddressModel.fromJson(Map<String, dynamic> json) => _$AddressModelFromJson(json);
@override final String street;
@override final String city;
@override final String province;
@override final String state;
@override final String country;
@override final int postCode;
/// Create a copy of AddressModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$AddressModelCopyWith<_AddressModel> get copyWith => __$AddressModelCopyWithImpl<_AddressModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$AddressModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AddressModel&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode);
@override
String toString() {
return 'AddressModel(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)';
}
}
/// @nodoc
abstract mixin class _$AddressModelCopyWith<$Res> implements $AddressModelCopyWith<$Res> {
factory _$AddressModelCopyWith(_AddressModel value, $Res Function(_AddressModel) _then) = __$AddressModelCopyWithImpl;
@override @useResult
$Res call({
String street, String city, String province, String state, String country, int postCode
});
}
/// @nodoc
class __$AddressModelCopyWithImpl<$Res>
implements _$AddressModelCopyWith<$Res> {
__$AddressModelCopyWithImpl(this._self, this._then);
final _AddressModel _self;
final $Res Function(_AddressModel) _then;
/// Create a copy of AddressModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = null,}) {
return _then(_AddressModel(
street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable
as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable
as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable
as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String,postCode: null == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
// dart format on

View File

@@ -0,0 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'address_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_AddressModel _$AddressModelFromJson(Map<String, dynamic> json) =>
_AddressModel(
street: json['street'] as String,
city: json['city'] as String,
province: json['province'] as String,
state: json['state'] as String,
country: json['country'] as String,
postCode: (json['postCode'] as num).toInt(),
);
Map<String, dynamic> _$AddressModelToJson(_AddressModel instance) =>
<String, dynamic>{
'street': instance.street,
'city': instance.city,
'province': instance.province,
'state': instance.state,
'country': instance.country,
'postCode': instance.postCode,
};

View File

@@ -0,0 +1,61 @@
import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'address_model.dart';
part 'sign_up_request_model.freezed.dart';
part 'sign_up_request_model.g.dart';
@freezed
abstract class SignUpRequestModel with _$SignUpRequestModel {
const factory SignUpRequestModel({
required String firstName,
required String lastName,
required String email,
required String phone,
required String language,
required String password,
required List<AddressModel> taxResidences,
required List<AddressModel> addresses,
required int bornAt,
required String userId,
required String placeOfBirth,
required String birthCountry,
// ignore: invalid_annotation_target
@JsonKey(name: 'document') required String documentNumber,
required String documentType,
// ignore: invalid_annotation_target
@JsonKey(name: 'relationType') required String relationship,
}) = _SignUpRequestModel;
factory SignUpRequestModel.fromJson(Map<String, dynamic> json) =>
_$SignUpRequestModelFromJson(json);
@override
@JsonKey(name: 'document')
String get documentNumber;
@override
@JsonKey(name: 'relationType')
String get relationship;
}
extension SignUpRequestModelMapper on SignUpRequestEntity {
SignUpRequestModel toModel() => SignUpRequestModel(
firstName: firstName,
lastName: lastName,
email: email,
phone: phone,
language: language,
password: password,
taxResidences: taxResidences
.map((e) => e.toModel())
.toList(growable: false),
addresses: addresses.map((e) => e.toModel()).toList(growable: false),
bornAt: bornAt,
userId: userId,
placeOfBirth: placeOfBirth,
birthCountry: birthCountry,
documentNumber: documentNumber,
documentType: documentType,
relationship: relationship,
);
}

View File

@@ -0,0 +1,335 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'sign_up_request_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$SignUpRequestModel {
String get firstName; String get lastName; String get email; String get phone; String get language; String get password; List<AddressModel> get taxResidences; List<AddressModel> get addresses; int get bornAt; String get userId; String get placeOfBirth; String get birthCountry;// ignore: invalid_annotation_target
@JsonKey(name: 'document') String get documentNumber; String get documentType;// ignore: invalid_annotation_target
@JsonKey(name: 'relationType') String get relationship;
/// Create a copy of SignUpRequestModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SignUpRequestModelCopyWith<SignUpRequestModel> get copyWith => _$SignUpRequestModelCopyWithImpl<SignUpRequestModel>(this as SignUpRequestModel, _$identity);
/// Serializes this SignUpRequestModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpRequestModel&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.password, password) || other.password == password)&&const DeepCollectionEquality().equals(other.taxResidences, taxResidences)&&const DeepCollectionEquality().equals(other.addresses, addresses)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.relationship, relationship) || other.relationship == relationship));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,firstName,lastName,email,phone,language,password,const DeepCollectionEquality().hash(taxResidences),const DeepCollectionEquality().hash(addresses),bornAt,userId,placeOfBirth,birthCountry,documentNumber,documentType,relationship);
@override
String toString() {
return 'SignUpRequestModel(firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, password: $password, taxResidences: $taxResidences, addresses: $addresses, bornAt: $bornAt, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, documentNumber: $documentNumber, documentType: $documentType, relationship: $relationship)';
}
}
/// @nodoc
abstract mixin class $SignUpRequestModelCopyWith<$Res> {
factory $SignUpRequestModelCopyWith(SignUpRequestModel value, $Res Function(SignUpRequestModel) _then) = _$SignUpRequestModelCopyWithImpl;
@useResult
$Res call({
String firstName, String lastName, String email, String phone, String language, String password, List<AddressModel> taxResidences, List<AddressModel> addresses, int bornAt, String userId, String placeOfBirth, String birthCountry,@JsonKey(name: 'document') String documentNumber, String documentType,@JsonKey(name: 'relationType') String relationship
});
}
/// @nodoc
class _$SignUpRequestModelCopyWithImpl<$Res>
implements $SignUpRequestModelCopyWith<$Res> {
_$SignUpRequestModelCopyWithImpl(this._self, this._then);
final SignUpRequestModel _self;
final $Res Function(SignUpRequestModel) _then;
/// Create a copy of SignUpRequestModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? password = null,Object? taxResidences = null,Object? addresses = null,Object? bornAt = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? documentNumber = null,Object? documentType = null,Object? relationship = null,}) {
return _then(_self.copyWith(
firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable
as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable
as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable
as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String,taxResidences: null == taxResidences ? _self.taxResidences : taxResidences // ignore: cast_nullable_to_non_nullable
as List<AddressModel>,addresses: null == addresses ? _self.addresses : addresses // ignore: cast_nullable_to_non_nullable
as List<AddressModel>,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as int,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable
as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable
as String,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable
as String,documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable
as String,relationship: null == relationship ? _self.relationship : relationship // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [SignUpRequestModel].
extension SignUpRequestModelPatterns on SignUpRequestModel {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SignUpRequestModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _SignUpRequestModel() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SignUpRequestModel value) $default,){
final _that = this;
switch (_that) {
case _SignUpRequestModel():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SignUpRequestModel value)? $default,){
final _that = this;
switch (_that) {
case _SignUpRequestModel() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String firstName, String lastName, String email, String phone, String language, String password, List<AddressModel> taxResidences, List<AddressModel> addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'document') String documentNumber, String documentType, @JsonKey(name: 'relationType') String relationship)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SignUpRequestModel() when $default != null:
return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.documentType,_that.relationship);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String firstName, String lastName, String email, String phone, String language, String password, List<AddressModel> taxResidences, List<AddressModel> addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'document') String documentNumber, String documentType, @JsonKey(name: 'relationType') String relationship) $default,) {final _that = this;
switch (_that) {
case _SignUpRequestModel():
return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.documentType,_that.relationship);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String firstName, String lastName, String email, String phone, String language, String password, List<AddressModel> taxResidences, List<AddressModel> addresses, int bornAt, String userId, String placeOfBirth, String birthCountry, @JsonKey(name: 'document') String documentNumber, String documentType, @JsonKey(name: 'relationType') String relationship)? $default,) {final _that = this;
switch (_that) {
case _SignUpRequestModel() when $default != null:
return $default(_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry,_that.documentNumber,_that.documentType,_that.relationship);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _SignUpRequestModel implements SignUpRequestModel {
const _SignUpRequestModel({required this.firstName, required this.lastName, required this.email, required this.phone, required this.language, required this.password, required final List<AddressModel> taxResidences, required final List<AddressModel> addresses, required this.bornAt, required this.userId, required this.placeOfBirth, required this.birthCountry, @JsonKey(name: 'document') required this.documentNumber, required this.documentType, @JsonKey(name: 'relationType') required this.relationship}): _taxResidences = taxResidences,_addresses = addresses;
factory _SignUpRequestModel.fromJson(Map<String, dynamic> json) => _$SignUpRequestModelFromJson(json);
@override final String firstName;
@override final String lastName;
@override final String email;
@override final String phone;
@override final String language;
@override final String password;
final List<AddressModel> _taxResidences;
@override List<AddressModel> get taxResidences {
if (_taxResidences is EqualUnmodifiableListView) return _taxResidences;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_taxResidences);
}
final List<AddressModel> _addresses;
@override List<AddressModel> get addresses {
if (_addresses is EqualUnmodifiableListView) return _addresses;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_addresses);
}
@override final int bornAt;
@override final String userId;
@override final String placeOfBirth;
@override final String birthCountry;
// ignore: invalid_annotation_target
@override@JsonKey(name: 'document') final String documentNumber;
@override final String documentType;
// ignore: invalid_annotation_target
@override@JsonKey(name: 'relationType') final String relationship;
/// Create a copy of SignUpRequestModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SignUpRequestModelCopyWith<_SignUpRequestModel> get copyWith => __$SignUpRequestModelCopyWithImpl<_SignUpRequestModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$SignUpRequestModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpRequestModel&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.password, password) || other.password == password)&&const DeepCollectionEquality().equals(other._taxResidences, _taxResidences)&&const DeepCollectionEquality().equals(other._addresses, _addresses)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.relationship, relationship) || other.relationship == relationship));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,firstName,lastName,email,phone,language,password,const DeepCollectionEquality().hash(_taxResidences),const DeepCollectionEquality().hash(_addresses),bornAt,userId,placeOfBirth,birthCountry,documentNumber,documentType,relationship);
@override
String toString() {
return 'SignUpRequestModel(firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, password: $password, taxResidences: $taxResidences, addresses: $addresses, bornAt: $bornAt, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry, documentNumber: $documentNumber, documentType: $documentType, relationship: $relationship)';
}
}
/// @nodoc
abstract mixin class _$SignUpRequestModelCopyWith<$Res> implements $SignUpRequestModelCopyWith<$Res> {
factory _$SignUpRequestModelCopyWith(_SignUpRequestModel value, $Res Function(_SignUpRequestModel) _then) = __$SignUpRequestModelCopyWithImpl;
@override @useResult
$Res call({
String firstName, String lastName, String email, String phone, String language, String password, List<AddressModel> taxResidences, List<AddressModel> addresses, int bornAt, String userId, String placeOfBirth, String birthCountry,@JsonKey(name: 'document') String documentNumber, String documentType,@JsonKey(name: 'relationType') String relationship
});
}
/// @nodoc
class __$SignUpRequestModelCopyWithImpl<$Res>
implements _$SignUpRequestModelCopyWith<$Res> {
__$SignUpRequestModelCopyWithImpl(this._self, this._then);
final _SignUpRequestModel _self;
final $Res Function(_SignUpRequestModel) _then;
/// Create a copy of SignUpRequestModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? password = null,Object? taxResidences = null,Object? addresses = null,Object? bornAt = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,Object? documentNumber = null,Object? documentType = null,Object? relationship = null,}) {
return _then(_SignUpRequestModel(
firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable
as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable
as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable
as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String,taxResidences: null == taxResidences ? _self._taxResidences : taxResidences // ignore: cast_nullable_to_non_nullable
as List<AddressModel>,addresses: null == addresses ? _self._addresses : addresses // ignore: cast_nullable_to_non_nullable
as List<AddressModel>,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as int,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable
as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable
as String,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable
as String,documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable
as String,relationship: null == relationship ? _self.relationship : relationship // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

View File

@@ -0,0 +1,49 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'sign_up_request_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_SignUpRequestModel _$SignUpRequestModelFromJson(Map<String, dynamic> json) =>
_SignUpRequestModel(
firstName: json['firstName'] as String,
lastName: json['lastName'] as String,
email: json['email'] as String,
phone: json['phone'] as String,
language: json['language'] as String,
password: json['password'] as String,
taxResidences: (json['taxResidences'] as List<dynamic>)
.map((e) => AddressModel.fromJson(e as Map<String, dynamic>))
.toList(),
addresses: (json['addresses'] as List<dynamic>)
.map((e) => AddressModel.fromJson(e as Map<String, dynamic>))
.toList(),
bornAt: (json['bornAt'] as num).toInt(),
userId: json['userId'] as String,
placeOfBirth: json['placeOfBirth'] as String,
birthCountry: json['birthCountry'] as String,
documentNumber: json['document'] as String,
documentType: json['documentType'] as String,
relationship: json['relationType'] as String,
);
Map<String, dynamic> _$SignUpRequestModelToJson(_SignUpRequestModel instance) =>
<String, dynamic>{
'firstName': instance.firstName,
'lastName': instance.lastName,
'email': instance.email,
'phone': instance.phone,
'language': instance.language,
'password': instance.password,
'taxResidences': instance.taxResidences,
'addresses': instance.addresses,
'bornAt': instance.bornAt,
'userId': instance.userId,
'placeOfBirth': instance.placeOfBirth,
'birthCountry': instance.birthCountry,
'document': instance.documentNumber,
'documentType': instance.documentType,
'relationType': instance.relationship,
};

View File

@@ -0,0 +1,24 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'sign_up_response_model.freezed.dart';
part 'sign_up_response_model.g.dart';
@freezed
abstract class SignUpResponseModel with _$SignUpResponseModel {
const factory SignUpResponseModel({
required bool isCreated,
required SignUpResponseItemModel item,
}) = _SignUpResponseModel;
factory SignUpResponseModel.fromJson(Map<String, dynamic> json) =>
_$SignUpResponseModelFromJson(json);
}
@freezed
abstract class SignUpResponseItemModel with _$SignUpResponseItemModel {
const factory SignUpResponseItemModel({required String token}) =
_SignUpResponseItemModel;
factory SignUpResponseItemModel.fromJson(Map<String, dynamic> json) =>
_$SignUpResponseItemModelFromJson(json);
}

View File

@@ -0,0 +1,561 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'sign_up_response_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$SignUpResponseModel {
bool get isCreated; SignUpResponseItemModel get item;
/// Create a copy of SignUpResponseModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SignUpResponseModelCopyWith<SignUpResponseModel> get copyWith => _$SignUpResponseModelCopyWithImpl<SignUpResponseModel>(this as SignUpResponseModel, _$identity);
/// Serializes this SignUpResponseModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpResponseModel&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'SignUpResponseModel(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class $SignUpResponseModelCopyWith<$Res> {
factory $SignUpResponseModelCopyWith(SignUpResponseModel value, $Res Function(SignUpResponseModel) _then) = _$SignUpResponseModelCopyWithImpl;
@useResult
$Res call({
bool isCreated, SignUpResponseItemModel item
});
$SignUpResponseItemModelCopyWith<$Res> get item;
}
/// @nodoc
class _$SignUpResponseModelCopyWithImpl<$Res>
implements $SignUpResponseModelCopyWith<$Res> {
_$SignUpResponseModelCopyWithImpl(this._self, this._then);
final SignUpResponseModel _self;
final $Res Function(SignUpResponseModel) _then;
/// Create a copy of SignUpResponseModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_self.copyWith(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as SignUpResponseItemModel,
));
}
/// Create a copy of SignUpResponseModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SignUpResponseItemModelCopyWith<$Res> get item {
return $SignUpResponseItemModelCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// Adds pattern-matching-related methods to [SignUpResponseModel].
extension SignUpResponseModelPatterns on SignUpResponseModel {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SignUpResponseModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _SignUpResponseModel() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SignUpResponseModel value) $default,){
final _that = this;
switch (_that) {
case _SignUpResponseModel():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SignUpResponseModel value)? $default,){
final _that = this;
switch (_that) {
case _SignUpResponseModel() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isCreated, SignUpResponseItemModel item)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SignUpResponseModel() when $default != null:
return $default(_that.isCreated,_that.item);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isCreated, SignUpResponseItemModel item) $default,) {final _that = this;
switch (_that) {
case _SignUpResponseModel():
return $default(_that.isCreated,_that.item);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isCreated, SignUpResponseItemModel item)? $default,) {final _that = this;
switch (_that) {
case _SignUpResponseModel() when $default != null:
return $default(_that.isCreated,_that.item);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _SignUpResponseModel implements SignUpResponseModel {
const _SignUpResponseModel({required this.isCreated, required this.item});
factory _SignUpResponseModel.fromJson(Map<String, dynamic> json) => _$SignUpResponseModelFromJson(json);
@override final bool isCreated;
@override final SignUpResponseItemModel item;
/// Create a copy of SignUpResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SignUpResponseModelCopyWith<_SignUpResponseModel> get copyWith => __$SignUpResponseModelCopyWithImpl<_SignUpResponseModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$SignUpResponseModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpResponseModel&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'SignUpResponseModel(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class _$SignUpResponseModelCopyWith<$Res> implements $SignUpResponseModelCopyWith<$Res> {
factory _$SignUpResponseModelCopyWith(_SignUpResponseModel value, $Res Function(_SignUpResponseModel) _then) = __$SignUpResponseModelCopyWithImpl;
@override @useResult
$Res call({
bool isCreated, SignUpResponseItemModel item
});
@override $SignUpResponseItemModelCopyWith<$Res> get item;
}
/// @nodoc
class __$SignUpResponseModelCopyWithImpl<$Res>
implements _$SignUpResponseModelCopyWith<$Res> {
__$SignUpResponseModelCopyWithImpl(this._self, this._then);
final _SignUpResponseModel _self;
final $Res Function(_SignUpResponseModel) _then;
/// Create a copy of SignUpResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_SignUpResponseModel(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as SignUpResponseItemModel,
));
}
/// Create a copy of SignUpResponseModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SignUpResponseItemModelCopyWith<$Res> get item {
return $SignUpResponseItemModelCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// @nodoc
mixin _$SignUpResponseItemModel {
String get token;
/// Create a copy of SignUpResponseItemModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SignUpResponseItemModelCopyWith<SignUpResponseItemModel> get copyWith => _$SignUpResponseItemModelCopyWithImpl<SignUpResponseItemModel>(this as SignUpResponseItemModel, _$identity);
/// Serializes this SignUpResponseItemModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpResponseItemModel&&(identical(other.token, token) || other.token == token));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,token);
@override
String toString() {
return 'SignUpResponseItemModel(token: $token)';
}
}
/// @nodoc
abstract mixin class $SignUpResponseItemModelCopyWith<$Res> {
factory $SignUpResponseItemModelCopyWith(SignUpResponseItemModel value, $Res Function(SignUpResponseItemModel) _then) = _$SignUpResponseItemModelCopyWithImpl;
@useResult
$Res call({
String token
});
}
/// @nodoc
class _$SignUpResponseItemModelCopyWithImpl<$Res>
implements $SignUpResponseItemModelCopyWith<$Res> {
_$SignUpResponseItemModelCopyWithImpl(this._self, this._then);
final SignUpResponseItemModel _self;
final $Res Function(SignUpResponseItemModel) _then;
/// Create a copy of SignUpResponseItemModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? token = null,}) {
return _then(_self.copyWith(
token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [SignUpResponseItemModel].
extension SignUpResponseItemModelPatterns on SignUpResponseItemModel {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SignUpResponseItemModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _SignUpResponseItemModel() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SignUpResponseItemModel value) $default,){
final _that = this;
switch (_that) {
case _SignUpResponseItemModel():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SignUpResponseItemModel value)? $default,){
final _that = this;
switch (_that) {
case _SignUpResponseItemModel() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String token)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SignUpResponseItemModel() when $default != null:
return $default(_that.token);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String token) $default,) {final _that = this;
switch (_that) {
case _SignUpResponseItemModel():
return $default(_that.token);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String token)? $default,) {final _that = this;
switch (_that) {
case _SignUpResponseItemModel() when $default != null:
return $default(_that.token);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _SignUpResponseItemModel implements SignUpResponseItemModel {
const _SignUpResponseItemModel({required this.token});
factory _SignUpResponseItemModel.fromJson(Map<String, dynamic> json) => _$SignUpResponseItemModelFromJson(json);
@override final String token;
/// Create a copy of SignUpResponseItemModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SignUpResponseItemModelCopyWith<_SignUpResponseItemModel> get copyWith => __$SignUpResponseItemModelCopyWithImpl<_SignUpResponseItemModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$SignUpResponseItemModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpResponseItemModel&&(identical(other.token, token) || other.token == token));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,token);
@override
String toString() {
return 'SignUpResponseItemModel(token: $token)';
}
}
/// @nodoc
abstract mixin class _$SignUpResponseItemModelCopyWith<$Res> implements $SignUpResponseItemModelCopyWith<$Res> {
factory _$SignUpResponseItemModelCopyWith(_SignUpResponseItemModel value, $Res Function(_SignUpResponseItemModel) _then) = __$SignUpResponseItemModelCopyWithImpl;
@override @useResult
$Res call({
String token
});
}
/// @nodoc
class __$SignUpResponseItemModelCopyWithImpl<$Res>
implements _$SignUpResponseItemModelCopyWith<$Res> {
__$SignUpResponseItemModelCopyWithImpl(this._self, this._then);
final _SignUpResponseItemModel _self;
final $Res Function(_SignUpResponseItemModel) _then;
/// Create a copy of SignUpResponseItemModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? token = null,}) {
return _then(_SignUpResponseItemModel(
token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

View File

@@ -0,0 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'sign_up_response_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_SignUpResponseModel _$SignUpResponseModelFromJson(Map<String, dynamic> json) =>
_SignUpResponseModel(
isCreated: json['isCreated'] as bool,
item: SignUpResponseItemModel.fromJson(
json['item'] as Map<String, dynamic>,
),
);
Map<String, dynamic> _$SignUpResponseModelToJson(
_SignUpResponseModel instance,
) => <String, dynamic>{'isCreated': instance.isCreated, 'item': instance.item};
_SignUpResponseItemModel _$SignUpResponseItemModelFromJson(
Map<String, dynamic> json,
) => _SignUpResponseItemModel(token: json['token'] as String);
Map<String, dynamic> _$SignUpResponseItemModelToJson(
_SignUpResponseItemModel instance,
) => <String, dynamic>{'token': instance.token};

View File

@@ -0,0 +1,37 @@
import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'two_fa_secret_response_model.freezed.dart';
part 'two_fa_secret_response_model.g.dart';
@freezed
abstract class TwoFASecretResponseModel with _$TwoFASecretResponseModel {
const factory TwoFASecretResponseModel({
required bool isCreated,
required TwoFASecretItemResponseModel item,
}) = _TwoFASecretResponseModel;
factory TwoFASecretResponseModel.fromJson(Map<String, dynamic> json) =>
_$TwoFASecretResponseModelFromJson(json);
}
@freezed
abstract class TwoFASecretItemResponseModel
with _$TwoFASecretItemResponseModel {
const factory TwoFASecretItemResponseModel({
required String secret,
required String qr,
}) = _TwoFASecretItemResponseModel;
factory TwoFASecretItemResponseModel.fromJson(Map<String, dynamic> json) =>
_$TwoFASecretItemResponseModelFromJson(json);
}
extension TwoFASecretResponseModelMapper on TwoFASecretResponseModel {
TwoFASecretEntity toEntity() {
return TwoFASecretEntity(
isCreated: isCreated,
item: TwoFASecretItemEntity(secret: item.secret, qr: item.qr),
);
}
}

View File

@@ -0,0 +1,564 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'two_fa_secret_response_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$TwoFASecretResponseModel {
bool get isCreated; TwoFASecretItemResponseModel get item;
/// Create a copy of TwoFASecretResponseModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$TwoFASecretResponseModelCopyWith<TwoFASecretResponseModel> get copyWith => _$TwoFASecretResponseModelCopyWithImpl<TwoFASecretResponseModel>(this as TwoFASecretResponseModel, _$identity);
/// Serializes this TwoFASecretResponseModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TwoFASecretResponseModel&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'TwoFASecretResponseModel(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class $TwoFASecretResponseModelCopyWith<$Res> {
factory $TwoFASecretResponseModelCopyWith(TwoFASecretResponseModel value, $Res Function(TwoFASecretResponseModel) _then) = _$TwoFASecretResponseModelCopyWithImpl;
@useResult
$Res call({
bool isCreated, TwoFASecretItemResponseModel item
});
$TwoFASecretItemResponseModelCopyWith<$Res> get item;
}
/// @nodoc
class _$TwoFASecretResponseModelCopyWithImpl<$Res>
implements $TwoFASecretResponseModelCopyWith<$Res> {
_$TwoFASecretResponseModelCopyWithImpl(this._self, this._then);
final TwoFASecretResponseModel _self;
final $Res Function(TwoFASecretResponseModel) _then;
/// Create a copy of TwoFASecretResponseModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_self.copyWith(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as TwoFASecretItemResponseModel,
));
}
/// Create a copy of TwoFASecretResponseModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$TwoFASecretItemResponseModelCopyWith<$Res> get item {
return $TwoFASecretItemResponseModelCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// Adds pattern-matching-related methods to [TwoFASecretResponseModel].
extension TwoFASecretResponseModelPatterns on TwoFASecretResponseModel {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _TwoFASecretResponseModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _TwoFASecretResponseModel() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _TwoFASecretResponseModel value) $default,){
final _that = this;
switch (_that) {
case _TwoFASecretResponseModel():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _TwoFASecretResponseModel value)? $default,){
final _that = this;
switch (_that) {
case _TwoFASecretResponseModel() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isCreated, TwoFASecretItemResponseModel item)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _TwoFASecretResponseModel() when $default != null:
return $default(_that.isCreated,_that.item);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isCreated, TwoFASecretItemResponseModel item) $default,) {final _that = this;
switch (_that) {
case _TwoFASecretResponseModel():
return $default(_that.isCreated,_that.item);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isCreated, TwoFASecretItemResponseModel item)? $default,) {final _that = this;
switch (_that) {
case _TwoFASecretResponseModel() when $default != null:
return $default(_that.isCreated,_that.item);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _TwoFASecretResponseModel implements TwoFASecretResponseModel {
const _TwoFASecretResponseModel({required this.isCreated, required this.item});
factory _TwoFASecretResponseModel.fromJson(Map<String, dynamic> json) => _$TwoFASecretResponseModelFromJson(json);
@override final bool isCreated;
@override final TwoFASecretItemResponseModel item;
/// Create a copy of TwoFASecretResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$TwoFASecretResponseModelCopyWith<_TwoFASecretResponseModel> get copyWith => __$TwoFASecretResponseModelCopyWithImpl<_TwoFASecretResponseModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$TwoFASecretResponseModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _TwoFASecretResponseModel&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'TwoFASecretResponseModel(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class _$TwoFASecretResponseModelCopyWith<$Res> implements $TwoFASecretResponseModelCopyWith<$Res> {
factory _$TwoFASecretResponseModelCopyWith(_TwoFASecretResponseModel value, $Res Function(_TwoFASecretResponseModel) _then) = __$TwoFASecretResponseModelCopyWithImpl;
@override @useResult
$Res call({
bool isCreated, TwoFASecretItemResponseModel item
});
@override $TwoFASecretItemResponseModelCopyWith<$Res> get item;
}
/// @nodoc
class __$TwoFASecretResponseModelCopyWithImpl<$Res>
implements _$TwoFASecretResponseModelCopyWith<$Res> {
__$TwoFASecretResponseModelCopyWithImpl(this._self, this._then);
final _TwoFASecretResponseModel _self;
final $Res Function(_TwoFASecretResponseModel) _then;
/// Create a copy of TwoFASecretResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_TwoFASecretResponseModel(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as TwoFASecretItemResponseModel,
));
}
/// Create a copy of TwoFASecretResponseModel
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$TwoFASecretItemResponseModelCopyWith<$Res> get item {
return $TwoFASecretItemResponseModelCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// @nodoc
mixin _$TwoFASecretItemResponseModel {
String get secret; String get qr;
/// Create a copy of TwoFASecretItemResponseModel
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$TwoFASecretItemResponseModelCopyWith<TwoFASecretItemResponseModel> get copyWith => _$TwoFASecretItemResponseModelCopyWithImpl<TwoFASecretItemResponseModel>(this as TwoFASecretItemResponseModel, _$identity);
/// Serializes this TwoFASecretItemResponseModel to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TwoFASecretItemResponseModel&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.qr, qr) || other.qr == qr));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,secret,qr);
@override
String toString() {
return 'TwoFASecretItemResponseModel(secret: $secret, qr: $qr)';
}
}
/// @nodoc
abstract mixin class $TwoFASecretItemResponseModelCopyWith<$Res> {
factory $TwoFASecretItemResponseModelCopyWith(TwoFASecretItemResponseModel value, $Res Function(TwoFASecretItemResponseModel) _then) = _$TwoFASecretItemResponseModelCopyWithImpl;
@useResult
$Res call({
String secret, String qr
});
}
/// @nodoc
class _$TwoFASecretItemResponseModelCopyWithImpl<$Res>
implements $TwoFASecretItemResponseModelCopyWith<$Res> {
_$TwoFASecretItemResponseModelCopyWithImpl(this._self, this._then);
final TwoFASecretItemResponseModel _self;
final $Res Function(TwoFASecretItemResponseModel) _then;
/// Create a copy of TwoFASecretItemResponseModel
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? secret = null,Object? qr = null,}) {
return _then(_self.copyWith(
secret: null == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable
as String,qr: null == qr ? _self.qr : qr // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [TwoFASecretItemResponseModel].
extension TwoFASecretItemResponseModelPatterns on TwoFASecretItemResponseModel {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _TwoFASecretItemResponseModel value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _TwoFASecretItemResponseModel() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _TwoFASecretItemResponseModel value) $default,){
final _that = this;
switch (_that) {
case _TwoFASecretItemResponseModel():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _TwoFASecretItemResponseModel value)? $default,){
final _that = this;
switch (_that) {
case _TwoFASecretItemResponseModel() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String secret, String qr)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _TwoFASecretItemResponseModel() when $default != null:
return $default(_that.secret,_that.qr);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String secret, String qr) $default,) {final _that = this;
switch (_that) {
case _TwoFASecretItemResponseModel():
return $default(_that.secret,_that.qr);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String secret, String qr)? $default,) {final _that = this;
switch (_that) {
case _TwoFASecretItemResponseModel() when $default != null:
return $default(_that.secret,_that.qr);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _TwoFASecretItemResponseModel implements TwoFASecretItemResponseModel {
const _TwoFASecretItemResponseModel({required this.secret, required this.qr});
factory _TwoFASecretItemResponseModel.fromJson(Map<String, dynamic> json) => _$TwoFASecretItemResponseModelFromJson(json);
@override final String secret;
@override final String qr;
/// Create a copy of TwoFASecretItemResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$TwoFASecretItemResponseModelCopyWith<_TwoFASecretItemResponseModel> get copyWith => __$TwoFASecretItemResponseModelCopyWithImpl<_TwoFASecretItemResponseModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$TwoFASecretItemResponseModelToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _TwoFASecretItemResponseModel&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.qr, qr) || other.qr == qr));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,secret,qr);
@override
String toString() {
return 'TwoFASecretItemResponseModel(secret: $secret, qr: $qr)';
}
}
/// @nodoc
abstract mixin class _$TwoFASecretItemResponseModelCopyWith<$Res> implements $TwoFASecretItemResponseModelCopyWith<$Res> {
factory _$TwoFASecretItemResponseModelCopyWith(_TwoFASecretItemResponseModel value, $Res Function(_TwoFASecretItemResponseModel) _then) = __$TwoFASecretItemResponseModelCopyWithImpl;
@override @useResult
$Res call({
String secret, String qr
});
}
/// @nodoc
class __$TwoFASecretItemResponseModelCopyWithImpl<$Res>
implements _$TwoFASecretItemResponseModelCopyWith<$Res> {
__$TwoFASecretItemResponseModelCopyWithImpl(this._self, this._then);
final _TwoFASecretItemResponseModel _self;
final $Res Function(_TwoFASecretItemResponseModel) _then;
/// Create a copy of TwoFASecretItemResponseModel
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? secret = null,Object? qr = null,}) {
return _then(_TwoFASecretItemResponseModel(
secret: null == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable
as String,qr: null == qr ? _self.qr : qr // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

View File

@@ -0,0 +1,31 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'two_fa_secret_response_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_TwoFASecretResponseModel _$TwoFASecretResponseModelFromJson(
Map<String, dynamic> json,
) => _TwoFASecretResponseModel(
isCreated: json['isCreated'] as bool,
item: TwoFASecretItemResponseModel.fromJson(
json['item'] as Map<String, dynamic>,
),
);
Map<String, dynamic> _$TwoFASecretResponseModelToJson(
_TwoFASecretResponseModel instance,
) => <String, dynamic>{'isCreated': instance.isCreated, 'item': instance.item};
_TwoFASecretItemResponseModel _$TwoFASecretItemResponseModelFromJson(
Map<String, dynamic> json,
) => _TwoFASecretItemResponseModel(
secret: json['secret'] as String,
qr: json['qr'] as String,
);
Map<String, dynamic> _$TwoFASecretItemResponseModelToJson(
_TwoFASecretItemResponseModel instance,
) => <String, dynamic>{'secret': instance.secret, 'qr': instance.qr};

View File

@@ -0,0 +1,48 @@
import 'package:auth/src/core/data/datasource/auth_remote_datasource.dart';
import 'package:auth/src/core/domain/repositories/auth_repository.dart';
import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart';
import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart';
class AuthRepositoryImpl implements AuthRepository {
const AuthRepositoryImpl(this._remote);
final AuthRemoteDatasource _remote;
@override
Future<void> requestPhoneCode({required String phone}) {
return _remote.requestPhoneCode(phone: phone);
}
@override
Future<void> verifyPhoneCode({required String phone, required String code}) {
return _remote.verifyPhoneCode(phone: phone, code: code);
}
@override
Future<String> login({required String email, required String password}) {
return _remote.login(email: email, password: password);
}
@override
Future<void> twoFactor({required String token, required String code}) {
return _remote.twoFALogin(token: token, code: code);
}
@override
Future<String> signUp({required SignUpRequestEntity request}) {
return _remote.signUp(request: request);
}
@override
Future<TwoFASecretEntity> generateTwoFASignUp({required String token}) {
return _remote.generateTwoFASignUp(token: token);
}
@override
Future<void> verifyTwoFACodeSignUp({
required String token,
required String code,
}) {
return _remote.verifyTwoFACodeSignUp(token: token, code: code);
}
}

View File

@@ -0,0 +1,20 @@
import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart';
import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart';
abstract class AuthRepository {
Future<void> requestPhoneCode({required String phone});
Future<void> verifyPhoneCode({required String phone, required String code});
Future<String> login({required String email, required String password});
Future<void> twoFactor({required String token, required String code});
Future<String> signUp({required SignUpRequestEntity request});
Future<TwoFASecretEntity> generateTwoFASignUp({required String token});
Future<void> verifyTwoFACodeSignUp({
required String token,
required String code,
});
}

View File

@@ -0,0 +1,9 @@
import 'package:auth/src/core/data/datasource/auth_remote_datasource.dart';
import 'package:auth/src/core/data/datasource/auth_remote_datasource_impl.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
final authRemoteDatasourceProvider = Provider<AuthRemoteDatasource>((ref) {
final questiaRepository = getIt<QuestiaRepository>();
return AuthRemoteDatasourceImpl(questiaRepository);
});

View File

@@ -0,0 +1,9 @@
import 'package:auth/src/core/data/repositories/auth_repository_impl.dart';
import 'package:auth/src/core/domain/repositories/auth_repository.dart';
import 'package:auth/src/core/providers/auth_remote_datasource_provider.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final authRepositoryProvider = Provider<AuthRepository>((ref) {
final remote = ref.read(authRemoteDatasourceProvider);
return AuthRepositoryImpl(remote);
});

View File

@@ -1,56 +0,0 @@
import 'package:auth/src/device_sign_up/link_watch/create_profile_screen.dart';
import 'package:flutter/material.dart';
class AddKidScreen extends StatelessWidget {
const AddKidScreen({super.key});
@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),
SizedBox(
width: double.infinity,
child: FilledButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => CreateProfileScreen()),
),
child: Text("¡Empezar!"),
),
),
],
),
),
);
}
}

View File

@@ -1,87 +0,0 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:auth/src/device_sign_up/add_kid_screen.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class AccountCreatedKidScreen extends ConsumerWidget {
const AccountCreatedKidScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final model = "SaveWatch Plus 2";
final id = "1106652524";
final fullName = "Carlos Pérez Cruz";
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: Container(
margin: EdgeInsets.all(30),
child: Center(
child: Column(
spacing: 20,
children: [
Spacer(flex: 2),
Icon(
Icons.check,
color: theme.getColorFor(ThemeCode.backgroundPrimary),
size: 50,
),
Text(
"Cuenta creada",
style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
),
Text.rich(
TextSpan(
text: "Has creado la cuenta para:\n",
children: [
TextSpan(
text: fullName,
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
Text("Reloj: $model"),
Text("ID del reloj: $id"),
Text(
"Ya puedes darle su primera paga paa que empiece a disfrutarla en su reloj",
style: TextStyle(fontWeight: FontWeight.bold),
),
Spacer(flex: 6),
Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
topLeft: Radius.circular(20),
),
),
child: Column(
children: [
Expanded(
child: FilledButton(
onPressed: () => {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (_) => AddKidScreen()),
),
},
child: Text("Dale su primera paga"),
),
),
TextButton(
onPressed: () => {},
child: Text("Añadir otro peque"),
),
],
),
),
],
),
),
),
);
}
}

View File

@@ -1,304 +0,0 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
class CreateProfileScreen extends ConsumerWidget {
const CreateProfileScreen({super.key});
final int currentStep = 0;
@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,95 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class AddKidScreen extends ConsumerWidget {
final nextStep;
const AddKidScreen({super.key, required this.nextStep});
@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: Column(
spacing: 15,
children: [
Spacer(flex: 6),
Text("Añade a tu peque", style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold)),
Text(
"Controla su gasto a la vez que aprende hábitos financieros responsables",
textAlign: TextAlign.center,
),
Container(
margin: EdgeInsets.symmetric(vertical: 30, horizontal: 50),
child: Row(
spacing: 10,
children: [
Stack(
children: [
Column(
spacing: 16,
children: List<Widget>.generate(3, (int index) =>
Container(
decoration: ShapeDecoration(
shape: CircleBorder(),
color: theme.getColorFor(ThemeCode.buttonPrimary)
),
width: 32,
height: 32,
child: Center(child: Text(
(index + 1).toString(),
style: TextStyle(
color: theme.getColorFor(ThemeCode.backgroundPrimary)
)
))
)
)
),
Divider(color: Colors.red, thickness: 4,),
],
),
Column(
spacing: 16,
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!",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
letterSpacing: 0
)
),
Text(
"Recuerda que necesitas tener un Plan SaveFamily",
textAlign: TextAlign.center
),
Text(
"Si aún no lo tienes, puedes conseguirlo a través de nuestra web",
textAlign: TextAlign.center,
),
Spacer(flex: 8),
PrimaryButton(
onPressed: nextStep,
text: "¡Empezar!",
color: theme.getColorFor(ThemeCode.buttonPrimary)
),
],
),
),
);
}
}

View File

@@ -1,11 +1,16 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class ContactScreen extends StatelessWidget {
class ContactScreen extends ConsumerWidget {
const ContactScreen({super.key});
@override
Widget build(BuildContext context) {
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(
@@ -36,32 +41,22 @@ class ContactScreen extends StatelessWidget {
],
),
Expanded(
child: TextField(
decoration: InputDecoration(
labelText: "Nombre",
hintText: "Nombre y apellidos",
border: OutlineInputBorder(),
),
child: CustomTextField(
label: "Nombre",
hint: "Nombre y apellidos",
),
),
Expanded(
child: TextField(
decoration: InputDecoration(
labelText: "Correo electrónico",
hintText: "Correo electrónico",
border: OutlineInputBorder(),
),
child: CustomTextField(
label: "Correo electrónico",
hint: "Correo electrónico",
),
),
Expanded(
child: TextField(
minLines: 3,
maxLines: 3,
decoration: InputDecoration(
labelText: "Asunto del mensaje",
hintText: "Escribe tu mensaje",
border: OutlineInputBorder(),
),
child: CustomTextField(
lines: 3,
label: "Asunto del mensaje",
hint: "Escribe tu mensaje",
),
),
Expanded(

View File

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

View File

@@ -0,0 +1,109 @@
import 'package:auth/auth.dart';
import 'package:auth/src/features/device_sign_up/add_kid_screen.dart';
import 'package:auth/src/features/device_sign_up/link_watch/link_watch_screen.dart';
import 'package:auth/src/features/device_sign_up/link_watch/link_watch_previous_screen.dart';
import 'package:auth/src/features/sign_up/presentation/screens/account_created_screen.dart';
import 'package:auth/src/widgets/layouts/form_step_layout.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/navigation.dart';
class DeviceSignupScreen extends ConsumerStatefulWidget {
final NavigationContract navigationContract;
const DeviceSignupScreen({super.key, required this.navigationContract});
@override
ConsumerState<DeviceSignupScreen> createState() =>
DeviceSignupScreenState(navigationContract);
}
class DeviceSignupScreenState extends ConsumerState<DeviceSignupScreen> {
late int currentStep;
final NavigationContract navigationContract;
DeviceSignupScreenState(this.navigationContract);
@override
void initState() {
currentStep = 0;
}
@override
Widget build(BuildContext context) {
return getSteps()[currentStep];
}
List<Widget> getSteps() {
final theme = ref.watch(themePortProvider);
final continueBtn = Container(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
child: PrimaryButton(
onPressed: () => {
setState(() {
currentStep++;
}),
},
text: "Continuar",
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
);
return [
AddKidScreen(
nextStep: () => {
setState(() {
currentStep++;
}),
},
),
FormStepLayout(
title: "Crea su perfil",
subtitle:
"Necesitamos estos datos para crear su cuenta y gestionar sus pagos y gastos",
currentStep: 1,
numSteps: 3,
body: [CreateProfileScreen()],
footer: [
Container(
padding: EdgeInsets.all(24),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
child: continueBtn,
),
],
nextStep: () => {},
previousStep: () => {},
),
FormStepLayout(
title: "Vincula su correa y su reloj",
currentStep: 2,
numSteps: 3,
body: [LinkWatchPreviousScreen()],
footer: [continueBtn],
nextStep: () => {},
previousStep: () => {},
),
FormStepLayout(
title: "Vincula su correa\ny su reloj",
currentStep: 2,
numSteps: 3,
body: [LinkWatchScreen(step: 1)],
footer: [continueBtn],
nextStep: () => {},
previousStep: () => {},
),
FormStepLayout(
title: "Vincula su correa\ny su reloj",
currentStep: 2,
numSteps: 3,
body: [LinkWatchScreen(step: 2)],
footer: [continueBtn],
nextStep: () => {},
previousStep: () => {},
),
AccountCreatedScreen(navigationContract: navigationContract),
];
}
}

View File

@@ -0,0 +1,82 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class CreateProfileScreen extends ConsumerWidget {
const CreateProfileScreen({super.key});
final bool firstTime = false;
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
return Column(
spacing: 24,
children: [
Text(
"Comienza con un peque; luego podrás agregar más",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
letterSpacing: 0,
),
),
CustomTextField(label: "Nombre", hint: "Nombre"),
CustomTextField(label: "Apellidos", hint: "Apellidos"),
Column(
spacing: 8,
children: [
Align(
alignment: Alignment.bottomLeft,
child: Text(
"Fecha de nacimiento",
style: TextStyle(fontSize: 14, letterSpacing: 0),
),
),
Row(
spacing: 10,
children: [
Expanded(
child: CustomTextField(
keyboardType: TextInputType.number,
hint: "DD",
length: 2,
),
),
Expanded(
child: CustomTextField(
keyboardType: TextInputType.number,
hint: "MM",
length: 2,
),
),
Expanded(
child: CustomTextField(
keyboardType: TextInputType.number,
hint: "AAAA",
length: 4,
),
),
],
),
],
),
CustomTextField(
label: "Dirección completa",
hint: "Nombre de la calle",
),
Align(
alignment: Alignment.topLeft,
child: CustomTextButton(
onPressed: () => {},
text: "Cambiar dirección",
size: 18,
weight: FontWeight.w500,
),
),
],
);
}
}

View File

@@ -0,0 +1,58 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
class LinkWatchPreviousScreen extends ConsumerWidget{
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
return Column(
spacing: 10,
children: [
SvgPicture.asset("assets/images/ui/formulario.svg"),
Row(
spacing: 16,
children: [
Container(
decoration: ShapeDecoration(
shape: CircleBorder(side: BorderSide(color: theme.getColorFor(ThemeCode.backgroundSecondary))),
color: theme.getColorFor(ThemeCode.backgroundSecondary)
),
width: 48,
height: 48,
child: Center(child: Text("1", style: TextStyle(fontSize: 24))),
),
Column(
children: [
Text("Escanea la correa", textAlign: TextAlign.left, style: TextStyle(fontSize: 24)),
Text("El peque podrá realizar pagos"),
],
),
],
),
Row(
spacing: 16,
children: [
Container(
decoration: ShapeDecoration(
shape: CircleBorder(side: BorderSide(color: theme.getColorFor(ThemeCode.backgroundSecondary))),
color: theme.getColorFor(ThemeCode.backgroundSecondary)
),
width: 48,
height: 48,
child: Center(child: Text("2", style: TextStyle(fontSize: 24))),
),
Column(
children: [
Text("Escanea el reloj", textAlign: TextAlign.left, style: TextStyle(fontSize: 24)),
Text("Visualizarás los gastos que se hagan"),
]
)
],
),
],
);
}
}

View File

@@ -0,0 +1,128 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
class LinkWatchScreen extends ConsumerStatefulWidget{
final int step;
const LinkWatchScreen({super.key, required this.step});
@override
ConsumerState<LinkWatchScreen> createState() => LinkWatchScreenState();
}
class LinkWatchScreenState extends ConsumerState<LinkWatchScreen>{
@override
Widget build(BuildContext context) {
final theme = ref.watch(themePortProvider);
return Column(
spacing: 32,
children: [
Stack(
children: [
Divider(
color: theme.getColorFor(ThemeCode.buttonPrimary),
thickness: 4,
indent: 93,
endIndent: 93,
height: 48,
),
if (widget.step==1)Divider(
color: theme.getColorFor(ThemeCode.backgroundSecondary),
thickness: 4,
indent: 186,
endIndent: 93,
height: 48,
),
Container(
padding: EdgeInsets.symmetric(horizontal: 69),
child: Row(
children: [
Container(
decoration: ShapeDecoration(
shape: CircleBorder(),
color: theme.getColorFor(ThemeCode.buttonPrimary)
),
width: 48,
height: 48,
child: Center(child: Text(
"1",
style: TextStyle(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
fontSize: 24
)
))
),
Spacer(),
Container(
decoration: ShapeDecoration(
shape: CircleBorder(),
color: theme.getColorFor(widget.step==1 ? ThemeCode.backgroundSecondary : ThemeCode.buttonPrimary)
),
width: 48,
height: 48,
child: Center(child: Text(
"2",
style: TextStyle(
color: theme.getColorFor(widget.step==1 ? ThemeCode.textPrimary : ThemeCode.backgroundSecondary),
fontSize: 24
)
))
),
],
),
)
],
),
Row(children: [
Spacer(),
Text("Escanea la correa"),
Spacer(),
Text("Escanea el reloj"),
Spacer(),
]),
Container(
padding: EdgeInsets.all(40),
decoration: BoxDecoration(
border: Border.all(color: theme.getColorFor(ThemeCode.textPrimary)),
borderRadius: BorderRadius.all(Radius.circular(16))
),
child: SvgPicture.asset("assets/images/ui/qr.svg")
),
if (widget.step == 2)Column(
spacing: 16,
children: [
Align(
alignment: Alignment.bottomLeft,
child: Text("O inserta el código del reloj"),
),
Row(
spacing: 16,
children: [
Expanded(child: CustomTextField(
hint: "XXXXXXXXXX",
)),
Expanded(child: PrimaryButton(
onPressed: ()=>{},
text: "Continuar con código",
size: 16,
color: theme.getColorFor(ThemeCode.buttonSecondary)
))
],
),
],
),
Column(
spacing: 8,
children: [
Text("Si no consigues vincular su correa o reloj"),
CustomTextButton(onPressed: ()=>{}, text: "Contáctanos", weight: FontWeight.w500, size: 18)
],
)
],
);
}
}

View File

@@ -0,0 +1,5 @@
abstract class LinkPhoneUseCase {
Future<void> requestCode({required String phone});
Future<void> verifyCode({required String phone, required String code});
}

View File

@@ -0,0 +1,20 @@
import 'package:auth/src/core/domain/repositories/auth_repository.dart';
import 'package:auth/src/features/link_phone/domain/use_cases/link_phone_use_case.dart';
class LinkPhoneUseCaseImpl implements LinkPhoneUseCase {
LinkPhoneUseCaseImpl(this._repository);
final AuthRepository _repository;
@override
Future<void> requestCode({required String phone}) async {
// return _repository.requestPhoneCode(phone: phone);
await Future<void>.delayed(const Duration(milliseconds: 500));
}
@override
Future<void> verifyCode({required String phone, required String code}) async {
// return _repository.verifyPhoneCode(phone: phone, code: code);
await Future<void>.delayed(const Duration(milliseconds: 500));
}
}

View File

@@ -0,0 +1,9 @@
import 'package:auth/src/core/providers/auth_repository_provider.dart';
import 'package:auth/src/features/link_phone/domain/use_cases/link_phone_use_case.dart';
import 'package:auth/src/features/link_phone/domain/use_cases/link_phone_use_case_impl.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final linkPhoneUseCaseProvider = Provider.autoDispose<LinkPhoneUseCase>((ref) {
final authRepository = ref.read(authRepositoryProvider);
return LinkPhoneUseCaseImpl(authRepository);
});

View File

@@ -0,0 +1,18 @@
import 'package:auth/src/features/link_phone/presentation/request_phone/request_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 RequestLinkPhoneBuilder {
const RequestLinkPhoneBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
return MaterialPage<void>(
key: state.pageKey,
child: RequestLinkPhoneScreen(navigationContract: navigationContract),
);
}
}

View File

@@ -0,0 +1,110 @@
import 'package:auth/src/features/link_phone/presentation/state/link_phone_view_model.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
class RequestLinkPhoneScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const RequestLinkPhoneScreen({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final viewModel = ref.read(linkPhoneViewModelProvider.notifier);
final viewState = ref.watch(linkPhoneViewModelProvider);
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
context.translate(I18n.linkPhoneTitle),
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.w500,
letterSpacing: 0,
),
),
const SizedBox(height: 24),
Text(
context.translate(I18n.linkPhoneSubtitle),
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 16, letterSpacing: 0),
),
const SizedBox(height: 48),
Column(
spacing: 8,
children: [
Align(
alignment: Alignment.bottomLeft,
child: Text(
context.translate(I18n.mobilePhone),
style: const TextStyle(fontSize: 14, letterSpacing: 0),
),
),
Row(
spacing: 10,
children: [
CountryPrefixPicker(
headerText: context.translate(I18n.selectYourCountry),
initialCountryCode: viewState.dialCode,
onChanged: (country) {
viewModel.updateDialCode(
country.dialCode ?? viewState.dialCode,
);
},
),
Expanded(
child: CustomTextField(
controller: viewModel.phoneNumberController,
hint: context.translate(I18n.phoneNumber),
keyboardType: TextInputType.number,
),
),
],
),
],
),
const SizedBox(height: 16),
if (viewState.errorMessage.isNotEmpty) ...[
const SizedBox(height: 4),
Text(
viewState.errorMessage,
textAlign: TextAlign.center,
style: const TextStyle(
color: Color.fromRGBO(239, 17, 17, 1),
fontSize: 12,
),
),
],
const SizedBox(height: 24),
PrimaryButton(
onPressed: () async {
await viewModel.requestCode();
final updatedState = ref.read(linkPhoneViewModelProvider);
if (updatedState.errorMessage.isEmpty) {
navigationContract.pushTo(AppRoutes.phoneCode);
}
},
text: context.translate(I18n.next),
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
],
),
),
);
}
}

View File

@@ -0,0 +1,148 @@
import 'package:auth/src/features/link_phone/presentation/providers/link_phone_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:auth/src/features/link_phone/domain/use_cases/link_phone_use_case.dart';
import 'package:auth/src/features/link_phone/presentation/state/link_phone_view_state.dart';
final linkPhoneViewModelProvider =
NotifierProvider.autoDispose<LinkPhoneViewModel, LinkPhoneViewState>(
LinkPhoneViewModel.new,
);
class LinkPhoneViewModel extends Notifier<LinkPhoneViewState> {
late final LinkPhoneUseCase _linkPhoneUseCase;
late final TextEditingController phoneNumberController;
late final TextEditingController codeController;
@override
LinkPhoneViewState build() {
_linkPhoneUseCase = ref.read(linkPhoneUseCaseProvider);
phoneNumberController = TextEditingController();
phoneNumberController.addListener(_onPhoneNumberChanged);
codeController = TextEditingController();
ref.onDispose(disposeControllers);
return const LinkPhoneViewState();
}
void _onPhoneNumberChanged() {
final raw = phoneNumberController.text;
state = state.copyWith(
phoneNumber: raw,
errorMessage: '',
codeVerified: false,
);
}
void updateDialCode(String dialCode) {
state = state.copyWith(
dialCode: dialCode,
errorMessage: '',
codeVerified: false,
);
}
void updateCode(String code) {
codeController.text = code;
state = state.copyWith(errorMessage: '', codeVerified: false);
}
Future<void> requestCode() async {
final trimmedNumber = state.phoneNumber.trim();
if (trimmedNumber.isEmpty) {
state = state.copyWith(
errorMessage: 'errorMessagePhoneIsEmpty',
codeVerified: false,
);
return;
}
final fullPhone = '${state.dialCode}$trimmedNumber';
state = state.copyWith(
isLoading: true,
errorMessage: '',
codeRequested: false,
codeVerified: false,
);
try {
await _linkPhoneUseCase.requestCode(phone: fullPhone);
if (!ref.mounted) return;
state = state.copyWith(
isLoading: false,
errorMessage: '',
codeRequested: true,
);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(
isLoading: false,
errorMessage: e.toString(),
codeRequested: false,
codeVerified: false,
);
}
}
Future<void> verifyCode() async {
final dialCode = state.dialCode;
final phoneNumber = state.phoneNumber.trim();
final code = codeController.text.trim();
final fullPhone = '$dialCode$phoneNumber';
if (phoneNumber.isEmpty) {
state = state.copyWith(
errorMessage: 'errorMessagePhoneIsEmpty',
codeVerified: false,
);
return;
}
if (code.isEmpty) {
state = state.copyWith(
errorMessage: 'errorMessageCodeIsEmpty',
codeVerified: false,
);
return;
}
state = state.copyWith(
isLoading: true,
errorMessage: '',
codeVerified: false,
);
try {
await _linkPhoneUseCase.verifyCode(phone: fullPhone, code: code);
if (!ref.mounted) return;
state = state.copyWith(
isLoading: false,
errorMessage: '',
codeVerified: true,
);
} catch (e) {
if (!ref.mounted) return;
state = state.copyWith(
isLoading: false,
errorMessage: e.toString(),
codeVerified: false,
);
}
}
void disposeControllers() {
phoneNumberController.removeListener(_onPhoneNumberChanged);
phoneNumberController.dispose();
codeController.dispose();
}
}

View File

@@ -0,0 +1,15 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'link_phone_view_state.freezed.dart';
@freezed
abstract class LinkPhoneViewState with _$LinkPhoneViewState {
const factory LinkPhoneViewState({
@Default('') String phoneNumber,
@Default('+34') String dialCode,
@Default('') String errorMessage,
@Default(false) bool isLoading,
@Default(false) bool codeRequested,
@Default(false) bool codeVerified,
}) = _LinkPhoneViewState;
}

View File

@@ -0,0 +1,286 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'link_phone_view_state.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$LinkPhoneViewState {
String get phoneNumber; String get dialCode; String get errorMessage; bool get isLoading; bool get codeRequested; bool get codeVerified;
/// Create a copy of LinkPhoneViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$LinkPhoneViewStateCopyWith<LinkPhoneViewState> get copyWith => _$LinkPhoneViewStateCopyWithImpl<LinkPhoneViewState>(this as LinkPhoneViewState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is LinkPhoneViewState&&(identical(other.phoneNumber, phoneNumber) || other.phoneNumber == phoneNumber)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.codeRequested, codeRequested) || other.codeRequested == codeRequested)&&(identical(other.codeVerified, codeVerified) || other.codeVerified == codeVerified));
}
@override
int get hashCode => Object.hash(runtimeType,phoneNumber,dialCode,errorMessage,isLoading,codeRequested,codeVerified);
@override
String toString() {
return 'LinkPhoneViewState(phoneNumber: $phoneNumber, dialCode: $dialCode, errorMessage: $errorMessage, isLoading: $isLoading, codeRequested: $codeRequested, codeVerified: $codeVerified)';
}
}
/// @nodoc
abstract mixin class $LinkPhoneViewStateCopyWith<$Res> {
factory $LinkPhoneViewStateCopyWith(LinkPhoneViewState value, $Res Function(LinkPhoneViewState) _then) = _$LinkPhoneViewStateCopyWithImpl;
@useResult
$Res call({
String phoneNumber, String dialCode, String errorMessage, bool isLoading, bool codeRequested, bool codeVerified
});
}
/// @nodoc
class _$LinkPhoneViewStateCopyWithImpl<$Res>
implements $LinkPhoneViewStateCopyWith<$Res> {
_$LinkPhoneViewStateCopyWithImpl(this._self, this._then);
final LinkPhoneViewState _self;
final $Res Function(LinkPhoneViewState) _then;
/// Create a copy of LinkPhoneViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? phoneNumber = null,Object? dialCode = null,Object? errorMessage = null,Object? isLoading = null,Object? codeRequested = null,Object? codeVerified = null,}) {
return _then(_self.copyWith(
phoneNumber: null == phoneNumber ? _self.phoneNumber : phoneNumber // ignore: cast_nullable_to_non_nullable
as String,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,codeRequested: null == codeRequested ? _self.codeRequested : codeRequested // ignore: cast_nullable_to_non_nullable
as bool,codeVerified: null == codeVerified ? _self.codeVerified : codeVerified // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// Adds pattern-matching-related methods to [LinkPhoneViewState].
extension LinkPhoneViewStatePatterns on LinkPhoneViewState {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _LinkPhoneViewState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _LinkPhoneViewState() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _LinkPhoneViewState value) $default,){
final _that = this;
switch (_that) {
case _LinkPhoneViewState():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _LinkPhoneViewState value)? $default,){
final _that = this;
switch (_that) {
case _LinkPhoneViewState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String phoneNumber, String dialCode, String errorMessage, bool isLoading, bool codeRequested, bool codeVerified)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _LinkPhoneViewState() when $default != null:
return $default(_that.phoneNumber,_that.dialCode,_that.errorMessage,_that.isLoading,_that.codeRequested,_that.codeVerified);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String phoneNumber, String dialCode, String errorMessage, bool isLoading, bool codeRequested, bool codeVerified) $default,) {final _that = this;
switch (_that) {
case _LinkPhoneViewState():
return $default(_that.phoneNumber,_that.dialCode,_that.errorMessage,_that.isLoading,_that.codeRequested,_that.codeVerified);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String phoneNumber, String dialCode, String errorMessage, bool isLoading, bool codeRequested, bool codeVerified)? $default,) {final _that = this;
switch (_that) {
case _LinkPhoneViewState() when $default != null:
return $default(_that.phoneNumber,_that.dialCode,_that.errorMessage,_that.isLoading,_that.codeRequested,_that.codeVerified);case _:
return null;
}
}
}
/// @nodoc
class _LinkPhoneViewState implements LinkPhoneViewState {
const _LinkPhoneViewState({this.phoneNumber = '', this.dialCode = '+34', this.errorMessage = '', this.isLoading = false, this.codeRequested = false, this.codeVerified = false});
@override@JsonKey() final String phoneNumber;
@override@JsonKey() final String dialCode;
@override@JsonKey() final String errorMessage;
@override@JsonKey() final bool isLoading;
@override@JsonKey() final bool codeRequested;
@override@JsonKey() final bool codeVerified;
/// Create a copy of LinkPhoneViewState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$LinkPhoneViewStateCopyWith<_LinkPhoneViewState> get copyWith => __$LinkPhoneViewStateCopyWithImpl<_LinkPhoneViewState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _LinkPhoneViewState&&(identical(other.phoneNumber, phoneNumber) || other.phoneNumber == phoneNumber)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.codeRequested, codeRequested) || other.codeRequested == codeRequested)&&(identical(other.codeVerified, codeVerified) || other.codeVerified == codeVerified));
}
@override
int get hashCode => Object.hash(runtimeType,phoneNumber,dialCode,errorMessage,isLoading,codeRequested,codeVerified);
@override
String toString() {
return 'LinkPhoneViewState(phoneNumber: $phoneNumber, dialCode: $dialCode, errorMessage: $errorMessage, isLoading: $isLoading, codeRequested: $codeRequested, codeVerified: $codeVerified)';
}
}
/// @nodoc
abstract mixin class _$LinkPhoneViewStateCopyWith<$Res> implements $LinkPhoneViewStateCopyWith<$Res> {
factory _$LinkPhoneViewStateCopyWith(_LinkPhoneViewState value, $Res Function(_LinkPhoneViewState) _then) = __$LinkPhoneViewStateCopyWithImpl;
@override @useResult
$Res call({
String phoneNumber, String dialCode, String errorMessage, bool isLoading, bool codeRequested, bool codeVerified
});
}
/// @nodoc
class __$LinkPhoneViewStateCopyWithImpl<$Res>
implements _$LinkPhoneViewStateCopyWith<$Res> {
__$LinkPhoneViewStateCopyWithImpl(this._self, this._then);
final _LinkPhoneViewState _self;
final $Res Function(_LinkPhoneViewState) _then;
/// Create a copy of LinkPhoneViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? phoneNumber = null,Object? dialCode = null,Object? errorMessage = null,Object? isLoading = null,Object? codeRequested = null,Object? codeVerified = null,}) {
return _then(_LinkPhoneViewState(
phoneNumber: null == phoneNumber ? _self.phoneNumber : phoneNumber // ignore: cast_nullable_to_non_nullable
as String,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,codeRequested: null == codeRequested ? _self.codeRequested : codeRequested // ignore: cast_nullable_to_non_nullable
as bool,codeVerified: null == codeVerified ? _self.codeVerified : codeVerified // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
// dart format on

View File

@@ -0,0 +1,18 @@
import 'package:auth/src/features/link_phone/presentation/verify_code/verify_link_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 VerifyLinkPhoneCodeBuilder {
const VerifyLinkPhoneCodeBuilder();
Page<void> buildPage(BuildContext context, GoRouterState state) {
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
return MaterialPage<void>(
key: state.pageKey,
child: VerifyLinkPhoneCodeScreen(navigationContract: navigationContract),
);
}
}

View File

@@ -0,0 +1,118 @@
import 'package:auth/src/features/link_phone/presentation/state/link_phone_view_model.dart';
import 'package:auth/src/features/link_phone/presentation/widgets/link_phone_code_input.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:navigation/navigation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_localizations/sf_localizations.dart';
class VerifyLinkPhoneCodeScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const VerifyLinkPhoneCodeScreen({
super.key,
required this.navigationContract,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final viewModel = ref.read(linkPhoneViewModelProvider.notifier);
final viewState = ref.watch(linkPhoneViewModelProvider);
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 48,
children: [
Column(
children: [
Text(
context.translate(I18n.connect),
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
),
const SizedBox(height: 24),
Text.rich(
TextSpan(
text: context.translate(I18n.verificationCodeSentTo),
children: [
TextSpan(
text: '${viewState.dialCode}${viewState.phoneNumber}',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
],
),
textAlign: TextAlign.center,
),
const SizedBox(height: 48),
Text(
context.translate(I18n.enterCodeHere),
style: TextStyle(fontSize: 16),
),
const SizedBox(height: 24),
LinkPhoneCodeInput(
length: 6,
onCodeChanged: viewModel.updateCode,
),
if (viewState.errorMessage.isNotEmpty) ...[
const SizedBox(height: 8),
Text(
viewState.errorMessage,
textAlign: TextAlign.center,
style: const TextStyle(
color: Color.fromRGBO(239, 17, 17, 1),
fontSize: 12,
),
),
],
],
),
Column(
children: [
PrimaryButton(
onPressed: () async {
await viewModel.verifyCode();
final updatedState = ref.read(linkPhoneViewModelProvider);
if (updatedState.codeVerified) {
navigationContract.pushTo(AppRoutes.login);
}
},
text: context.translate(I18n.enter),
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
const SizedBox(height: 24),
Text(
context.translate(I18n.didNotReceiveIt),
style: TextStyle(
fontSize: 18,
letterSpacing: 0,
height: 1.5,
),
),
const SizedBox(height: 8),
CustomTextButton(
onPressed: () => navigationContract.goBack(),
text: context.translate(I18n.tryAgain),
size: 18,
weight: FontWeight.w500,
),
],
),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,86 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class LinkPhoneCodeInput extends StatefulWidget {
const LinkPhoneCodeInput({
super.key,
this.length = 6,
required this.onCodeChanged,
});
final int length;
final ValueChanged<String> onCodeChanged;
@override
State<LinkPhoneCodeInput> createState() => _LinkPhoneCodeInputState();
}
class _LinkPhoneCodeInputState extends State<LinkPhoneCodeInput> {
late final List<TextEditingController> _controllers;
late final List<FocusNode> _focusNodes;
@override
void initState() {
super.initState();
_controllers = List<TextEditingController>.generate(
widget.length,
(_) => TextEditingController(),
);
_focusNodes = List<FocusNode>.generate(widget.length, (_) => FocusNode());
}
@override
void dispose() {
for (final controller in _controllers) {
controller.dispose();
}
for (final node in _focusNodes) {
node.dispose();
}
super.dispose();
}
void _onDigitChanged(int index, String value) {
if (value.length > 1) {
final single = value.characters.last;
_controllers[index].text = single;
_controllers[index].selection = TextSelection.fromPosition(
TextPosition(offset: single.length),
);
}
if (value.isNotEmpty && index < widget.length - 1) {
_focusNodes[index + 1].requestFocus();
} else if (value.isEmpty && index > 0) {
_focusNodes[index - 1].requestFocus();
}
final code = _controllers.map((c) => c.text).join();
widget.onCodeChanged(code);
}
@override
Widget build(BuildContext context) {
return Row(
spacing: 8,
children: List<Widget>.generate(widget.length, (int i) {
return Expanded(
child: TextField(
controller: _controllers[i],
focusNode: _focusNodes[i],
keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
textAlign: TextAlign.center,
decoration: const InputDecoration(
hintText: '0',
counterText: '',
border: OutlineInputBorder(),
),
maxLength: 1,
onChanged: (value) => _onDigitChanged(i, value),
),
);
}),
);
}
}

View File

@@ -0,0 +1,3 @@
abstract class LoginUseCase {
Future<String> login({required String email, required String password});
}

View File

@@ -0,0 +1,13 @@
import 'package:auth/src/core/domain/repositories/auth_repository.dart';
import 'package:auth/src/features/login/domain/login_use_case.dart';
class LoginUseCaseImpl implements LoginUseCase {
LoginUseCaseImpl(this._repository);
final AuthRepository _repository;
@override
Future<String> login({required String email, required String password}) {
return _repository.login(email: email, password: password);
}
}

View File

@@ -0,0 +1,3 @@
abstract class TwoFactorUseCase {
Future<void> twoFactor({required String token, required String code});
}

View File

@@ -0,0 +1,13 @@
import 'package:auth/src/core/domain/repositories/auth_repository.dart';
import 'package:auth/src/features/login/domain/two_factor_use_case.dart';
class TwoFactorUseCaseImpl implements TwoFactorUseCase {
TwoFactorUseCaseImpl(this._repository);
final AuthRepository _repository;
@override
Future<void> twoFactor({required String token, required String code}) {
return _repository.twoFactor(token: token, code: code);
}
}

View File

@@ -1,4 +1,4 @@
import 'package:auth/src/login/presentation/login_screen.dart';
import 'package:auth/src/features/login/presentation/login_screen.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart';

View File

@@ -0,0 +1,408 @@
import 'package:auth/src/features/login/presentation/loading_google_screen.dart';
import 'package:auth/src/features/login/presentation/state/login_view_model.dart';
import 'package:auth/src/features/login/presentation/widgets/two_factor_bottom_sheet.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
class LoginScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const LoginScreen({super.key, required this.navigationContract});
Future<void> _onLogIn(BuildContext context, WidgetRef ref) async {
FocusManager.instance.primaryFocus?.unfocus();
final vm = ref.read(loginViewModelProvider.notifier);
final String? token = await vm.login();
if (!context.mounted) return;
if (token == null || token.isEmpty) return;
vm.prepareTwoFactor();
final bool? verified = await showModalBottomSheet<bool>(
context: context,
isScrollControlled: true,
useSafeArea: true,
isDismissible: false,
enableDrag: false,
backgroundColor: Colors.transparent,
builder: (context) {
return Consumer(
builder: (context, ref, _) {
final theme = ref.watch(themePortProvider);
final vm = ref.read(loginViewModelProvider.notifier);
final otpErrorKey = ref.watch(
loginViewModelProvider.select((s) => s.otpError),
);
final isOtpLoading = ref.watch(
loginViewModelProvider.select((s) => s.isOtpLoading),
);
final otpCode = ref.watch(
loginViewModelProvider.select((s) => s.otpCode),
);
final otpErrorText = otpErrorKey.isEmpty
? ''
: context.translate(otpErrorKey);
Future<void> onVerify() async {
FocusManager.instance.primaryFocus?.unfocus();
final ok = await vm.twoFactor(token: token);
if (!context.mounted) return;
if (ok) Navigator.of(context).pop(true);
}
return TwoFactorBottomSheetView(
theme: theme,
title: context.translate(I18n.twoFactorTitle),
subtitle: context.translate(I18n.twoFactorSubtitle),
verifyText: context.translate(I18n.twoFactorVerify),
closeText: context.translate(I18n.close),
isOtpLoading: isOtpLoading,
otpCode: otpCode,
otpErrorText: otpErrorText,
onChanged: vm.setOtpCode,
onVerify: onVerify,
onClose: () => Navigator.of(context).pop(false),
);
},
);
},
);
if (!context.mounted) return;
if (verified == true) {
navigationContract.goTo(AppRoutes.dashboardHome);
}
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final bool isLoading = ref.watch(
loginViewModelProvider.select((s) => s.isLoading),
);
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: SafeArea(
child: AbsorbPointer(
absorbing: isLoading,
child: SingleChildScrollView(
padding: EdgeInsets.symmetric(horizontal: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_Header(theme: theme),
SizedBox(height: 48),
const _EmailSection(),
SizedBox(height: 24),
_PasswordSection(onSubmitted: () => _onLogIn(context, ref)),
SizedBox(height: 16),
_ForgotPassword(navigationContract: navigationContract),
SizedBox(height: 30),
_SignInSection(
theme: theme,
onSignIn: () => _onLogIn(context, ref),
),
SizedBox(height: 30),
_OrContinueWith(theme: theme),
SizedBox(height: 24),
_SocialButtons(theme: theme),
SizedBox(height: 30),
_Footer(navigationContract: navigationContract),
],
),
),
),
),
);
}
}
class _Header extends StatelessWidget {
const _Header({required this.theme});
final ThemePort theme;
@override
Widget build(BuildContext context) {
return Column(
children: [
Icon(
Icons.check,
color: theme.getColorFor(ThemeCode.buttonPrimary),
size: 54,
),
Text(
context.translate(I18n.welcome),
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
),
],
);
}
}
class _EmailSection extends ConsumerWidget {
const _EmailSection();
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(loginViewModelProvider.notifier);
final String emailErrorKey = ref.watch(
loginViewModelProvider.select((s) => s.emailError),
);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
CustomTextField(
hint: context.translate(I18n.username),
label: context.translate(I18n.username),
controller: vm.emailController,
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
),
_FieldErrorText.fromKey(errorKey: emailErrorKey),
],
);
}
}
class _PasswordSection extends ConsumerWidget {
const _PasswordSection({required this.onSubmitted});
final Future<void> Function() onSubmitted;
@override
Widget build(BuildContext context, WidgetRef ref) {
final vm = ref.read(loginViewModelProvider.notifier);
final bool passwordVisible = ref.watch(
loginViewModelProvider.select((s) => s.passwordVisible),
);
final String passwordErrorKey = ref.watch(
loginViewModelProvider.select((s) => s.passwordError),
);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
CustomTextField(
showPassword: passwordVisible,
label: context.translate(I18n.password),
hint: '********',
controller: vm.passwordController,
textInputAction: TextInputAction.done,
onSubmitted: (_) => onSubmitted(),
),
_FieldErrorText.fromKey(errorKey: passwordErrorKey),
],
);
}
}
class _ForgotPassword extends ConsumerWidget {
const _ForgotPassword({required this.navigationContract});
final NavigationContract navigationContract;
@override
Widget build(BuildContext context, WidgetRef ref) {
final bool isLoading = ref.watch(
loginViewModelProvider.select((s) => s.isLoading),
);
return Align(
alignment: Alignment.topLeft,
child: CustomTextButton(
text: context.translate(I18n.forgotPassword),
onPressed: isLoading
? () {}
: () => navigationContract.pushTo(AppRoutes.recoverPassword),
size: 16,
),
);
}
}
class _SignInSection extends ConsumerWidget {
const _SignInSection({required this.onSignIn, required this.theme});
final VoidCallback onSignIn;
final ThemePort theme;
@override
Widget build(BuildContext context, WidgetRef ref) {
final bool isLoading = ref.watch(
loginViewModelProvider.select((s) => s.isLoading),
);
final String errorMessage = ref.watch(
loginViewModelProvider.select((s) => s.errorMessage),
);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
PrimaryButton(
onPressed: isLoading ? () {} : onSignIn,
text: context.translate(I18n.signIn),
color: theme.getColorFor(ThemeCode.buttonPrimary),
leading: isLoading
? const SizedBox(
height: 18,
width: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: null,
),
if (errorMessage.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 12),
child: Text(
errorMessage,
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
fontSize: 13,
),
),
),
],
);
}
}
class _OrContinueWith extends StatelessWidget {
const _OrContinueWith({required this.theme});
final ThemePort theme;
@override
Widget build(BuildContext context) {
return Stack(
children: [
const Divider(endIndent: 74, indent: 74),
Align(
alignment: Alignment.center,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 14),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
child: Text(context.translate(I18n.orContinueWith)),
),
),
],
);
}
}
class _SocialButtons extends ConsumerWidget {
const _SocialButtons({required this.theme});
final ThemePort theme;
@override
Widget build(BuildContext context, WidgetRef ref) {
final bool isLoading = ref.watch(
loginViewModelProvider.select((s) => s.isLoading),
);
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SecondaryButton(
onPressed: isLoading
? () {}
: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const LoadingGoogleScreen(),
),
),
radius: 16,
padding: 44,
text: context.translate(I18n.google),
label: 'Google',
),
const SizedBox(width: 16),
SecondaryButton(
onPressed: isLoading ? () {} : () {},
radius: 16,
padding: 44,
icon: Icons.apple,
label: 'Apple',
),
],
);
}
}
class _Footer extends ConsumerWidget {
const _Footer({required this.navigationContract});
final NavigationContract navigationContract;
@override
Widget build(BuildContext context, WidgetRef ref) {
final bool isLoading = ref.watch(
loginViewModelProvider.select((s) => s.isLoading),
);
return Column(
children: [
Text(
context.translate(I18n.dontHaveAccount),
style: const TextStyle(fontSize: 18, letterSpacing: 0),
),
TextButton(
onPressed: isLoading
? null
: () => navigationContract.pushTo(AppRoutes.signup),
child: Text(
context.translate(I18n.createOneNow),
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w500,
letterSpacing: 0,
),
),
),
],
);
}
}
class _FieldErrorText extends StatelessWidget {
const _FieldErrorText._({required this.text});
final String text;
factory _FieldErrorText.fromKey({required String errorKey}) {
return _FieldErrorText._(text: errorKey);
}
@override
Widget build(BuildContext context) {
if (text.isEmpty) return const SizedBox.shrink();
return Padding(
padding: const EdgeInsets.only(top: 8),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
context.translate(text),
style: TextStyle(
color: Theme.of(context).colorScheme.error,
fontSize: 12,
),
),
),
);
}
}

View File

@@ -0,0 +1,9 @@
import 'package:auth/src/core/providers/auth_repository_provider.dart';
import 'package:auth/src/features/login/domain/login_use_case.dart';
import 'package:auth/src/features/login/domain/login_use_case_impl.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final loginUseCaseProvider = Provider.autoDispose<LoginUseCase>((ref) {
final authRepository = ref.read(authRepositoryProvider);
return LoginUseCaseImpl(authRepository);
});

View File

@@ -0,0 +1,9 @@
import 'package:auth/src/core/providers/auth_repository_provider.dart';
import 'package:auth/src/features/login/domain/two_factor_use_case.dart';
import 'package:auth/src/features/login/domain/two_factor_use_case_impl.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final twoFactorUseCaseProvider = Provider.autoDispose<TwoFactorUseCase>((ref) {
final authRepository = ref.read(authRepositoryProvider);
return TwoFactorUseCaseImpl(authRepository);
});

View File

@@ -0,0 +1,202 @@
import 'package:auth/src/features/login/domain/login_use_case.dart';
import 'package:auth/src/features/login/domain/two_factor_use_case.dart';
import 'package:auth/src/features/login/presentation/providers/login_provider.dart';
import 'package:auth/src/features/login/presentation/providers/two_factor_provider.dart';
import 'package:auth/src/features/login/presentation/state/login_view_state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sf_localizations/sf_localizations.dart';
final loginViewModelProvider =
NotifierProvider.autoDispose<LoginViewModel, LoginViewState>(
LoginViewModel.new,
);
class LoginViewModel extends Notifier<LoginViewState> {
late final LoginUseCase _loginUseCase;
late final TwoFactorUseCase _twoFactorUseCase;
late final TextEditingController emailController;
late final TextEditingController passwordController;
late final TextEditingController otpController;
static final RegExp _emailRegex = RegExp(
r'^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$',
caseSensitive: false,
);
@override
LoginViewState build() {
_loginUseCase = ref.read(loginUseCaseProvider);
_twoFactorUseCase = ref.read(twoFactorUseCaseProvider);
emailController = TextEditingController();
passwordController = TextEditingController();
otpController = TextEditingController();
emailController.addListener(_onEmailChanged);
passwordController.addListener(_onPasswordChanged);
otpController.addListener(_onOtpChanged);
ref.onDispose(disposeControllers);
return const LoginViewState();
}
void _onEmailChanged() {
final text = emailController.text;
if (text == state.email) return;
state = state.copyWith(email: text, errorMessage: '');
if (state.showErrors) {
state = state.copyWith(emailError: _emailErrorFor(text));
}
}
void _onPasswordChanged() {
final text = passwordController.text;
if (text == state.password) return;
state = state.copyWith(password: text, errorMessage: '');
if (state.showErrors) {
state = state.copyWith(passwordError: _passwordErrorFor(text));
}
}
void togglePasswordVisible() {
state = state.copyWith(passwordVisible: !state.passwordVisible);
}
bool _isValidEmail(String email) => _emailRegex.hasMatch(email);
String _emailErrorFor(String value) {
final email = value.trim();
if (email.isEmpty) return I18n.errorEmailRequired;
if (!_isValidEmail(email)) return I18n.errorEmailInvalid;
return '';
}
String _passwordErrorFor(String value) {
final password = value.trim();
if (password.isEmpty) return I18n.errorPasswordRequired;
if (password.length < 6) return I18n.errorPasswordMinLength;
return '';
}
bool _validateForm() {
final emailError = _emailErrorFor(state.email);
final passwordError = _passwordErrorFor(state.password);
state = state.copyWith(
showErrors: true,
emailError: emailError,
passwordError: passwordError,
errorMessage: '',
);
return emailError.isEmpty && passwordError.isEmpty;
}
Future<String?> login() async {
if (!_validateForm()) return null;
final email = state.email.trim();
final password = state.password.trim();
state = state.copyWith(isLoading: true, errorMessage: '');
try {
final String token = await _loginUseCase.login(
email: email,
password: password,
);
if (!ref.mounted) return null;
state = state.copyWith(isLoading: false);
return token;
} catch (e) {
if (!ref.mounted) return null;
state = state.copyWith(isLoading: false, errorMessage: e.toString());
return null;
}
}
void prepareTwoFactor() {
otpController.text = '';
state = state.copyWith(otpCode: '', otpError: '', isOtpLoading: false);
}
void _onOtpChanged() {
final raw = otpController.text;
if (raw == state.otpCode) return;
state = state.copyWith(otpCode: raw, otpError: '');
if (state.showErrors) {
state = state.copyWith(otpError: _otpErrorFor(raw));
}
}
void setOtpCode(String code) {
state = state.copyWith(otpCode: code, otpError: '');
}
String _otpErrorFor(String value) {
final code = value.trim();
if (code.isEmpty) return I18n.errorTwoFactorCodeRequired;
if (code.length != 6) return I18n.errorTwoFactorCodeInvalidLength;
return '';
}
bool _validateOtp() {
final otpError = _otpErrorFor(state.otpCode);
state = state.copyWith(
showErrors: true,
otpError: otpError,
errorMessage: '',
);
return otpError.isEmpty;
}
Future<bool> twoFactor({required String token}) async {
if (!_validateOtp()) return false;
final code = state.otpCode.trim();
state = state.copyWith(isOtpLoading: true, otpError: '', errorMessage: '');
try {
await _twoFactorUseCase.twoFactor(token: token, code: code);
if (!ref.mounted) return false;
state = state.copyWith(isOtpLoading: false);
return true;
} catch (e) {
if (!ref.mounted) return false;
state = state.copyWith(
isOtpLoading: false,
otpError: I18n.errorTwoFactorCodeInvalid,
);
return false;
}
}
void disposeControllers() {
emailController.removeListener(_onEmailChanged);
passwordController.removeListener(_onPasswordChanged);
otpController.removeListener(_onOtpChanged);
emailController.dispose();
passwordController.dispose();
otpController.dispose();
}
}

View File

@@ -0,0 +1,21 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'login_view_state.freezed.dart';
@freezed
abstract class LoginViewState with _$LoginViewState {
const factory LoginViewState({
@Default('') String email,
@Default('') String password,
@Default(false) bool passwordVisible,
@Default('') String emailError,
@Default('') String passwordError,
@Default('') String errorMessage,
@Default(false) bool showErrors,
@Default(false) bool isLoading,
@Default('') String token,
@Default('') String otpCode,
@Default('') String otpError,
@Default(false) bool isOtpLoading,
}) = _LoginViewState;
}

View File

@@ -0,0 +1,304 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'login_view_state.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$LoginViewState {
String get email; String get password; bool get passwordVisible; String get emailError; String get passwordError; String get errorMessage; bool get showErrors; bool get isLoading; String get token; String get otpCode; String get otpError; bool get isOtpLoading;
/// Create a copy of LoginViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$LoginViewStateCopyWith<LoginViewState> get copyWith => _$LoginViewStateCopyWithImpl<LoginViewState>(this as LoginViewState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is LoginViewState&&(identical(other.email, email) || other.email == email)&&(identical(other.password, password) || other.password == password)&&(identical(other.passwordVisible, passwordVisible) || other.passwordVisible == passwordVisible)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.token, token) || other.token == token)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading));
}
@override
int get hashCode => Object.hash(runtimeType,email,password,passwordVisible,emailError,passwordError,errorMessage,showErrors,isLoading,token,otpCode,otpError,isOtpLoading);
@override
String toString() {
return 'LoginViewState(email: $email, password: $password, passwordVisible: $passwordVisible, emailError: $emailError, passwordError: $passwordError, errorMessage: $errorMessage, showErrors: $showErrors, isLoading: $isLoading, token: $token, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading)';
}
}
/// @nodoc
abstract mixin class $LoginViewStateCopyWith<$Res> {
factory $LoginViewStateCopyWith(LoginViewState value, $Res Function(LoginViewState) _then) = _$LoginViewStateCopyWithImpl;
@useResult
$Res call({
String email, String password, bool passwordVisible, String emailError, String passwordError, String errorMessage, bool showErrors, bool isLoading, String token, String otpCode, String otpError, bool isOtpLoading
});
}
/// @nodoc
class _$LoginViewStateCopyWithImpl<$Res>
implements $LoginViewStateCopyWith<$Res> {
_$LoginViewStateCopyWithImpl(this._self, this._then);
final LoginViewState _self;
final $Res Function(LoginViewState) _then;
/// Create a copy of LoginViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? email = null,Object? password = null,Object? passwordVisible = null,Object? emailError = null,Object? passwordError = null,Object? errorMessage = null,Object? showErrors = null,Object? isLoading = null,Object? token = null,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,}) {
return _then(_self.copyWith(
email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable
as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String,passwordVisible: null == passwordVisible ? _self.passwordVisible : passwordVisible // ignore: cast_nullable_to_non_nullable
as bool,emailError: null == emailError ? _self.emailError : emailError // ignore: cast_nullable_to_non_nullable
as String,passwordError: null == passwordError ? _self.passwordError : passwordError // ignore: cast_nullable_to_non_nullable
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,showErrors: null == showErrors ? _self.showErrors : showErrors // ignore: cast_nullable_to_non_nullable
as bool,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable
as String,otpCode: null == otpCode ? _self.otpCode : otpCode // ignore: cast_nullable_to_non_nullable
as String,otpError: null == otpError ? _self.otpError : otpError // ignore: cast_nullable_to_non_nullable
as String,isOtpLoading: null == isOtpLoading ? _self.isOtpLoading : isOtpLoading // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// Adds pattern-matching-related methods to [LoginViewState].
extension LoginViewStatePatterns on LoginViewState {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _LoginViewState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _LoginViewState() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _LoginViewState value) $default,){
final _that = this;
switch (_that) {
case _LoginViewState():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _LoginViewState value)? $default,){
final _that = this;
switch (_that) {
case _LoginViewState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String email, String password, bool passwordVisible, String emailError, String passwordError, String errorMessage, bool showErrors, bool isLoading, String token, String otpCode, String otpError, bool isOtpLoading)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _LoginViewState() when $default != null:
return $default(_that.email,_that.password,_that.passwordVisible,_that.emailError,_that.passwordError,_that.errorMessage,_that.showErrors,_that.isLoading,_that.token,_that.otpCode,_that.otpError,_that.isOtpLoading);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String email, String password, bool passwordVisible, String emailError, String passwordError, String errorMessage, bool showErrors, bool isLoading, String token, String otpCode, String otpError, bool isOtpLoading) $default,) {final _that = this;
switch (_that) {
case _LoginViewState():
return $default(_that.email,_that.password,_that.passwordVisible,_that.emailError,_that.passwordError,_that.errorMessage,_that.showErrors,_that.isLoading,_that.token,_that.otpCode,_that.otpError,_that.isOtpLoading);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String email, String password, bool passwordVisible, String emailError, String passwordError, String errorMessage, bool showErrors, bool isLoading, String token, String otpCode, String otpError, bool isOtpLoading)? $default,) {final _that = this;
switch (_that) {
case _LoginViewState() when $default != null:
return $default(_that.email,_that.password,_that.passwordVisible,_that.emailError,_that.passwordError,_that.errorMessage,_that.showErrors,_that.isLoading,_that.token,_that.otpCode,_that.otpError,_that.isOtpLoading);case _:
return null;
}
}
}
/// @nodoc
class _LoginViewState implements LoginViewState {
const _LoginViewState({this.email = '', this.password = '', this.passwordVisible = false, this.emailError = '', this.passwordError = '', this.errorMessage = '', this.showErrors = false, this.isLoading = false, this.token = '', this.otpCode = '', this.otpError = '', this.isOtpLoading = false});
@override@JsonKey() final String email;
@override@JsonKey() final String password;
@override@JsonKey() final bool passwordVisible;
@override@JsonKey() final String emailError;
@override@JsonKey() final String passwordError;
@override@JsonKey() final String errorMessage;
@override@JsonKey() final bool showErrors;
@override@JsonKey() final bool isLoading;
@override@JsonKey() final String token;
@override@JsonKey() final String otpCode;
@override@JsonKey() final String otpError;
@override@JsonKey() final bool isOtpLoading;
/// Create a copy of LoginViewState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$LoginViewStateCopyWith<_LoginViewState> get copyWith => __$LoginViewStateCopyWithImpl<_LoginViewState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _LoginViewState&&(identical(other.email, email) || other.email == email)&&(identical(other.password, password) || other.password == password)&&(identical(other.passwordVisible, passwordVisible) || other.passwordVisible == passwordVisible)&&(identical(other.emailError, emailError) || other.emailError == emailError)&&(identical(other.passwordError, passwordError) || other.passwordError == passwordError)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.showErrors, showErrors) || other.showErrors == showErrors)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.token, token) || other.token == token)&&(identical(other.otpCode, otpCode) || other.otpCode == otpCode)&&(identical(other.otpError, otpError) || other.otpError == otpError)&&(identical(other.isOtpLoading, isOtpLoading) || other.isOtpLoading == isOtpLoading));
}
@override
int get hashCode => Object.hash(runtimeType,email,password,passwordVisible,emailError,passwordError,errorMessage,showErrors,isLoading,token,otpCode,otpError,isOtpLoading);
@override
String toString() {
return 'LoginViewState(email: $email, password: $password, passwordVisible: $passwordVisible, emailError: $emailError, passwordError: $passwordError, errorMessage: $errorMessage, showErrors: $showErrors, isLoading: $isLoading, token: $token, otpCode: $otpCode, otpError: $otpError, isOtpLoading: $isOtpLoading)';
}
}
/// @nodoc
abstract mixin class _$LoginViewStateCopyWith<$Res> implements $LoginViewStateCopyWith<$Res> {
factory _$LoginViewStateCopyWith(_LoginViewState value, $Res Function(_LoginViewState) _then) = __$LoginViewStateCopyWithImpl;
@override @useResult
$Res call({
String email, String password, bool passwordVisible, String emailError, String passwordError, String errorMessage, bool showErrors, bool isLoading, String token, String otpCode, String otpError, bool isOtpLoading
});
}
/// @nodoc
class __$LoginViewStateCopyWithImpl<$Res>
implements _$LoginViewStateCopyWith<$Res> {
__$LoginViewStateCopyWithImpl(this._self, this._then);
final _LoginViewState _self;
final $Res Function(_LoginViewState) _then;
/// Create a copy of LoginViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? email = null,Object? password = null,Object? passwordVisible = null,Object? emailError = null,Object? passwordError = null,Object? errorMessage = null,Object? showErrors = null,Object? isLoading = null,Object? token = null,Object? otpCode = null,Object? otpError = null,Object? isOtpLoading = null,}) {
return _then(_LoginViewState(
email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable
as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String,passwordVisible: null == passwordVisible ? _self.passwordVisible : passwordVisible // ignore: cast_nullable_to_non_nullable
as bool,emailError: null == emailError ? _self.emailError : emailError // ignore: cast_nullable_to_non_nullable
as String,passwordError: null == passwordError ? _self.passwordError : passwordError // ignore: cast_nullable_to_non_nullable
as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable
as String,showErrors: null == showErrors ? _self.showErrors : showErrors // ignore: cast_nullable_to_non_nullable
as bool,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable
as bool,token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable
as String,otpCode: null == otpCode ? _self.otpCode : otpCode // ignore: cast_nullable_to_non_nullable
as String,otpError: null == otpError ? _self.otpError : otpError // ignore: cast_nullable_to_non_nullable
as String,isOtpLoading: null == isOtpLoading ? _self.isOtpLoading : isOtpLoading // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
// dart format on

View File

@@ -0,0 +1,211 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class OtpCodeFields extends StatefulWidget {
const OtpCodeFields({
super.key,
this.length = 6,
this.autofocus = true,
this.enabled = true,
this.errorText,
this.onChanged,
this.onCompleted,
this.boxSize = 48,
this.gap = 10,
});
final int length;
final bool autofocus;
final bool enabled;
final String? errorText;
final ValueChanged<String>? onChanged;
final ValueChanged<String>? onCompleted;
final double boxSize;
final double gap;
@override
State<OtpCodeFields> createState() => _OtpCodeFieldsState();
}
class _OtpCodeFieldsState extends State<OtpCodeFields> {
late final List<TextEditingController> _controllers;
late final List<FocusNode> _focusNodes;
String get _code => _controllers.map((c) => c.text.trim()).join();
@override
void initState() {
super.initState();
_controllers = List.generate(widget.length, (_) => TextEditingController());
_focusNodes = List.generate(widget.length, (_) => FocusNode());
if (widget.autofocus) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
_focusNodes.first.requestFocus();
});
}
}
@override
void dispose() {
for (final c in _controllers) {
c.dispose();
}
for (final f in _focusNodes) {
f.dispose();
}
super.dispose();
}
void _emit() {
final code = _code;
widget.onChanged?.call(code);
if (code.length == widget.length &&
!_controllers.any((c) => c.text.isEmpty)) {
widget.onCompleted?.call(code);
}
}
void _setFromPaste(String value) {
final digits = value.replaceAll(RegExp(r'\D'), '');
if (digits.isEmpty) return;
final clipped = digits.length > widget.length
? digits.substring(0, widget.length)
: digits;
for (var i = 0; i < widget.length; i++) {
_controllers[i].text = i < clipped.length ? clipped[i] : '';
}
final nextIndex = clipped.length >= widget.length
? widget.length - 1
: clipped.length;
_focusNodes[nextIndex].requestFocus();
_emit();
setState(() {});
}
void _onChanged(int index, String value) {
if (!mounted) return;
if (value.length > 1) {
_setFromPaste(value);
return;
}
if (value.isNotEmpty && index < widget.length - 1) {
_focusNodes[index + 1].requestFocus();
}
_emit();
setState(() {});
}
KeyEventResult _onKey(int index, KeyEvent event) {
if (event is! KeyDownEvent) return KeyEventResult.ignored;
if (event.logicalKey == LogicalKeyboardKey.backspace) {
final current = _controllers[index].text;
if (current.isEmpty && index > 0) {
_controllers[index - 1].text = '';
_focusNodes[index - 1].requestFocus();
_emit();
setState(() {});
return KeyEventResult.handled;
}
}
return KeyEventResult.ignored;
}
@override
Widget build(BuildContext context) {
final borderColor = widget.errorText == null || widget.errorText!.isEmpty
? Theme.of(context).dividerColor
: Theme.of(context).colorScheme.error;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Wrap(
alignment: WrapAlignment.center,
spacing: widget.gap,
children: List.generate(widget.length, (i) {
return SizedBox(
width: widget.boxSize,
height: widget.boxSize,
child: Focus(
onKeyEvent: (_, event) => _onKey(i, event),
child: TextField(
enabled: widget.enabled,
controller: _controllers[i],
focusNode: _focusNodes[i],
keyboardType: TextInputType.number,
textInputAction: i == widget.length - 1
? TextInputAction.done
: TextInputAction.next,
textAlign: TextAlign.center,
maxLength: 1,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
),
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(1),
],
decoration: InputDecoration(
counterText: '',
contentPadding: EdgeInsets.zero,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide(color: borderColor),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide(
color: Theme.of(context).colorScheme.primary,
width: 1.6,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide(
color: Theme.of(context).colorScheme.error,
width: 1.2,
),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: BorderSide(
color: Theme.of(context).colorScheme.error,
width: 1.6,
),
),
),
onChanged: (v) => _onChanged(i, v),
),
),
);
}),
),
if (widget.errorText != null && widget.errorText!.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 10),
child: Text(
widget.errorText!,
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
fontSize: 12,
),
),
),
],
);
}
}

View File

@@ -0,0 +1,116 @@
import 'dart:async';
import 'package:auth/src/features/login/presentation/widgets/otp_code_fields.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
class TwoFactorBottomSheetView extends StatelessWidget {
const TwoFactorBottomSheetView({
super.key,
required this.theme,
required this.title,
required this.subtitle,
required this.verifyText,
required this.closeText,
required this.isOtpLoading,
required this.otpCode,
required this.otpErrorText,
required this.onChanged,
required this.onVerify,
required this.onClose,
});
final ThemePort theme;
final String title;
final String subtitle;
final String verifyText;
final String closeText;
final bool isOtpLoading;
final String otpCode;
final String otpErrorText;
final ValueChanged<String> onChanged;
final Future<void> Function() onVerify;
final VoidCallback onClose;
bool get _isValidOtp => otpCode.trim().length == 6;
@override
Widget build(BuildContext context) {
final bottomInset = MediaQuery.of(context).viewInsets.bottom;
return Container(
decoration: BoxDecoration(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
),
padding: EdgeInsets.fromLTRB(24, 12, 24, 24 + bottomInset),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
height: 4,
width: 48,
decoration: BoxDecoration(
color: Colors.grey.withValues(alpha: 0.35),
borderRadius: BorderRadius.circular(999),
),
),
const SizedBox(height: 16),
Text(
title,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w700),
),
const SizedBox(height: 8),
Text(
subtitle,
textAlign: TextAlign.center,
style: const TextStyle(fontSize: 14),
),
const SizedBox(height: 20),
OtpCodeFields(
length: 6,
enabled: !isOtpLoading,
errorText: otpErrorText.isEmpty ? null : otpErrorText,
onChanged: onChanged,
onCompleted: (_) {
if (isOtpLoading || !_isValidOtp) return;
unawaited(onVerify());
},
),
const SizedBox(height: 20),
PrimaryButton(
onPressed: (isOtpLoading || !_isValidOtp)
? () {}
: () => unawaited(onVerify()),
text: verifyText,
color: theme.getColorFor(ThemeCode.buttonPrimary),
leading: isOtpLoading
? const SizedBox(
height: 18,
width: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: null,
),
const SizedBox(height: 12),
TextButton(
onPressed: isOtpLoading ? null : onClose,
child: Text(closeText),
),
],
),
);
}
}

View File

@@ -0,0 +1,31 @@
import 'package:sf_localizations/sf_localizations.dart';
class OnboardingPage {
final String image;
final String title;
final String subtitle;
const OnboardingPage({
required this.image,
required this.title,
required this.subtitle,
});
}
const List<OnboardingPage> onboardingPages = <OnboardingPage>[
OnboardingPage(
image: 'assets/images/ui/bienvenida_paso1.svg',
title: I18n.onboardingTitle1,
subtitle: I18n.onboardingSubtitle1,
),
OnboardingPage(
image: 'assets/images/ui/bienvenida_paso2.svg',
title: I18n.onboardingTitle2,
subtitle: I18n.onboardingSubtitle2,
),
OnboardingPage(
image: 'assets/images/ui/bienvenida_paso3.svg',
title: I18n.onboardingTitle3,
subtitle: I18n.onboardingSubtitle3,
),
];

View File

@@ -1,4 +1,4 @@
import 'package:auth/src/onboarding/presentation/welcome_screen.dart';
import 'package:auth/src/features/onboarding/presentation/onboarding_screen.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
@@ -12,7 +12,7 @@ class OnboardingBuilder {
return MaterialPage<void>(
key: state.pageKey,
child: WelcomeScreen(navigationContract: navigationContract),
child: OnboardingScreen(navigationContract: navigationContract),
);
}
}

View File

@@ -0,0 +1,126 @@
import 'package:auth/src/features/onboarding/domain/onboarding_page.dart';
import 'package:auth/src/features/onboarding/presentation/onboarding_view_model.dart';
import 'package:auth/src/features/onboarding/presentation/widgets/onboarding_content.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
final onboardingPageControllerProvider = Provider.autoDispose<PageController>((
ref,
) {
final controller = PageController();
ref.onDispose(controller.dispose);
return controller;
});
class OnboardingScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const OnboardingScreen({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(onBoardingViewModelProvider);
final viewModel = ref.read(onBoardingViewModelProvider.notifier);
final pageController = ref.watch(onboardingPageControllerProvider);
final isLast = state.cardIndex >= onboardingPages.length - 1;
void goToNext() {
if (isLast) {
navigationContract.goTo(AppRoutes.linkPhone);
} else {
pageController.nextPage(
duration: const Duration(milliseconds: 400),
curve: Curves.easeOut,
);
}
}
return Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Column(
children: [
Expanded(
child: PageView.builder(
controller: pageController,
itemCount: onboardingPages.length,
onPageChanged: viewModel.onPageChanged,
itemBuilder: (context, index) {
final page = onboardingPages[index];
return OnboardingContent(
image: page.image,
title: page.title,
subtitle: page.subtitle,
);
},
),
),
StepIndicator(
current: state.cardIndex + 1,
total: onboardingPages.length,
color: const Color(0xFF4A4A4A),
),
const SizedBox(height: 38),
Container(
padding: const EdgeInsets.symmetric(horizontal: 24),
width: double.infinity,
child: TextButton(
onPressed: goToNext,
style: TextButton.styleFrom(
backgroundColor: isLast
? const Color(0xFF329E95)
: const Color(0xFF333333),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 24,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
),
child: Text(
isLast
? context.translate(I18n.start)
: context.translate(I18n.next),
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
),
),
const SizedBox(height: 8),
SizedBox(
height: 48,
child: Center(
child: isLast
? const SizedBox.shrink()
: TextButton(
onPressed: () =>
navigationContract.goTo(AppRoutes.linkPhone),
child: Text(
context.translate(I18n.skip),
style: TextStyle(
color: Color(0xFF333333),
decoration: TextDecoration.underline,
fontWeight: FontWeight.w500,
fontSize: 18,
),
),
),
),
),
const SizedBox(height: 48),
],
),
),
);
}
}

View File

@@ -0,0 +1,19 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:auth/src/features/onboarding/presentation/onboarding_view_state.dart';
final onBoardingViewModelProvider =
NotifierProvider.autoDispose<OnBoardingViewModel, OnboardingViewState>(
OnBoardingViewModel.new,
);
class OnBoardingViewModel extends Notifier<OnboardingViewState> {
@override
OnboardingViewState build() {
return const OnboardingViewState();
}
void onPageChanged(int index) {
state = state.copyWith(cardIndex: index);
}
}

View File

@@ -0,0 +1,11 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'onboarding_view_state.freezed.dart';
@freezed
abstract class OnboardingViewState with _$OnboardingViewState {
const factory OnboardingViewState({
@Default(0) int cardIndex,
String? error,
}) = _OnboardingViewState;
}

View File

@@ -0,0 +1,274 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'onboarding_view_state.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$OnboardingViewState {
int get cardIndex; String? get error;
/// Create a copy of OnboardingViewState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$OnboardingViewStateCopyWith<OnboardingViewState> get copyWith => _$OnboardingViewStateCopyWithImpl<OnboardingViewState>(this as OnboardingViewState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is OnboardingViewState&&(identical(other.cardIndex, cardIndex) || other.cardIndex == cardIndex)&&(identical(other.error, error) || other.error == error));
}
@override
int get hashCode => Object.hash(runtimeType,cardIndex,error);
@override
String toString() {
return 'OnboardingViewState(cardIndex: $cardIndex, error: $error)';
}
}
/// @nodoc
abstract mixin class $OnboardingViewStateCopyWith<$Res> {
factory $OnboardingViewStateCopyWith(OnboardingViewState value, $Res Function(OnboardingViewState) _then) = _$OnboardingViewStateCopyWithImpl;
@useResult
$Res call({
int cardIndex, String? error
});
}
/// @nodoc
class _$OnboardingViewStateCopyWithImpl<$Res>
implements $OnboardingViewStateCopyWith<$Res> {
_$OnboardingViewStateCopyWithImpl(this._self, this._then);
final OnboardingViewState _self;
final $Res Function(OnboardingViewState) _then;
/// Create a copy of OnboardingViewState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? cardIndex = null,Object? error = freezed,}) {
return _then(_self.copyWith(
cardIndex: null == cardIndex ? _self.cardIndex : cardIndex // ignore: cast_nullable_to_non_nullable
as int,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
/// Adds pattern-matching-related methods to [OnboardingViewState].
extension OnboardingViewStatePatterns on OnboardingViewState {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _OnboardingViewState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _OnboardingViewState() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _OnboardingViewState value) $default,){
final _that = this;
switch (_that) {
case _OnboardingViewState():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _OnboardingViewState value)? $default,){
final _that = this;
switch (_that) {
case _OnboardingViewState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int cardIndex, String? error)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _OnboardingViewState() when $default != null:
return $default(_that.cardIndex,_that.error);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int cardIndex, String? error) $default,) {final _that = this;
switch (_that) {
case _OnboardingViewState():
return $default(_that.cardIndex,_that.error);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int cardIndex, String? error)? $default,) {final _that = this;
switch (_that) {
case _OnboardingViewState() when $default != null:
return $default(_that.cardIndex,_that.error);case _:
return null;
}
}
}
/// @nodoc
class _OnboardingViewState implements OnboardingViewState {
const _OnboardingViewState({this.cardIndex = 0, this.error});
@override@JsonKey() final int cardIndex;
@override final String? error;
/// Create a copy of OnboardingViewState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$OnboardingViewStateCopyWith<_OnboardingViewState> get copyWith => __$OnboardingViewStateCopyWithImpl<_OnboardingViewState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _OnboardingViewState&&(identical(other.cardIndex, cardIndex) || other.cardIndex == cardIndex)&&(identical(other.error, error) || other.error == error));
}
@override
int get hashCode => Object.hash(runtimeType,cardIndex,error);
@override
String toString() {
return 'OnboardingViewState(cardIndex: $cardIndex, error: $error)';
}
}
/// @nodoc
abstract mixin class _$OnboardingViewStateCopyWith<$Res> implements $OnboardingViewStateCopyWith<$Res> {
factory _$OnboardingViewStateCopyWith(_OnboardingViewState value, $Res Function(_OnboardingViewState) _then) = __$OnboardingViewStateCopyWithImpl;
@override @useResult
$Res call({
int cardIndex, String? error
});
}
/// @nodoc
class __$OnboardingViewStateCopyWithImpl<$Res>
implements _$OnboardingViewStateCopyWith<$Res> {
__$OnboardingViewStateCopyWithImpl(this._self, this._then);
final _OnboardingViewState _self;
final $Res Function(_OnboardingViewState) _then;
/// Create a copy of OnboardingViewState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? cardIndex = null,Object? error = freezed,}) {
return _then(_OnboardingViewState(
cardIndex: null == cardIndex ? _self.cardIndex : cardIndex // ignore: cast_nullable_to_non_nullable
as int,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
as String?,
));
}
}
// dart format on

View File

@@ -0,0 +1,52 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:sf_localizations/sf_localizations.dart';
class OnboardingContent extends StatelessWidget {
final String image;
final String title;
final String subtitle;
const OnboardingContent({
super.key,
required this.image,
required this.title,
required this.subtitle,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Flexible(flex: 3, child: SvgPicture.asset(image)),
const SizedBox(height: 48),
Text(
context.translate(title),
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 28,
height: 1.4,
letterSpacing: 0.3,
color: Color(0xFF4A4A4A),
),
),
const SizedBox(height: 16),
Text(
context.translate(subtitle),
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 18,
height: 1.4,
letterSpacing: 0.3,
color: Color(0xFF9B9B9B),
),
),
],
),
);
}
}

View File

@@ -0,0 +1,224 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:get_it/get_it.dart';
import 'package:navigation/navigation.dart';
class NewPasswordScreen extends ConsumerStatefulWidget {
const NewPasswordScreen({super.key});
@override
ConsumerState<NewPasswordScreen> createState() => NewPasswordScreenState();
}
class NewPasswordScreenState extends ConsumerState<NewPasswordScreen> {
bool passwordVisible = false;
bool equalPasswords = false;
String password = '';
Map<String, bool> securityChecks = {
'min': false,
'capital': false,
'number': false,
'special': false,
};
@override
void initState() {
super.initState();
passwordVisible = false;
equalPasswords = false;
password = '';
securityChecks = {
'min': false,
'capital': false,
'number': false,
'special': false,
};
}
@override
Widget build(BuildContext context) {
final NavigationContract navigationContract = GetIt.I<NavigationContract>();
final theme = ref.watch(themePortProvider);
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: Container(
margin: const EdgeInsets.all(24),
child: Center(
child: Column(
spacing: 48,
children: [
const Spacer(),
Column(
spacing: 32,
children: [
const Text(
"Recuperar contraseña",
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 30,
letterSpacing: 0,
),
),
Column(
spacing: 16,
children: [
CustomTextField(
showPassword: passwordVisible,
label: "Nueva contraseña",
hint: "********",
onChanged: (value) => {
setState(() {
password = value;
securityChecks = checkSecurity(value);
}),
},
),
CustomTextField(
showPassword: passwordVisible,
label: "Repetir contraseña",
hint: "********",
onChanged: (value) => {
setState(() {
equalPasswords = password == value;
}),
},
),
Column(
spacing: 4,
children: [
Row(
spacing: 8,
children: [
Icon(
Icons.check,
color: theme.getColorFor(
securityChecks["min"]!
? ThemeCode.buttonPrimary
: ThemeCode.buttonSecondary,
),
),
const Text(
"Al menos 8 caracteres",
style: TextStyle(fontSize: 14),
),
],
),
Row(
spacing: 8,
children: [
Icon(
Icons.check,
color: theme.getColorFor(
securityChecks["capital"]!
? ThemeCode.buttonPrimary
: ThemeCode.buttonSecondary,
),
),
const Text(
"Una mayúscula",
style: TextStyle(fontSize: 14),
),
],
),
Row(
spacing: 8,
children: [
Icon(
Icons.check,
color: theme.getColorFor(
securityChecks["number"]!
? ThemeCode.buttonPrimary
: ThemeCode.buttonSecondary,
),
),
const Text(
"Un número",
style: TextStyle(fontSize: 14),
),
],
),
Row(
spacing: 8,
children: [
Icon(
Icons.check,
color: theme.getColorFor(
securityChecks["special"]!
? ThemeCode.buttonPrimary
: ThemeCode.buttonSecondary,
),
),
const Text(
"Un carácter especial",
style: TextStyle(fontSize: 14),
),
],
),
],
),
],
),
Column(
spacing: 8,
children: [
Align(
alignment: Alignment.bottomLeft,
child: const Text(
"Teléfono móvil",
style: TextStyle(fontSize: 14, letterSpacing: 0),
),
),
Row(
spacing: 8,
children: [
CustomDropdown(
value: 0,
items: [
Icon(Icons.outlined_flag),
Icon(Icons.outlined_flag),
Icon(Icons.outlined_flag),
],
onChanged: (value) => {},
width: 80,
),
Expanded(
child: CustomTextField(
hint: "Teléfono",
keyboardType: TextInputType.number,
),
),
],
),
],
),
],
),
PrimaryButton(
onPressed: () => {
navigationContract.goTo(AppRoutes.dashboardHome),
},
text: "Aceptar",
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
const Spacer(),
],
),
),
),
);
}
//TODO: Extraer de la vista
Map<String, bool> checkSecurity(String value) {
return {
'min': value.length >= 8,
'capital': RegExp(r'[A-Z]').hasMatch(value),
'number': RegExp(r'[0-9]').hasMatch(value),
'special': RegExp(r'[^A-Za-z0-9]').hasMatch(value),
};
}
}

View File

@@ -0,0 +1,114 @@
import 'package:auth/src/features/recover_password/presentation/sent_screen.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:navigation/navigation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class RestorePasswordScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const RestorePasswordScreen({super.key, required this.navigationContract});
@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: 48,
children: [
Spacer(flex: 8),
Column(
spacing: 32,
children: [
Text(
"Recuperar contaseña",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
),
Text(
"Introduce tu email para enviarte un enlace de recuperación",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18, letterSpacing: 0),
),
],
),
Column(
spacing: 40,
children: [
CustomTextField(
label: "Correo electrónico",
hint: "Correo electrónico",
),
Column(
spacing: 8,
children: [
Align(
alignment: Alignment.bottomLeft,
child: Text(
"Teléfono móvil",
style: TextStyle(fontSize: 14, letterSpacing: 0),
),
),
Row(
spacing: 10,
children: [
CustomDropdown(
value: 0,
items: [
Icon(Icons.outlined_flag),
Icon(Icons.outlined_flag),
Icon(Icons.outlined_flag),
],
onChanged: (value) => {},
width: 80,
),
Expanded(
child: CustomTextField(
hint: "Teléfono",
keyboardType: TextInputType.number,
),
),
],
),
],
),
Row(
spacing: 20,
children: [
Expanded(
child: SecondaryButton(
onPressed: () => {Navigator.pop(context)},
text: "Volver",
),
),
Expanded(
child: PrimaryButton(
onPressed: () => {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => SentScreen(format: "email"),
),
),
},
text: "Enviar",
size: 16,
color: theme.getColorFor(ThemeCode.buttonSecondary),
),
),
],
),
],
),
Spacer(flex: 10),
],
),
),
),
);
}
}

View File

@@ -0,0 +1,99 @@
import 'package:auth/src/features/recover_password/presentation/new_password_screen.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class SentScreen extends ConsumerWidget {
final String format;
const SentScreen({super.key, required this.format});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: Container(
margin: EdgeInsets.all(24),
child: Center(
child: Column(
spacing: 48,
children: [
Spacer(flex: 8),
Text(
"Recuperar contraseña",
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 30,
letterSpacing: 0,
),
),
Row(
spacing: 10,
children: [
Spacer(),
Icon(
Icons.check,
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
Text(
format == "email"
? "Correo enviado correctamente"
: "SMS enviado correctamente",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
Spacer(),
],
),
Column(
spacing: 16,
children: [
Text(
format == "email"
? "Revisa tu email y haz clic en el enlace para crear una nueva contraseña."
: "Revisa tu móvil y sigue las instrucciones para crear una nueva contraseña.",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18, letterSpacing: 0),
),
Text(
format == "email"
? "Si no recibes el correo en unos minutos, revisa tu carpeta de spam o pulsa \"Reenviar correo\"."
: "Si no recibes el SMS en unos minutos, asegúrate de tener cobertura o pulsa \"Reenviar SMS \".",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, letterSpacing: 0),
),
],
),
Row(
spacing: 10,
children: [
Expanded(
child: SecondaryButton(
onPressed: () => {},
text: format == "email"
? "Reenviar correo"
: "Reenviar SMS",
),
),
Expanded(
child: PrimaryButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => NewPasswordScreen()),
),
text: "Continuar",
color: theme.getColorFor(ThemeCode.buttonSecondary),
),
),
],
),
Spacer(flex: 10),
],
),
),
),
);
}
}

View File

@@ -1,4 +1,4 @@
import 'package:auth/src/recover_password/presentation/restore_password_screen.dart';
import 'package:auth/src/features/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';

View File

@@ -0,0 +1,15 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'address_entity.freezed.dart';
@freezed
abstract class AddressEntity with _$AddressEntity {
const factory AddressEntity({
required String street,
required String city,
required String province,
required String state,
required String country,
required int postCode,
}) = _AddressEntity;
}

View File

@@ -0,0 +1,286 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'address_entity.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$AddressEntity {
String get street; String get city; String get province; String get state; String get country; int get postCode;
/// Create a copy of AddressEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$AddressEntityCopyWith<AddressEntity> get copyWith => _$AddressEntityCopyWithImpl<AddressEntity>(this as AddressEntity, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is AddressEntity&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode));
}
@override
int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode);
@override
String toString() {
return 'AddressEntity(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)';
}
}
/// @nodoc
abstract mixin class $AddressEntityCopyWith<$Res> {
factory $AddressEntityCopyWith(AddressEntity value, $Res Function(AddressEntity) _then) = _$AddressEntityCopyWithImpl;
@useResult
$Res call({
String street, String city, String province, String state, String country, int postCode
});
}
/// @nodoc
class _$AddressEntityCopyWithImpl<$Res>
implements $AddressEntityCopyWith<$Res> {
_$AddressEntityCopyWithImpl(this._self, this._then);
final AddressEntity _self;
final $Res Function(AddressEntity) _then;
/// Create a copy of AddressEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = null,}) {
return _then(_self.copyWith(
street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable
as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable
as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable
as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String,postCode: null == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// Adds pattern-matching-related methods to [AddressEntity].
extension AddressEntityPatterns on AddressEntity {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AddressEntity value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _AddressEntity() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AddressEntity value) $default,){
final _that = this;
switch (_that) {
case _AddressEntity():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AddressEntity value)? $default,){
final _that = this;
switch (_that) {
case _AddressEntity() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String street, String city, String province, String state, String country, int postCode)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _AddressEntity() when $default != null:
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String street, String city, String province, String state, String country, int postCode) $default,) {final _that = this;
switch (_that) {
case _AddressEntity():
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String street, String city, String province, String state, String country, int postCode)? $default,) {final _that = this;
switch (_that) {
case _AddressEntity() when $default != null:
return $default(_that.street,_that.city,_that.province,_that.state,_that.country,_that.postCode);case _:
return null;
}
}
}
/// @nodoc
class _AddressEntity implements AddressEntity {
const _AddressEntity({required this.street, required this.city, required this.province, required this.state, required this.country, required this.postCode});
@override final String street;
@override final String city;
@override final String province;
@override final String state;
@override final String country;
@override final int postCode;
/// Create a copy of AddressEntity
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$AddressEntityCopyWith<_AddressEntity> get copyWith => __$AddressEntityCopyWithImpl<_AddressEntity>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AddressEntity&&(identical(other.street, street) || other.street == street)&&(identical(other.city, city) || other.city == city)&&(identical(other.province, province) || other.province == province)&&(identical(other.state, state) || other.state == state)&&(identical(other.country, country) || other.country == country)&&(identical(other.postCode, postCode) || other.postCode == postCode));
}
@override
int get hashCode => Object.hash(runtimeType,street,city,province,state,country,postCode);
@override
String toString() {
return 'AddressEntity(street: $street, city: $city, province: $province, state: $state, country: $country, postCode: $postCode)';
}
}
/// @nodoc
abstract mixin class _$AddressEntityCopyWith<$Res> implements $AddressEntityCopyWith<$Res> {
factory _$AddressEntityCopyWith(_AddressEntity value, $Res Function(_AddressEntity) _then) = __$AddressEntityCopyWithImpl;
@override @useResult
$Res call({
String street, String city, String province, String state, String country, int postCode
});
}
/// @nodoc
class __$AddressEntityCopyWithImpl<$Res>
implements _$AddressEntityCopyWith<$Res> {
__$AddressEntityCopyWithImpl(this._self, this._then);
final _AddressEntity _self;
final $Res Function(_AddressEntity) _then;
/// Create a copy of AddressEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? street = null,Object? city = null,Object? province = null,Object? state = null,Object? country = null,Object? postCode = null,}) {
return _then(_AddressEntity(
street: null == street ? _self.street : street // ignore: cast_nullable_to_non_nullable
as String,city: null == city ? _self.city : city // ignore: cast_nullable_to_non_nullable
as String,province: null == province ? _self.province : province // ignore: cast_nullable_to_non_nullable
as String,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
as String,country: null == country ? _self.country : country // ignore: cast_nullable_to_non_nullable
as String,postCode: null == postCode ? _self.postCode : postCode // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
// dart format on

View File

@@ -0,0 +1,25 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'address_entity.dart';
part 'sign_up_request_entity.freezed.dart';
@freezed
abstract class SignUpRequestEntity with _$SignUpRequestEntity {
const factory SignUpRequestEntity({
required String documentType,
required String documentNumber,
required String relationship,
required String firstName,
required String lastName,
required String email,
required String phone,
required String language,
required String password,
required List<AddressEntity> taxResidences,
required List<AddressEntity> addresses,
required int bornAt,
required String userId, // UUID
required String placeOfBirth,
required String birthCountry,
}) = _SignUpRequestEntity;
}

View File

@@ -0,0 +1,327 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'sign_up_request_entity.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$SignUpRequestEntity {
String get documentType; String get documentNumber; String get relationship; String get firstName; String get lastName; String get email; String get phone; String get language; String get password; List<AddressEntity> get taxResidences; List<AddressEntity> get addresses; int get bornAt; String get userId;// UUID
String get placeOfBirth; String get birthCountry;
/// Create a copy of SignUpRequestEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SignUpRequestEntityCopyWith<SignUpRequestEntity> get copyWith => _$SignUpRequestEntityCopyWithImpl<SignUpRequestEntity>(this as SignUpRequestEntity, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SignUpRequestEntity&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.password, password) || other.password == password)&&const DeepCollectionEquality().equals(other.taxResidences, taxResidences)&&const DeepCollectionEquality().equals(other.addresses, addresses)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry));
}
@override
int get hashCode => Object.hash(runtimeType,documentType,documentNumber,relationship,firstName,lastName,email,phone,language,password,const DeepCollectionEquality().hash(taxResidences),const DeepCollectionEquality().hash(addresses),bornAt,userId,placeOfBirth,birthCountry);
@override
String toString() {
return 'SignUpRequestEntity(documentType: $documentType, documentNumber: $documentNumber, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, password: $password, taxResidences: $taxResidences, addresses: $addresses, bornAt: $bornAt, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry)';
}
}
/// @nodoc
abstract mixin class $SignUpRequestEntityCopyWith<$Res> {
factory $SignUpRequestEntityCopyWith(SignUpRequestEntity value, $Res Function(SignUpRequestEntity) _then) = _$SignUpRequestEntityCopyWithImpl;
@useResult
$Res call({
String documentType, String documentNumber, String relationship, String firstName, String lastName, String email, String phone, String language, String password, List<AddressEntity> taxResidences, List<AddressEntity> addresses, int bornAt, String userId, String placeOfBirth, String birthCountry
});
}
/// @nodoc
class _$SignUpRequestEntityCopyWithImpl<$Res>
implements $SignUpRequestEntityCopyWith<$Res> {
_$SignUpRequestEntityCopyWithImpl(this._self, this._then);
final SignUpRequestEntity _self;
final $Res Function(SignUpRequestEntity) _then;
/// Create a copy of SignUpRequestEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? documentType = null,Object? documentNumber = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? password = null,Object? taxResidences = null,Object? addresses = null,Object? bornAt = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,}) {
return _then(_self.copyWith(
documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable
as String,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable
as String,relationship: null == relationship ? _self.relationship : relationship // ignore: cast_nullable_to_non_nullable
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable
as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable
as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable
as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String,taxResidences: null == taxResidences ? _self.taxResidences : taxResidences // ignore: cast_nullable_to_non_nullable
as List<AddressEntity>,addresses: null == addresses ? _self.addresses : addresses // ignore: cast_nullable_to_non_nullable
as List<AddressEntity>,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as int,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable
as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [SignUpRequestEntity].
extension SignUpRequestEntityPatterns on SignUpRequestEntity {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SignUpRequestEntity value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _SignUpRequestEntity() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SignUpRequestEntity value) $default,){
final _that = this;
switch (_that) {
case _SignUpRequestEntity():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SignUpRequestEntity value)? $default,){
final _that = this;
switch (_that) {
case _SignUpRequestEntity() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String documentType, String documentNumber, String relationship, String firstName, String lastName, String email, String phone, String language, String password, List<AddressEntity> taxResidences, List<AddressEntity> addresses, int bornAt, String userId, String placeOfBirth, String birthCountry)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _SignUpRequestEntity() when $default != null:
return $default(_that.documentType,_that.documentNumber,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String documentType, String documentNumber, String relationship, String firstName, String lastName, String email, String phone, String language, String password, List<AddressEntity> taxResidences, List<AddressEntity> addresses, int bornAt, String userId, String placeOfBirth, String birthCountry) $default,) {final _that = this;
switch (_that) {
case _SignUpRequestEntity():
return $default(_that.documentType,_that.documentNumber,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String documentType, String documentNumber, String relationship, String firstName, String lastName, String email, String phone, String language, String password, List<AddressEntity> taxResidences, List<AddressEntity> addresses, int bornAt, String userId, String placeOfBirth, String birthCountry)? $default,) {final _that = this;
switch (_that) {
case _SignUpRequestEntity() when $default != null:
return $default(_that.documentType,_that.documentNumber,_that.relationship,_that.firstName,_that.lastName,_that.email,_that.phone,_that.language,_that.password,_that.taxResidences,_that.addresses,_that.bornAt,_that.userId,_that.placeOfBirth,_that.birthCountry);case _:
return null;
}
}
}
/// @nodoc
class _SignUpRequestEntity implements SignUpRequestEntity {
const _SignUpRequestEntity({required this.documentType, required this.documentNumber, required this.relationship, required this.firstName, required this.lastName, required this.email, required this.phone, required this.language, required this.password, required final List<AddressEntity> taxResidences, required final List<AddressEntity> addresses, required this.bornAt, required this.userId, required this.placeOfBirth, required this.birthCountry}): _taxResidences = taxResidences,_addresses = addresses;
@override final String documentType;
@override final String documentNumber;
@override final String relationship;
@override final String firstName;
@override final String lastName;
@override final String email;
@override final String phone;
@override final String language;
@override final String password;
final List<AddressEntity> _taxResidences;
@override List<AddressEntity> get taxResidences {
if (_taxResidences is EqualUnmodifiableListView) return _taxResidences;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_taxResidences);
}
final List<AddressEntity> _addresses;
@override List<AddressEntity> get addresses {
if (_addresses is EqualUnmodifiableListView) return _addresses;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_addresses);
}
@override final int bornAt;
@override final String userId;
// UUID
@override final String placeOfBirth;
@override final String birthCountry;
/// Create a copy of SignUpRequestEntity
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SignUpRequestEntityCopyWith<_SignUpRequestEntity> get copyWith => __$SignUpRequestEntityCopyWithImpl<_SignUpRequestEntity>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SignUpRequestEntity&&(identical(other.documentType, documentType) || other.documentType == documentType)&&(identical(other.documentNumber, documentNumber) || other.documentNumber == documentNumber)&&(identical(other.relationship, relationship) || other.relationship == relationship)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.email, email) || other.email == email)&&(identical(other.phone, phone) || other.phone == phone)&&(identical(other.language, language) || other.language == language)&&(identical(other.password, password) || other.password == password)&&const DeepCollectionEquality().equals(other._taxResidences, _taxResidences)&&const DeepCollectionEquality().equals(other._addresses, _addresses)&&(identical(other.bornAt, bornAt) || other.bornAt == bornAt)&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.placeOfBirth, placeOfBirth) || other.placeOfBirth == placeOfBirth)&&(identical(other.birthCountry, birthCountry) || other.birthCountry == birthCountry));
}
@override
int get hashCode => Object.hash(runtimeType,documentType,documentNumber,relationship,firstName,lastName,email,phone,language,password,const DeepCollectionEquality().hash(_taxResidences),const DeepCollectionEquality().hash(_addresses),bornAt,userId,placeOfBirth,birthCountry);
@override
String toString() {
return 'SignUpRequestEntity(documentType: $documentType, documentNumber: $documentNumber, relationship: $relationship, firstName: $firstName, lastName: $lastName, email: $email, phone: $phone, language: $language, password: $password, taxResidences: $taxResidences, addresses: $addresses, bornAt: $bornAt, userId: $userId, placeOfBirth: $placeOfBirth, birthCountry: $birthCountry)';
}
}
/// @nodoc
abstract mixin class _$SignUpRequestEntityCopyWith<$Res> implements $SignUpRequestEntityCopyWith<$Res> {
factory _$SignUpRequestEntityCopyWith(_SignUpRequestEntity value, $Res Function(_SignUpRequestEntity) _then) = __$SignUpRequestEntityCopyWithImpl;
@override @useResult
$Res call({
String documentType, String documentNumber, String relationship, String firstName, String lastName, String email, String phone, String language, String password, List<AddressEntity> taxResidences, List<AddressEntity> addresses, int bornAt, String userId, String placeOfBirth, String birthCountry
});
}
/// @nodoc
class __$SignUpRequestEntityCopyWithImpl<$Res>
implements _$SignUpRequestEntityCopyWith<$Res> {
__$SignUpRequestEntityCopyWithImpl(this._self, this._then);
final _SignUpRequestEntity _self;
final $Res Function(_SignUpRequestEntity) _then;
/// Create a copy of SignUpRequestEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? documentType = null,Object? documentNumber = null,Object? relationship = null,Object? firstName = null,Object? lastName = null,Object? email = null,Object? phone = null,Object? language = null,Object? password = null,Object? taxResidences = null,Object? addresses = null,Object? bornAt = null,Object? userId = null,Object? placeOfBirth = null,Object? birthCountry = null,}) {
return _then(_SignUpRequestEntity(
documentType: null == documentType ? _self.documentType : documentType // ignore: cast_nullable_to_non_nullable
as String,documentNumber: null == documentNumber ? _self.documentNumber : documentNumber // ignore: cast_nullable_to_non_nullable
as String,relationship: null == relationship ? _self.relationship : relationship // ignore: cast_nullable_to_non_nullable
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
as String,lastName: null == lastName ? _self.lastName : lastName // ignore: cast_nullable_to_non_nullable
as String,email: null == email ? _self.email : email // ignore: cast_nullable_to_non_nullable
as String,phone: null == phone ? _self.phone : phone // ignore: cast_nullable_to_non_nullable
as String,language: null == language ? _self.language : language // ignore: cast_nullable_to_non_nullable
as String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable
as String,taxResidences: null == taxResidences ? _self._taxResidences : taxResidences // ignore: cast_nullable_to_non_nullable
as List<AddressEntity>,addresses: null == addresses ? _self._addresses : addresses // ignore: cast_nullable_to_non_nullable
as List<AddressEntity>,bornAt: null == bornAt ? _self.bornAt : bornAt // ignore: cast_nullable_to_non_nullable
as int,userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
as String,placeOfBirth: null == placeOfBirth ? _self.placeOfBirth : placeOfBirth // ignore: cast_nullable_to_non_nullable
as String,birthCountry: null == birthCountry ? _self.birthCountry : birthCountry // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

View File

@@ -0,0 +1,19 @@
import 'package:freezed_annotation/freezed_annotation.dart';
part 'two_fa_secret_entity.freezed.dart';
@freezed
abstract class TwoFASecretEntity with _$TwoFASecretEntity {
const factory TwoFASecretEntity({
required bool isCreated,
required TwoFASecretItemEntity item,
}) = _TwoFASecretEntity;
}
@freezed
abstract class TwoFASecretItemEntity with _$TwoFASecretItemEntity {
const factory TwoFASecretItemEntity({
required String secret,
required String qr,
}) = _TwoFASecretItemEntity;
}

View File

@@ -0,0 +1,552 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'two_fa_secret_entity.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$TwoFASecretEntity {
bool get isCreated; TwoFASecretItemEntity get item;
/// Create a copy of TwoFASecretEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$TwoFASecretEntityCopyWith<TwoFASecretEntity> get copyWith => _$TwoFASecretEntityCopyWithImpl<TwoFASecretEntity>(this as TwoFASecretEntity, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TwoFASecretEntity&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'TwoFASecretEntity(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class $TwoFASecretEntityCopyWith<$Res> {
factory $TwoFASecretEntityCopyWith(TwoFASecretEntity value, $Res Function(TwoFASecretEntity) _then) = _$TwoFASecretEntityCopyWithImpl;
@useResult
$Res call({
bool isCreated, TwoFASecretItemEntity item
});
$TwoFASecretItemEntityCopyWith<$Res> get item;
}
/// @nodoc
class _$TwoFASecretEntityCopyWithImpl<$Res>
implements $TwoFASecretEntityCopyWith<$Res> {
_$TwoFASecretEntityCopyWithImpl(this._self, this._then);
final TwoFASecretEntity _self;
final $Res Function(TwoFASecretEntity) _then;
/// Create a copy of TwoFASecretEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_self.copyWith(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as TwoFASecretItemEntity,
));
}
/// Create a copy of TwoFASecretEntity
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$TwoFASecretItemEntityCopyWith<$Res> get item {
return $TwoFASecretItemEntityCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// Adds pattern-matching-related methods to [TwoFASecretEntity].
extension TwoFASecretEntityPatterns on TwoFASecretEntity {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _TwoFASecretEntity value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _TwoFASecretEntity() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _TwoFASecretEntity value) $default,){
final _that = this;
switch (_that) {
case _TwoFASecretEntity():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _TwoFASecretEntity value)? $default,){
final _that = this;
switch (_that) {
case _TwoFASecretEntity() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isCreated, TwoFASecretItemEntity item)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _TwoFASecretEntity() when $default != null:
return $default(_that.isCreated,_that.item);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isCreated, TwoFASecretItemEntity item) $default,) {final _that = this;
switch (_that) {
case _TwoFASecretEntity():
return $default(_that.isCreated,_that.item);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isCreated, TwoFASecretItemEntity item)? $default,) {final _that = this;
switch (_that) {
case _TwoFASecretEntity() when $default != null:
return $default(_that.isCreated,_that.item);case _:
return null;
}
}
}
/// @nodoc
class _TwoFASecretEntity implements TwoFASecretEntity {
const _TwoFASecretEntity({required this.isCreated, required this.item});
@override final bool isCreated;
@override final TwoFASecretItemEntity item;
/// Create a copy of TwoFASecretEntity
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$TwoFASecretEntityCopyWith<_TwoFASecretEntity> get copyWith => __$TwoFASecretEntityCopyWithImpl<_TwoFASecretEntity>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _TwoFASecretEntity&&(identical(other.isCreated, isCreated) || other.isCreated == isCreated)&&(identical(other.item, item) || other.item == item));
}
@override
int get hashCode => Object.hash(runtimeType,isCreated,item);
@override
String toString() {
return 'TwoFASecretEntity(isCreated: $isCreated, item: $item)';
}
}
/// @nodoc
abstract mixin class _$TwoFASecretEntityCopyWith<$Res> implements $TwoFASecretEntityCopyWith<$Res> {
factory _$TwoFASecretEntityCopyWith(_TwoFASecretEntity value, $Res Function(_TwoFASecretEntity) _then) = __$TwoFASecretEntityCopyWithImpl;
@override @useResult
$Res call({
bool isCreated, TwoFASecretItemEntity item
});
@override $TwoFASecretItemEntityCopyWith<$Res> get item;
}
/// @nodoc
class __$TwoFASecretEntityCopyWithImpl<$Res>
implements _$TwoFASecretEntityCopyWith<$Res> {
__$TwoFASecretEntityCopyWithImpl(this._self, this._then);
final _TwoFASecretEntity _self;
final $Res Function(_TwoFASecretEntity) _then;
/// Create a copy of TwoFASecretEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? isCreated = null,Object? item = null,}) {
return _then(_TwoFASecretEntity(
isCreated: null == isCreated ? _self.isCreated : isCreated // ignore: cast_nullable_to_non_nullable
as bool,item: null == item ? _self.item : item // ignore: cast_nullable_to_non_nullable
as TwoFASecretItemEntity,
));
}
/// Create a copy of TwoFASecretEntity
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$TwoFASecretItemEntityCopyWith<$Res> get item {
return $TwoFASecretItemEntityCopyWith<$Res>(_self.item, (value) {
return _then(_self.copyWith(item: value));
});
}
}
/// @nodoc
mixin _$TwoFASecretItemEntity {
String get secret; String get qr;
/// Create a copy of TwoFASecretItemEntity
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$TwoFASecretItemEntityCopyWith<TwoFASecretItemEntity> get copyWith => _$TwoFASecretItemEntityCopyWithImpl<TwoFASecretItemEntity>(this as TwoFASecretItemEntity, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is TwoFASecretItemEntity&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.qr, qr) || other.qr == qr));
}
@override
int get hashCode => Object.hash(runtimeType,secret,qr);
@override
String toString() {
return 'TwoFASecretItemEntity(secret: $secret, qr: $qr)';
}
}
/// @nodoc
abstract mixin class $TwoFASecretItemEntityCopyWith<$Res> {
factory $TwoFASecretItemEntityCopyWith(TwoFASecretItemEntity value, $Res Function(TwoFASecretItemEntity) _then) = _$TwoFASecretItemEntityCopyWithImpl;
@useResult
$Res call({
String secret, String qr
});
}
/// @nodoc
class _$TwoFASecretItemEntityCopyWithImpl<$Res>
implements $TwoFASecretItemEntityCopyWith<$Res> {
_$TwoFASecretItemEntityCopyWithImpl(this._self, this._then);
final TwoFASecretItemEntity _self;
final $Res Function(TwoFASecretItemEntity) _then;
/// Create a copy of TwoFASecretItemEntity
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? secret = null,Object? qr = null,}) {
return _then(_self.copyWith(
secret: null == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable
as String,qr: null == qr ? _self.qr : qr // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// Adds pattern-matching-related methods to [TwoFASecretItemEntity].
extension TwoFASecretItemEntityPatterns on TwoFASecretItemEntity {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _TwoFASecretItemEntity value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _TwoFASecretItemEntity() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _TwoFASecretItemEntity value) $default,){
final _that = this;
switch (_that) {
case _TwoFASecretItemEntity():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _TwoFASecretItemEntity value)? $default,){
final _that = this;
switch (_that) {
case _TwoFASecretItemEntity() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String secret, String qr)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _TwoFASecretItemEntity() when $default != null:
return $default(_that.secret,_that.qr);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String secret, String qr) $default,) {final _that = this;
switch (_that) {
case _TwoFASecretItemEntity():
return $default(_that.secret,_that.qr);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String secret, String qr)? $default,) {final _that = this;
switch (_that) {
case _TwoFASecretItemEntity() when $default != null:
return $default(_that.secret,_that.qr);case _:
return null;
}
}
}
/// @nodoc
class _TwoFASecretItemEntity implements TwoFASecretItemEntity {
const _TwoFASecretItemEntity({required this.secret, required this.qr});
@override final String secret;
@override final String qr;
/// Create a copy of TwoFASecretItemEntity
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$TwoFASecretItemEntityCopyWith<_TwoFASecretItemEntity> get copyWith => __$TwoFASecretItemEntityCopyWithImpl<_TwoFASecretItemEntity>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _TwoFASecretItemEntity&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.qr, qr) || other.qr == qr));
}
@override
int get hashCode => Object.hash(runtimeType,secret,qr);
@override
String toString() {
return 'TwoFASecretItemEntity(secret: $secret, qr: $qr)';
}
}
/// @nodoc
abstract mixin class _$TwoFASecretItemEntityCopyWith<$Res> implements $TwoFASecretItemEntityCopyWith<$Res> {
factory _$TwoFASecretItemEntityCopyWith(_TwoFASecretItemEntity value, $Res Function(_TwoFASecretItemEntity) _then) = __$TwoFASecretItemEntityCopyWithImpl;
@override @useResult
$Res call({
String secret, String qr
});
}
/// @nodoc
class __$TwoFASecretItemEntityCopyWithImpl<$Res>
implements _$TwoFASecretItemEntityCopyWith<$Res> {
__$TwoFASecretItemEntityCopyWithImpl(this._self, this._then);
final _TwoFASecretItemEntity _self;
final $Res Function(_TwoFASecretItemEntity) _then;
/// Create a copy of TwoFASecretItemEntity
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? secret = null,Object? qr = null,}) {
return _then(_TwoFASecretItemEntity(
secret: null == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable
as String,qr: null == qr ? _self.qr : qr // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
// dart format on

View File

@@ -0,0 +1,5 @@
import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart';
abstract class GenerateTwoFASignUpUseCase {
Future<TwoFASecretEntity> generateTwoFASignUp({required String token});
}

View File

@@ -0,0 +1,13 @@
import 'package:auth/src/core/domain/repositories/auth_repository.dart';
import 'package:auth/src/features/sign_up/domain/entities/two_fa_secret_entity.dart';
import 'package:auth/src/features/sign_up/domain/generate_two_fa_sign_up_use_case.dart';
class GenerateTwoFASignUpUseCaseImpl implements GenerateTwoFASignUpUseCase {
GenerateTwoFASignUpUseCaseImpl(this._repository);
final AuthRepository _repository;
@override
Future<TwoFASecretEntity> generateTwoFASignUp({required String token}) {
return _repository.generateTwoFASignUp(token: token);
}
}

View File

@@ -0,0 +1,5 @@
import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart';
abstract class SignUpUseCase {
Future<String> signUp({required SignUpRequestEntity request});
}

View File

@@ -0,0 +1,14 @@
import 'package:auth/src/core/domain/repositories/auth_repository.dart';
import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart';
import 'package:auth/src/features/sign_up/domain/sign_up_use_case.dart';
class SignUpUseCaseImpl implements SignUpUseCase {
SignUpUseCaseImpl(this._repository);
final AuthRepository _repository;
@override
Future<String> signUp({required SignUpRequestEntity request}) {
return _repository.signUp(request: request);
}
}

View File

@@ -0,0 +1,6 @@
abstract class VerifyTwoFACodeSignUpUseCase {
Future<void> verifyTwoFACodeSignUp({
required String token,
required String code,
});
}

View File

@@ -0,0 +1,15 @@
import 'package:auth/src/core/domain/repositories/auth_repository.dart';
import 'package:auth/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case.dart';
class VerifyTwoFaCodeSignUpUseCaseImpl implements VerifyTwoFACodeSignUpUseCase {
VerifyTwoFaCodeSignUpUseCaseImpl(this._repository);
final AuthRepository _repository;
@override
Future<void> verifyTwoFACodeSignUp({
required String token,
required String code,
}) {
return _repository.verifyTwoFACodeSignUp(token: token, code: code);
}
}

View File

@@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
typedef SignUpBodyBuilder =
Widget Function(BuildContext context, WidgetRef ref);
class SignUpStepConfig {
final String supertitle;
final String title;
final String? subtitle;
final SignUpBodyBuilder bodyBuilder;
const SignUpStepConfig({
required this.supertitle,
required this.title,
this.subtitle,
required this.bodyBuilder,
});
}

View File

@@ -0,0 +1,23 @@
import 'package:auth/src/features/sign_up/domain/entities/address_entity.dart';
import 'package:auth/src/features/sign_up/presentation/state/address_view_state.dart';
extension AddressViewStateMapper on AddressViewState {
bool get isValid =>
street.trim().isNotEmpty &&
city.trim().isNotEmpty &&
province.trim().isNotEmpty &&
state.trim().isNotEmpty &&
country.trim().isNotEmpty &&
(postCode != null);
AddressEntity toEntity() {
return AddressEntity(
street: street.trim(),
city: city.trim(),
province: province.trim(),
state: state.trim(),
country: country.trim(),
postCode: postCode ?? 0,
);
}
}

View File

@@ -0,0 +1,46 @@
import 'package:auth/src/features/sign_up/domain/entities/address_entity.dart';
import 'package:auth/src/features/sign_up/domain/entities/sign_up_request_entity.dart';
import 'package:auth/src/features/sign_up/presentation/mappers/address_view_state_mapper.dart';
import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_state.dart';
extension SignUpViewStateMapper on SignUpViewState {
bool get canSubmit =>
documentNumber.trim().isNotEmpty &&
documentType.trim().isNotEmpty &&
relationship.trim().isNotEmpty &&
firstName.trim().isNotEmpty &&
lastName.trim().isNotEmpty &&
email.trim().isNotEmpty &&
phone.trim().isNotEmpty &&
password.isNotEmpty &&
bornAt != null &&
userId.trim().isNotEmpty &&
placeOfBirth.trim().isNotEmpty &&
birthCountry.trim().isNotEmpty &&
address.isValid;
SignUpRequestEntity toRequestEntity() {
final birth = bornAt;
if (birth == null) {
throw Exception('bornAt is required');
}
return SignUpRequestEntity(
documentNumber: documentNumber.trim(),
documentType: documentType.trim(),
relationship: relationship.trim(),
firstName: firstName.trim(),
lastName: lastName.trim(),
email: email.trim(),
phone: phone.trim(),
language: language.trim(),
password: password,
bornAt: birth.millisecondsSinceEpoch,
userId: userId.trim(),
placeOfBirth: placeOfBirth.trim(),
birthCountry: birthCountry.trim(),
addresses: <AddressEntity>[address.toEntity()],
taxResidences: <AddressEntity>[address.toEntity()],
);
}
}

View File

@@ -0,0 +1,10 @@
import 'package:auth/src/core/providers/auth_repository_provider.dart';
import 'package:auth/src/features/sign_up/domain/generate_two_fa_sign_up_use_case.dart';
import 'package:auth/src/features/sign_up/domain/generate_two_fa_sign_up_use_case_impl.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final generateTwoFASignUpUseCaseProvider =
Provider.autoDispose<GenerateTwoFASignUpUseCase>((ref) {
final authRepository = ref.read(authRepositoryProvider);
return GenerateTwoFASignUpUseCaseImpl(authRepository);
});

View File

@@ -0,0 +1,9 @@
import 'package:auth/src/core/providers/auth_repository_provider.dart';
import 'package:auth/src/features/sign_up/domain/sign_up_use_case.dart';
import 'package:auth/src/features/sign_up/domain/sign_up_use_case_impl.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final signUpUseCaseProvider = Provider.autoDispose<SignUpUseCase>((ref) {
final authRepository = ref.read(authRepositoryProvider);
return SignUpUseCaseImpl(authRepository);
});

View File

@@ -0,0 +1,10 @@
import 'package:auth/src/core/providers/auth_repository_provider.dart';
import 'package:auth/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case.dart';
import 'package:auth/src/features/sign_up/domain/verify_two_fa_code_sign_up_use_case_impl.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final verifyTwoFACodeSignUpUseCaseProvider =
Provider.autoDispose<VerifyTwoFACodeSignUpUseCase>((ref) {
final authRepository = ref.read(authRepositoryProvider);
return VerifyTwoFaCodeSignUpUseCaseImpl(authRepository);
});

View File

@@ -0,0 +1,92 @@
import 'package:auth/src/features/sign_up/presentation/state/sign_up_view_model.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/navigation.dart';
import 'package:sf_localizations/sf_localizations.dart';
class AccountCreatedScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const AccountCreatedScreen({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
final state = ref.watch(signUpViewModelProvider);
final String email = state.email;
final String fullName = '${state.firstName} ${state.lastName}'.trim();
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: Container(
margin: const EdgeInsets.all(30),
child: Center(
child: Column(
children: [
const Spacer(flex: 10),
Icon(
Icons.check,
color: theme.getColorFor(ThemeCode.buttonPrimary),
size: 50,
),
const SizedBox(height: 20),
Text(
context.translate(I18n.accountCreatedTitle),
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
Text.rich(
textAlign: TextAlign.center,
TextSpan(
text: '${context.translate(I18n.accountCreatedForLabel)}\n',
children: [
TextSpan(
text: fullName,
style: const TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
const SizedBox(height: 16),
Text.rich(
textAlign: TextAlign.center,
TextSpan(
text:
'${context.translate(I18n.accountCreatedEmailVerificationSentLabel)}\n',
children: [
TextSpan(
text: email,
style: const TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
const SizedBox(height: 20),
Text(
context.translate(I18n.accountCreatedChildSetupHint),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
PrimaryButton(
onPressed: () => navigationContract.goTo(AppRoutes.login),
text: context.translate(I18n.accountCreatedContinue),
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
const Spacer(flex: 8),
],
),
),
),
);
}
}

Some files were not shown because too many files have changed in this diff Show More