Compare commits

...

18 Commits

Author SHA1 Message Date
ae429302bb restore 'fix onboarding design' 2025-12-05 13:53:46 +01:00
1f3de3df26 Merge remote-tracking branch 'origin/develop' into onboarding_screen
# Conflicts:
#	modules/auth/lib/src/onboarding/presentation/onboarding_screen.dart
2025-12-05 12:49:46 +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
baef98a443 fix onboarding titles design 2025-12-04 16:58:07 +01:00
b8bb3e65c2 fix onboarding design 2025-12-04 16:56:43 +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
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
125 changed files with 3029 additions and 728 deletions

View File

@@ -0,0 +1,364 @@
{
"configVersion": 2,
"packages": [
{
"name": "ansi_styles",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/ansi_styles-0.3.2+1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "args",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/args-2.7.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "async",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/async-2.13.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "characters",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/characters-1.4.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "charcode",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/charcode-1.4.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "checked_yaml",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/checked_yaml-2.0.4",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "cli_launcher",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/cli_launcher-0.3.2+1",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "cli_util",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/cli_util-0.4.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "collection",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/collection-1.19.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "conventional_commit",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/conventional_commit-0.6.1+1",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "ffi",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/ffi-2.1.4",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "file",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/file-7.0.1",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "flutter",
"rootUri": "file:///Users/juliandalcalaf/Development/flutter/packages/flutter",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "flutter_secure_storage",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_secure_storage_linux",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_secure_storage_macos",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_secure_storage_platform_interface",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_platform_interface-1.1.2",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_secure_storage_web",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_secure_storage_windows",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "flutter_web_plugins",
"rootUri": "file:///Users/juliandalcalaf/Development/flutter/packages/flutter_web_plugins",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "glob",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/glob-2.1.3",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "graphs",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/graphs-2.3.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "http",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/http-1.5.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "http_parser",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/http_parser-4.1.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "io",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/io-1.0.5",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "js",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/js-0.6.7",
"packageUri": "lib/",
"languageVersion": "2.19"
},
{
"name": "json_annotation",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/json_annotation-4.9.0",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "material_color_utilities",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/material_color_utilities-0.11.1",
"packageUri": "lib/",
"languageVersion": "2.17"
},
{
"name": "melos",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/melos-6.3.3",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "meta",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/meta-1.16.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "mustache_template",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/mustache_template-2.0.2",
"packageUri": "lib/",
"languageVersion": "3.7"
},
{
"name": "path",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path-1.9.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "path_provider",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider-2.1.5",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "path_provider_android",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_android-2.2.20",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "path_provider_foundation",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.3",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "path_provider_linux",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1",
"packageUri": "lib/",
"languageVersion": "2.19"
},
{
"name": "path_provider_platform_interface",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_platform_interface-2.1.2",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "path_provider_windows",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "platform",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/platform-3.1.6",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "plugin_platform_interface",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/plugin_platform_interface-2.1.8",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{
"name": "pool",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/pool-1.5.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "process",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/process-5.0.5",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "prompts",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/prompts-2.0.0",
"packageUri": "lib/",
"languageVersion": "2.12"
},
{
"name": "pub_semver",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/pub_semver-2.2.0",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "pub_updater",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/pub_updater-0.5.0",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "pubspec_parse",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/pubspec_parse-1.5.0",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "sky_engine",
"rootUri": "file:///Users/juliandalcalaf/Development/flutter/bin/cache/pkg/sky_engine",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "source_span",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/source_span-1.10.1",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "stack_trace",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/stack_trace-1.12.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "string_scanner",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/string_scanner-1.4.1",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "term_glyph",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/term_glyph-1.2.2",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "typed_data",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/typed_data-1.4.0",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{
"name": "vector_math",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/vector_math-2.2.0",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "web",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/web-1.1.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "win32",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/win32-5.15.0",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "xdg_directories",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/xdg_directories-1.1.0",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{
"name": "yaml",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/yaml-3.1.3",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{
"name": "yaml_edit",
"rootUri": "file:///Users/juliandalcalaf/.pub-cache/hosted/pub.dev/yaml_edit-2.2.2",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "sf_app_platform_mono_repo",
"rootUri": "../",
"packageUri": "lib/",
"languageVersion": "3.0"
}
],
"generator": "pub",
"generatorVersion": "3.9.2",
"flutterRoot": "file:///Users/juliandalcalaf/Development/flutter",
"flutterVersion": "3.35.7",
"pubCache": "file:///Users/juliandalcalaf/.pub-cache"
}

View File

@@ -0,0 +1,491 @@
{
"roots": [
"sf_app_platform_mono_repo"
],
"packages": [
{
"name": "sf_app_platform_mono_repo",
"version": "0.0.0",
"dependencies": [
"flutter_secure_storage"
],
"devDependencies": [
"melos"
]
},
{
"name": "flutter_secure_storage",
"version": "9.2.4",
"dependencies": [
"flutter",
"flutter_secure_storage_linux",
"flutter_secure_storage_macos",
"flutter_secure_storage_platform_interface",
"flutter_secure_storage_web",
"flutter_secure_storage_windows",
"meta"
]
},
{
"name": "melos",
"version": "6.3.3",
"dependencies": [
"ansi_styles",
"args",
"async",
"cli_launcher",
"cli_util",
"collection",
"conventional_commit",
"file",
"glob",
"graphs",
"http",
"meta",
"mustache_template",
"path",
"platform",
"pool",
"prompts",
"pub_semver",
"pub_updater",
"pubspec_parse",
"string_scanner",
"yaml",
"yaml_edit"
]
},
{
"name": "meta",
"version": "1.16.0",
"dependencies": []
},
{
"name": "flutter_secure_storage_windows",
"version": "3.1.2",
"dependencies": [
"ffi",
"flutter",
"flutter_secure_storage_platform_interface",
"path",
"path_provider",
"win32"
]
},
{
"name": "flutter_secure_storage_web",
"version": "1.2.1",
"dependencies": [
"flutter",
"flutter_secure_storage_platform_interface",
"flutter_web_plugins",
"js"
]
},
{
"name": "flutter_secure_storage_platform_interface",
"version": "1.1.2",
"dependencies": [
"flutter",
"plugin_platform_interface"
]
},
{
"name": "flutter_secure_storage_macos",
"version": "3.1.3",
"dependencies": [
"flutter",
"flutter_secure_storage_platform_interface"
]
},
{
"name": "flutter_secure_storage_linux",
"version": "1.2.3",
"dependencies": [
"flutter",
"flutter_secure_storage_platform_interface"
]
},
{
"name": "flutter",
"version": "0.0.0",
"dependencies": [
"characters",
"collection",
"material_color_utilities",
"meta",
"sky_engine",
"vector_math"
]
},
{
"name": "yaml_edit",
"version": "2.2.2",
"dependencies": [
"collection",
"meta",
"source_span",
"yaml"
]
},
{
"name": "yaml",
"version": "3.1.3",
"dependencies": [
"collection",
"source_span",
"string_scanner"
]
},
{
"name": "string_scanner",
"version": "1.4.1",
"dependencies": [
"source_span"
]
},
{
"name": "pubspec_parse",
"version": "1.5.0",
"dependencies": [
"checked_yaml",
"collection",
"json_annotation",
"pub_semver",
"yaml"
]
},
{
"name": "pub_updater",
"version": "0.5.0",
"dependencies": [
"http",
"json_annotation",
"process",
"pub_semver"
]
},
{
"name": "pub_semver",
"version": "2.2.0",
"dependencies": [
"collection"
]
},
{
"name": "prompts",
"version": "2.0.0",
"dependencies": [
"charcode",
"io"
]
},
{
"name": "pool",
"version": "1.5.2",
"dependencies": [
"async",
"stack_trace"
]
},
{
"name": "platform",
"version": "3.1.6",
"dependencies": []
},
{
"name": "path",
"version": "1.9.1",
"dependencies": []
},
{
"name": "mustache_template",
"version": "2.0.2",
"dependencies": []
},
{
"name": "http",
"version": "1.5.0",
"dependencies": [
"async",
"http_parser",
"meta",
"web"
]
},
{
"name": "graphs",
"version": "2.3.2",
"dependencies": [
"collection"
]
},
{
"name": "glob",
"version": "2.1.3",
"dependencies": [
"async",
"collection",
"file",
"path",
"string_scanner"
]
},
{
"name": "file",
"version": "7.0.1",
"dependencies": [
"meta",
"path"
]
},
{
"name": "conventional_commit",
"version": "0.6.1+1",
"dependencies": []
},
{
"name": "collection",
"version": "1.19.1",
"dependencies": []
},
{
"name": "cli_util",
"version": "0.4.2",
"dependencies": [
"meta",
"path"
]
},
{
"name": "cli_launcher",
"version": "0.3.2+1",
"dependencies": [
"path",
"yaml"
]
},
{
"name": "async",
"version": "2.13.0",
"dependencies": [
"collection",
"meta"
]
},
{
"name": "args",
"version": "2.7.0",
"dependencies": []
},
{
"name": "ansi_styles",
"version": "0.3.2+1",
"dependencies": []
},
{
"name": "win32",
"version": "5.15.0",
"dependencies": [
"ffi"
]
},
{
"name": "path_provider",
"version": "2.1.5",
"dependencies": [
"flutter",
"path_provider_android",
"path_provider_foundation",
"path_provider_linux",
"path_provider_platform_interface",
"path_provider_windows"
]
},
{
"name": "ffi",
"version": "2.1.4",
"dependencies": []
},
{
"name": "js",
"version": "0.6.7",
"dependencies": [
"meta"
]
},
{
"name": "flutter_web_plugins",
"version": "0.0.0",
"dependencies": [
"flutter"
]
},
{
"name": "plugin_platform_interface",
"version": "2.1.8",
"dependencies": [
"meta"
]
},
{
"name": "sky_engine",
"version": "0.0.0",
"dependencies": []
},
{
"name": "vector_math",
"version": "2.2.0",
"dependencies": []
},
{
"name": "material_color_utilities",
"version": "0.11.1",
"dependencies": [
"collection"
]
},
{
"name": "characters",
"version": "1.4.0",
"dependencies": []
},
{
"name": "source_span",
"version": "1.10.1",
"dependencies": [
"collection",
"path",
"term_glyph"
]
},
{
"name": "json_annotation",
"version": "4.9.0",
"dependencies": [
"meta"
]
},
{
"name": "checked_yaml",
"version": "2.0.4",
"dependencies": [
"json_annotation",
"source_span",
"yaml"
]
},
{
"name": "process",
"version": "5.0.5",
"dependencies": [
"file",
"path",
"platform"
]
},
{
"name": "io",
"version": "1.0.5",
"dependencies": [
"meta",
"path",
"string_scanner"
]
},
{
"name": "charcode",
"version": "1.4.0",
"dependencies": []
},
{
"name": "stack_trace",
"version": "1.12.1",
"dependencies": [
"path"
]
},
{
"name": "web",
"version": "1.1.1",
"dependencies": []
},
{
"name": "http_parser",
"version": "4.1.2",
"dependencies": [
"collection",
"source_span",
"string_scanner",
"typed_data"
]
},
{
"name": "path_provider_windows",
"version": "2.3.0",
"dependencies": [
"ffi",
"flutter",
"path",
"path_provider_platform_interface"
]
},
{
"name": "path_provider_platform_interface",
"version": "2.1.2",
"dependencies": [
"flutter",
"platform",
"plugin_platform_interface"
]
},
{
"name": "path_provider_linux",
"version": "2.2.1",
"dependencies": [
"ffi",
"flutter",
"path",
"path_provider_platform_interface",
"xdg_directories"
]
},
{
"name": "path_provider_foundation",
"version": "2.4.3",
"dependencies": [
"flutter",
"path_provider_platform_interface"
]
},
{
"name": "path_provider_android",
"version": "2.2.20",
"dependencies": [
"flutter",
"path_provider_platform_interface"
]
},
{
"name": "term_glyph",
"version": "1.2.2",
"dependencies": []
},
{
"name": "typed_data",
"version": "1.4.0",
"dependencies": [
"collection"
]
},
{
"name": "xdg_directories",
"version": "1.1.0",
"dependencies": [
"meta",
"path"
]
}
],
"configVersion": 1
}

Binary file not shown.

1
.dart_tool/version Normal file
View File

@@ -0,0 +1 @@
3.35.7

View File

@@ -0,0 +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-12-04 12:33:12.174628","version":"3.35.7","swift_package_manager_enabled":{"ios":false,"macos":false}}

1
.idea/modules.xml generated
View File

@@ -11,6 +11,7 @@
<module fileurl="file://$PROJECT_DIR$/modules/notifications/melos_notifications.iml" filepath="$PROJECT_DIR$/modules/notifications/melos_notifications.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$/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$/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_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/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" /> <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>

1
apps/mobile_app/.env Normal file
View File

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

View File

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

View File

@@ -0,0 +1,10 @@
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'env.dart';
class QuestiaEnvConfig implements EnvConfig {
@override
String get apiBaseUrl => Env.apiBaseUrl;
// @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/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:design_system/design_system.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:sf_app_platform/navigation/app_router.dart';
import 'package:navigation/navigation_module.dart'; import 'package:navigation/navigation_module.dart';
import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'package:sf_localizations/sf_localizations.dart'; import 'package:sf_localizations/sf_localizations.dart';
Future<void> main() async { Future<void> main() async {
@@ -11,7 +15,9 @@ Future<void> main() async {
navigationModule(); navigationModule();
configureAppRouter(); configureAppRouter();
themePackages(); themePackages();
await dotenv.load(fileName: '.env');
await configureDependencies(QuestiaEnvConfig(), log: kDebugMode);
runApp(const ProviderScope(child: PlatformApp())); runApp(const ProviderScope(child: PlatformApp()));
} }
@@ -48,4 +54,4 @@ class PlatformApp extends ConsumerWidget {
}, },
); );
} }
} }

View File

@@ -15,7 +15,7 @@ late final GoRouter appRouter;
void configureAppRouter() { void configureAppRouter() {
appRouter = GoRouter( appRouter = GoRouter(
navigatorKey: rootNavigatorKey, navigatorKey: rootNavigatorKey,
initialLocation: AppRoutes.onboarding, initialLocation: AppRoutes.linkPhone,
debugLogDiagnostics: true, debugLogDiagnostics: true,
routes: [ routes: [
GoRoute( GoRoute(

View File

@@ -168,6 +168,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
country_code_picker:
dependency: transitive
description:
name: country_code_picker
sha256: f0411f4833b6f98e8b7215f4fa3813bcc88e50f13925f70a170dbd36e3e447f5
url: "https://pub.dev"
source: hosted
version: "3.4.1"
coverage: coverage:
dependency: transitive dependency: transitive
description: description:
@@ -214,6 +222,30 @@ packages:
relative: true relative: true
source: path source: path
version: "0.0.1" 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: equatable:
dependency: transitive dependency: transitive
description: description:
@@ -259,6 +291,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" 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: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@@ -408,6 +448,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.20.2" version: "0.20.2"
intl_phone_field_v2:
dependency: transitive
description:
name: intl_phone_field_v2
sha256: b1e5077e31cc8705639a69b2e0410a8ecc858c3e518726d99b378b6c35adfefb
url: "https://pub.dev"
source: hosted
version: "4.0.5"
io: io:
dependency: transitive dependency: transitive
description: description:
@@ -597,6 +645,13 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.3" version: "3.0.3"
sf_infrastructure:
dependency: "direct main"
description:
path: "../../packages/sf_infrastructure"
relative: true
source: path
version: "0.0.1"
sf_localizations: sf_localizations:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@@ -57,12 +57,15 @@ dependencies:
path: ../../packages/sf_localizations path: ../../packages/sf_localizations
fonts: fonts:
path: ../../packages/fonts path: ../../packages/fonts
sf_infrastructure:
path: ../../packages/sf_infrastructure
#dependencies go here #dependencies go here
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
flutter_svg: ^2.2.1 flutter_svg: ^2.2.1
go_router_builder: ^4.1.1 go_router_builder: ^4.1.1
build_runner: ^2.7.1 build_runner: ^2.7.1
flutter_dotenv: ^6.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@@ -89,7 +92,7 @@ flutter:
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
assets: assets:
- assets/images/ui/ - assets/images/ui/
- .env
# An image asset can refer to one or more resolution-specific "variants", see # An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images # 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,fonts # melos_managed_dependency_overrides: auth,dashboard_shell,design_system,home,navigation,notifications,profile,sf_shared,utils,sf_localizations,fonts,sf_infrastructure
dependency_overrides: dependency_overrides:
auth: auth:
path: ../../modules/auth path: ../../modules/auth
@@ -16,6 +16,8 @@ dependency_overrides:
path: ../../modules/notifications path: ../../modules/notifications
profile: profile:
path: ../../modules/profile path: ../../modules/profile
sf_infrastructure:
path: ../../packages/sf_infrastructure
sf_localizations: sf_localizations:
path: ../../packages/sf_localizations path: ../../packages/sf_localizations
sf_shared: sf_shared:

View File

@@ -1,8 +1,8 @@
export 'src/device_sign_up/link_watch/create_profile_screen.dart'; export 'src/features/device_sign_up/link_watch/create_profile_screen.dart';
export 'src/onboarding/onboarding_builder.dart'; export 'src/features/onboarding/onboarding_builder.dart';
export 'src/login/link_phone_builder.dart'; export 'src/features/link_phone/link_phone_builder.dart';
export 'src/login/phone_code_builder.dart'; export 'src/features/login/phone_code_builder.dart';
export 'src/login/login_builder.dart'; export 'src/features/login/login_builder.dart';
export 'src/recover_password/recover_password_builder.dart'; export 'src/features/recover_password/recover_password_builder.dart';
export 'src/device_sign_up/device_signup_builder.dart'; export 'src/features/device_sign_up/device_signup_builder.dart';
export 'src/sign_up/signup_builder.dart'; export 'src/features/sign_up/signup_builder.dart';

View File

@@ -0,0 +1,5 @@
abstract class AuthRemoteDatasource {
Future<void> requestPhoneCode({required String phone});
Future<void> verifyPhoneCode({required String phone, required String code});
}

View File

@@ -0,0 +1,53 @@
import 'package:dio/dio.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 al solicitar el código');
}
}
@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 al verificar el código');
}
}
Exception _mapDioError(DioException error, {required String defaultMessage}) {
final responseData = error.response?.data;
String message = defaultMessage;
if (responseData is Map<String, dynamic>) {
final serverMessage = responseData['message'];
if (serverMessage is String && serverMessage.isNotEmpty) {
message = serverMessage;
}
} else if (error.message != null && error.message!.isNotEmpty) {
message = error.message!;
}
return Exception(message);
}
}

View File

@@ -0,0 +1,18 @@
import 'package:auth/src/core/data/datasource/auth_remote_datasource.dart';
import 'package:auth/src/core/domain/repositories/auth_repository.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);
}
}

View File

@@ -0,0 +1,5 @@
abstract class AuthRepository {
Future<void> requestPhoneCode({required String phone});
Future<void> verifyPhoneCode({required String phone, 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,4 +1,4 @@
import 'package:auth/src/device_sign_up/device_signup_screen.dart'; import 'package:auth/src/features/device_sign_up/device_signup_screen.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';

View File

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

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,18 @@
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}) {
return _repository.requestPhoneCode(phone: phone);
}
@override
Future<void> verifyCode({required String phone, required String code}) {
return _repository.verifyPhoneCode(phone: phone, code: code);
}
}

View File

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

View File

@@ -0,0 +1,110 @@
import 'package:auth/src/features/link_phone/presentation/link_phone_view_model.dart';
import 'package:design_system/src/dropdowns/country_prefix_picker.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 LinkPhoneScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const LinkPhoneScreen({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(
initialCountryCode: viewState.dialCode,
onChanged: (country) {
viewModel.updateDialCode(
country.dialCode ?? viewState.dialCode,
);
},
),
Expanded(
child: CustomTextField(
controller: viewModel.phoneNumberController,
hint: context.translate(I18n.phoneNumber),
numeric: true,
),
),
],
),
],
),
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,78 @@
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/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;
@override
LinkPhoneViewState build() {
_linkPhoneUseCase = ref.read(linkPhoneUseCaseProvider);
phoneNumberController = TextEditingController();
phoneNumberController.addListener(_onPhoneNumberChanged);
ref.onDispose(disposeControllers);
return const LinkPhoneViewState();
}
void _onPhoneNumberChanged() {
final raw = phoneNumberController.text;
state = state.copyWith(phoneNumber: raw, errorMessage: '');
}
void updateDialCode(String dialCode) {
state = state.copyWith(dialCode: dialCode, errorMessage: '');
}
Future<void> requestCode() async {
final trimmedNumber = state.phoneNumber.trim();
if (trimmedNumber.isEmpty) {
state = state.copyWith(errorMessage: 'El teléfono no puede estar vacío');
return;
}
final fullPhone = '${state.dialCode}$trimmedNumber';
state = state.copyWith(
isLoading: true,
errorMessage: '',
codeRequested: 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,
);
}
}
void disposeControllers() {
phoneNumberController.removeListener(_onPhoneNumberChanged);
phoneNumberController.dispose();
}
}

View File

@@ -0,0 +1,14 @@
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,
}) = _LinkPhoneViewState;
}

View File

@@ -0,0 +1,283 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of '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;
/// 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));
}
@override
int get hashCode => Object.hash(runtimeType,phoneNumber,dialCode,errorMessage,isLoading,codeRequested);
@override
String toString() {
return 'LinkPhoneViewState(phoneNumber: $phoneNumber, dialCode: $dialCode, errorMessage: $errorMessage, isLoading: $isLoading, codeRequested: $codeRequested)';
}
}
/// @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
});
}
/// @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,}) {
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,
));
}
}
/// 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)? $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);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) $default,) {final _that = this;
switch (_that) {
case _LinkPhoneViewState():
return $default(_that.phoneNumber,_that.dialCode,_that.errorMessage,_that.isLoading,_that.codeRequested);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)? $default,) {final _that = this;
switch (_that) {
case _LinkPhoneViewState() when $default != null:
return $default(_that.phoneNumber,_that.dialCode,_that.errorMessage,_that.isLoading,_that.codeRequested);case _:
return null;
}
}
}
/// @nodoc
class _LinkPhoneViewState implements LinkPhoneViewState {
const _LinkPhoneViewState({this.phoneNumber = '', this.dialCode = '+34', this.errorMessage = '', this.isLoading = false, this.codeRequested = 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;
/// 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));
}
@override
int get hashCode => Object.hash(runtimeType,phoneNumber,dialCode,errorMessage,isLoading,codeRequested);
@override
String toString() {
return 'LinkPhoneViewState(phoneNumber: $phoneNumber, dialCode: $dialCode, errorMessage: $errorMessage, isLoading: $isLoading, codeRequested: $codeRequested)';
}
}
/// @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
});
}
/// @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,}) {
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,
));
}
}
// dart format on

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

@@ -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:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';

View File

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

View File

@@ -1,5 +1,5 @@
import 'package:auth/src/login/presentation/loading_google_screen.dart'; import 'package:auth/src/features/login/presentation/loading_google_screen.dart';
import 'package:auth/src/sign_up/signup_screen.dart'; import 'package:auth/src/features/sign_up/signup_screen.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -15,7 +15,7 @@ class LoginScreen extends ConsumerStatefulWidget {
ConsumerState<ConsumerStatefulWidget> createState() => _LoginScreenState(); ConsumerState<ConsumerStatefulWidget> createState() => _LoginScreenState();
} }
class _LoginScreenState extends ConsumerState<LoginScreen>{ class _LoginScreenState extends ConsumerState<LoginScreen> {
bool passwordVisible = false; bool passwordVisible = false;
@override @override
@@ -28,7 +28,11 @@ class _LoginScreenState extends ConsumerState<LoginScreen>{
Column( Column(
spacing: 8, spacing: 8,
children: [ children: [
Icon(Icons.check, color: theme.getColorFor(ThemeCode.buttonPrimary), size: 50), Icon(
Icons.check,
color: theme.getColorFor(ThemeCode.buttonPrimary),
size: 50,
),
Text( Text(
// context.translate(I18n.example) // context.translate(I18n.example)
"¡Te damos la bienvenida!", "¡Te damos la bienvenida!",
@@ -51,43 +55,48 @@ class _LoginScreenState extends ConsumerState<LoginScreen>{
spacing: 12, spacing: 12,
children: [ children: [
CustomTextField( CustomTextField(
showPassword: passwordVisible, showPassword: passwordVisible,
label: "Contraseña", label: "Contraseña",
hint: "********" hint: "********",
), ),
Align( Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: CustomTextButton( child: CustomTextButton(
text: "¿Has olvidado la contraseña?", text: "¿Has olvidado la contraseña?",
onPressed: () => onPressed: () => widget.navigationContract.pushTo(
widget.navigationContract.pushTo(AppRoutes.recoverPassword), AppRoutes.recoverPassword,
),
size: 16, size: 16,
)), ),
),
], ],
) ),
], ],
), ),
PrimaryButton( PrimaryButton(
onPressed: () => widget.navigationContract.goTo(AppRoutes.dashboardHome), onPressed: () =>
text: "Iniciar sesión", widget.navigationContract.goTo(AppRoutes.dashboardHome),
color: theme.getColorFor(ThemeCode.buttonPrimary) text: "Iniciar sesión",
color: theme.getColorFor(ThemeCode.buttonPrimary),
), ),
Container( Container(
padding: EdgeInsets.only(top: 24), padding: EdgeInsets.only(top: 24),
child: Column( child: Column(
spacing: 24, spacing: 24,
children: [ children: [
Stack(children: [ Stack(
Divider(endIndent: 74, indent: 74), children: [
Align( Divider(endIndent: 74, indent: 74),
Align(
alignment: Alignment.center, alignment: Alignment.center,
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 14), padding: const EdgeInsets.symmetric(horizontal: 14),
color: theme.getColorFor(ThemeCode.backgroundPrimary), color: theme.getColorFor(ThemeCode.backgroundPrimary),
child: Text("o continúa con"), child: Text("o continúa con"),
) ),
) ),
]), ],
),
Row( Row(
spacing: 20, spacing: 20,
children: [ children: [
@@ -105,7 +114,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>{
label: "Google", label: "Google",
), ),
SecondaryButton( SecondaryButton(
onPressed: ()=>{}, onPressed: () => {},
radius: 16, radius: 16,
padding: 44, padding: 44,
icon: Icons.apple, icon: Icons.apple,
@@ -121,18 +130,23 @@ class _LoginScreenState extends ConsumerState<LoginScreen>{
spacing: 8, spacing: 8,
children: [ children: [
Text( Text(
"¿No tienes cuenta?", "¿No tienes cuenta?",
style: TextStyle(fontSize: 18, letterSpacing: 0) style: TextStyle(fontSize: 18, letterSpacing: 0),
), ),
TextButton( TextButton(
onPressed: () => widget.navigationContract.goTo(AppRoutes.signup), onPressed: () =>
widget.navigationContract.goTo(AppRoutes.signup),
child: Text( child: Text(
"Crear una ahora", "Crear una ahora",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: 0) style: TextStyle(
) fontSize: 18,
fontWeight: FontWeight.w500,
letterSpacing: 0,
),
),
), ),
], ],
) ),
], ],
), ),
]; ];
@@ -153,8 +167,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen>{
itemCount: content.length, itemCount: content.length,
), ),
), ),
) ),
) ),
); );
} }
} }

View File

@@ -5,12 +5,10 @@ import 'package:navigation/navigation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
class PhoneCodeScreen extends ConsumerWidget { class PhoneCodeScreen extends ConsumerWidget {
// final String phone;
final NavigationContract navigationContract; final NavigationContract navigationContract;
PhoneCodeScreen({super.key, required this.navigationContract}); PhoneCodeScreen({super.key, required this.navigationContract});
// class PhoneCodeScreenState extends State<PhoneCodeScreen> {
final focusNodes = List<FocusNode>.generate(6, (int i) { final focusNodes = List<FocusNode>.generate(6, (int i) {
return FocusNode(); return FocusNode();
}); });
@@ -34,7 +32,10 @@ class PhoneCodeScreen extends ConsumerWidget {
children: [ children: [
Text( Text(
"Conéctate", "Conéctate",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30), style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
),
), ),
Text.rich( Text.rich(
TextSpan( TextSpan(
@@ -55,7 +56,9 @@ class PhoneCodeScreen extends ConsumerWidget {
child: TextField( child: TextField(
focusNode: focusNodes[i], focusNode: focusNodes[i],
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
inputFormatters: [FilteringTextInputFormatter.digitsOnly], inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
textAlign: TextAlign.center, textAlign: TextAlign.center,
decoration: InputDecoration( decoration: InputDecoration(
hintText: "0", hintText: "0",
@@ -78,7 +81,9 @@ class PhoneCodeScreen extends ConsumerWidget {
spacing: 24, spacing: 24,
children: [ children: [
PrimaryButton( PrimaryButton(
onPressed: () => {navigationContract.pushTo(AppRoutes.login)}, onPressed: () => {
navigationContract.pushTo(AppRoutes.login),
},
text: "Entrar", text: "Entrar",
color: theme.getColorFor(ThemeCode.buttonPrimary), color: theme.getColorFor(ThemeCode.buttonPrimary),
), ),
@@ -87,7 +92,11 @@ class PhoneCodeScreen extends ConsumerWidget {
children: [ children: [
Text( Text(
"¿No lo has recibido?", "¿No lo has recibido?",
style: TextStyle(fontSize: 18, letterSpacing: 0, height: 1.5), style: TextStyle(
fontSize: 18,
letterSpacing: 0,
height: 1.5,
),
), ),
CustomTextButton( CustomTextButton(
onPressed: () => {}, onPressed: () => {},
@@ -96,7 +105,7 @@ class PhoneCodeScreen extends ConsumerWidget {
weight: FontWeight.w500, weight: FontWeight.w500,
), ),
], ],
) ),
], ],
), ),
Spacer(flex: 10), Spacer(flex: 10),

View File

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

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: Color(0xFFF7F7F7),
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(0xFF4B4B4B),
),
const SizedBox(height: 48),
Container(
padding: const EdgeInsets.symmetric(horizontal: 24),
width: double.infinity,
child: TextButton(
onPressed: goToNext,
style: TextButton.styleFrom(
backgroundColor: isLast
? const Color(0xFF329E95)
: const Color(0xFF4B4B4B),
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(0xFF4B4B4B),
decoration: TextDecoration.underline,
fontWeight: FontWeight.w500,
fontSize: 18,
),
),
),
),
),
const SizedBox(height: 36),
],
),
),
);
}
}

View File

@@ -1,6 +1,6 @@
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:auth/src/onboarding/presentation/onboarding_view_state.dart'; import 'package:auth/src/features/onboarding/presentation/onboarding_view_state.dart';
final onBoardingViewModelProvider = final onBoardingViewModelProvider =
NotifierProvider.autoDispose<OnBoardingViewModel, OnboardingViewState>( NotifierProvider.autoDispose<OnBoardingViewModel, OnboardingViewState>(

View File

@@ -0,0 +1,49 @@
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 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: 30,
height: 1.25,
letterSpacing: 0,
color: Color(0xFF4B4B4B),
),
),
const SizedBox(height: 16),
Text(
context.translate(subtitle),
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 18,
height: 1.5,
letterSpacing: 0,
color: Color(0xFF4B4B4B),
),
),
],
);
}
}

View File

@@ -1,4 +1,4 @@
import 'package:auth/src/recover_password/presentation/sent_screen.dart'; import 'package:auth/src/features/recover_password/presentation/sent_screen.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:navigation/navigation.dart'; import 'package:navigation/navigation.dart';
@@ -47,26 +47,30 @@ class RestorePasswordScreen extends ConsumerWidget {
spacing: 8, spacing: 8,
children: [ children: [
Align( Align(
alignment: Alignment.bottomLeft, alignment: Alignment.bottomLeft,
child: Text( child: Text(
"Teléfono móvil", "Teléfono móvil",
style: TextStyle(fontSize: 14, letterSpacing: 0), style: TextStyle(fontSize: 14, letterSpacing: 0),
) ),
), ),
Row( Row(
spacing: 10, spacing: 10,
children: [ children: [
CustomDropdown( CustomDropdown(
value: 0, value: 0,
items: [Icon(Icons.outlined_flag), Icon(Icons.outlined_flag), Icon(Icons.outlined_flag)], items: [
onChanged: (value)=> {}, Icon(Icons.outlined_flag),
Icon(Icons.outlined_flag),
Icon(Icons.outlined_flag),
],
onChanged: (value) => {},
width: 80, width: 80,
), ),
Expanded( Expanded(
child: CustomTextField( child: CustomTextField(
hint: "Teléfono", hint: "Teléfono",
numeric: true numeric: true,
) ),
), ),
], ],
), ),
@@ -75,11 +79,14 @@ class RestorePasswordScreen extends ConsumerWidget {
Row( Row(
spacing: 20, spacing: 20,
children: [ children: [
Expanded( child: SecondaryButton( Expanded(
child: SecondaryButton(
onPressed: () => {Navigator.pop(context)}, onPressed: () => {Navigator.pop(context)},
text: "Volver" text: "Volver",
)), ),
Expanded( child: PrimaryButton( ),
Expanded(
child: PrimaryButton(
onPressed: () => { onPressed: () => {
Navigator.push( Navigator.push(
context, context,
@@ -90,8 +97,9 @@ class RestorePasswordScreen extends ConsumerWidget {
}, },
text: "Enviar", text: "Enviar",
size: 16, size: 16,
color: theme.getColorFor(ThemeCode.buttonSecondary) color: theme.getColorFor(ThemeCode.buttonSecondary),
)), ),
),
], ],
), ),
], ],

View File

@@ -1,4 +1,4 @@
import 'package:auth/src/recover_password/presentation/new_password_screen.dart'; import 'package:auth/src/features/recover_password/presentation/new_password_screen.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -6,10 +6,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
class SentScreen extends ConsumerWidget { class SentScreen extends ConsumerWidget {
final String format; final String format;
const SentScreen({ const SentScreen({super.key, required this.format});
super.key,
required this.format
});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
@@ -27,7 +24,11 @@ class SentScreen extends ConsumerWidget {
Text( Text(
"Recuperar contraseña", "Recuperar contraseña",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.w500, fontSize: 30, letterSpacing: 0), style: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 30,
letterSpacing: 0,
),
), ),
Row( Row(
spacing: 10, spacing: 10,
@@ -38,9 +39,9 @@ class SentScreen extends ConsumerWidget {
color: theme.getColorFor(ThemeCode.buttonPrimary), color: theme.getColorFor(ThemeCode.buttonPrimary),
), ),
Text( Text(
format=="email" format == "email"
?"Correo enviado correctamente" ? "Correo enviado correctamente"
:"SMS enviado correctamente", : "SMS enviado correctamente",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
), ),
Spacer(), Spacer(),
@@ -50,16 +51,16 @@ class SentScreen extends ConsumerWidget {
spacing: 16, spacing: 16,
children: [ children: [
Text( Text(
format=="email" format == "email"
?"Revisa tu email y haz clic en el enlace para crear una nueva contraseña." ? "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.", : "Revisa tu móvil y sigue las instrucciones para crear una nueva contraseña.",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(fontSize: 18, letterSpacing: 0), style: TextStyle(fontSize: 18, letterSpacing: 0),
), ),
Text( Text(
format=="email" format == "email"
?"Si no recibes el correo en unos minutos, revisa tu carpeta de spam o pulsa \"Reenviar correo\"." ? "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 \".", : "Si no recibes el SMS en unos minutos, asegúrate de tener cobertura o pulsa \"Reenviar SMS \".",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, letterSpacing: 0), style: TextStyle(fontSize: 14, letterSpacing: 0),
), ),
@@ -68,22 +69,22 @@ class SentScreen extends ConsumerWidget {
Row( Row(
spacing: 10, spacing: 10,
children: [ children: [
Expanded( child: SecondaryButton( Expanded(
onPressed: () => {}, child: SecondaryButton(
text: format=="email" onPressed: () => {},
?"Reenviar correo" text: format == "email"
:"Reenviar SMS" ? "Reenviar correo"
)), : "Reenviar SMS",
),
),
Expanded( Expanded(
child: PrimaryButton( child: PrimaryButton(
onPressed: ()=>Navigator.push( onPressed: () => Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(builder: (_) => NewPasswordScreen()),
builder: (_) => NewPasswordScreen(),
),
), ),
text: "Continuar", text: "Continuar",
color: theme.getColorFor(ThemeCode.buttonSecondary) color: theme.getColorFor(ThemeCode.buttonSecondary),
), ),
), ),
], ],

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:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';

View File

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

View File

@@ -1,9 +1,9 @@
import 'package:auth/src/widgets/layouts/form_step_layout.dart'; import 'package:auth/src/widgets/layouts/form_step_layout.dart';
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:auth/src/sign_up/signup_address_screen.dart'; import 'package:auth/src/features/sign_up/signup_address_screen.dart';
import 'package:auth/src/sign_up/signup_personal_screen.dart'; import 'package:auth/src/features/sign_up/signup_personal_screen.dart';
import 'package:auth/src/sign_up/signup_user_screen.dart'; import 'package:auth/src/features/sign_up/signup_user_screen.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:navigation/navigation.dart'; import 'package:navigation/navigation.dart';
@@ -13,10 +13,7 @@ import 'account_created_screen.dart';
class SignupScreen extends ConsumerStatefulWidget { class SignupScreen extends ConsumerStatefulWidget {
NavigationContract navigationContract; NavigationContract navigationContract;
SignupScreen({ SignupScreen({super.key, required this.navigationContract});
super.key,
required this.navigationContract
});
@override @override
ConsumerState<SignupScreen> createState() => _SignupScreenState(); ConsumerState<SignupScreen> createState() => _SignupScreenState();
@@ -37,8 +34,9 @@ class _SignupScreenState extends ConsumerState<SignupScreen> {
return [ return [
FormStepLayout( FormStepLayout(
title: "Crea tu usuario", title: "Crea tu usuario",
subtitle: "Con tu email y tu número podremos mantenerte siempre informado", subtitle:
supertitle: "Usuario y contacto", "Con tu email y tu número podremos mantenerte siempre informado",
supertitle: "Usuario y contacto",
currentStep: 1, currentStep: 1,
numSteps: 3, numSteps: 3,
body: [SignupPersonalScreen()], body: [SignupPersonalScreen()],
@@ -46,20 +44,26 @@ class _SignupScreenState extends ConsumerState<SignupScreen> {
Row( Row(
spacing: 16, spacing: 16,
children: [ children: [
Expanded(child: SecondaryButton( Expanded(
onPressed: ()=>{}, child: SecondaryButton(
text: "Atrás", onPressed: () => {},
size: 16, text: "Atrás",
)), size: 16,
Expanded(child: PrimaryButton( ),
onPressed: ()=>{setState(() { ),
currentStep++; Expanded(
})}, child: PrimaryButton(
text: "Siguiente", onPressed: () => {
size: 16, setState(() {
color: theme.getColorFor(ThemeCode.buttonSecondary) currentStep++;
)) }),
] },
text: "Siguiente",
size: 16,
color: theme.getColorFor(ThemeCode.buttonSecondary),
),
),
],
), ),
CheckboxListTile( CheckboxListTile(
value: acceptTerms, value: acceptTerms,
@@ -74,42 +78,57 @@ class _SignupScreenState extends ConsumerState<SignupScreen> {
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
activeColor: theme.getColorFor(ThemeCode.buttonPrimary), activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
controlAffinity: ListTileControlAffinity.leading, controlAffinity: ListTileControlAffinity.leading,
) ),
], ],
nextStep: ()=>{setState(() { nextStep: () => {
currentStep++; setState(() {
})}, currentStep++;
previousStep: ()=>{}, }),
},
previousStep: () => {},
), ),
FormStepLayout( FormStepLayout(
title: "Tu dirección", title: "Tu dirección",
subtitle: "Tu dirección nos ayudará a verificar y mantener la seguridad de tu cuenta", subtitle:
"Tu dirección nos ayudará a verificar y mantener la seguridad de tu cuenta",
supertitle: "Domicilio", supertitle: "Domicilio",
currentStep: 2, currentStep: 2,
numSteps: 3, numSteps: 3,
body: [SignupAddressScreen()], body: [SignupAddressScreen()],
nextStep: ()=>{setState(() { nextStep: () => {
currentStep++; setState(() {
})}, currentStep++;
previousStep: ()=>{setState(() { }),
currentStep--; },
})}, previousStep: () => {
setState(() {
currentStep--;
}),
},
), ),
FormStepLayout( FormStepLayout(
title: "Identifícate", title: "Identifícate",
subtitle: "Contraseña mínima de 8 caracteres, con una mayúscula, un número y un carácter especial", subtitle:
"Contraseña mínima de 8 caracteres, con una mayúscula, un número y un carácter especial",
supertitle: "Usuario y contacto", supertitle: "Usuario y contacto",
currentStep: 3, currentStep: 3,
numSteps: 3, numSteps: 3,
body: [SignupUserScreen()], body: [SignupUserScreen()],
nextStep: ()=>{setState(() { nextStep: () => {
currentStep++; setState(() {
})}, currentStep++;
previousStep: ()=>{setState(() { }),
currentStep--; },
})}, previousStep: () => {
setState(() {
currentStep--;
}),
},
),
AccountCreatedScreen(
navigationContract: widget.navigationContract,
kidAccount: false,
), ),
AccountCreatedScreen(navigationContract: widget.navigationContract, kidAccount: false)
]; ];
} }
} }

View File

@@ -1,78 +0,0 @@
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 LinkPhoneScreen extends ConsumerWidget {
final NavigationContract navigationContract;
const LinkPhoneScreen({super.key, required this.navigationContract});
@override
Widget build(BuildContext context, WidgetRef ref) {
final theme = ref.watch(themePortProvider);
// TextEditingController phoneController = TextEditingController();
// String? phone;
return Scaffold(
backgroundColor: theme.getColorFor(ThemeCode.backgroundPrimary),
body: SafeArea(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 24),
child: Expanded(
child: Center(
child: Column(
spacing: 48,
children: [
Spacer(flex: 8),
Text(
"¡Nos alegra mucho tenerte por aquí!",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30, fontWeight: FontWeight.w500, letterSpacing: 0),
),
Text(
"Para poder entrar de forma segura, te vamos a enviar un código al teléfono",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16, letterSpacing: 0),
),
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",
numeric: true
)
),
],
),
],
),
PrimaryButton(
onPressed: () => navigationContract.pushTo(AppRoutes.phoneCode),
text: "Siguiente",
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
Spacer(flex: 10)
],
),
),
),
),
));
}
}

View File

@@ -1,128 +0,0 @@
import 'package:auth/src/onboarding/domain/onboarding_page.dart';
import 'package:auth/src/onboarding/presentation/onboarding_view_model.dart';
import 'package:auth/src/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: SafeArea(
child: 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,
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: 36),
],
),
),
),
);
}
}

View File

@@ -1,52 +0,0 @@
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

@@ -22,6 +22,8 @@ dependencies:
path: ../../packages/navigation path: ../../packages/navigation
sf_localizations: sf_localizations:
path: ../../packages/sf_localizations path: ../../packages/sf_localizations
sf_infrastructure:
path: ../../packages/sf_infrastructure
#dependencies go here #dependencies go here
flutter_svg: ^2.2.1 flutter_svg: ^2.2.1
get_it: ^9.0.5 get_it: ^9.0.5
@@ -29,6 +31,8 @@ dependencies:
flutter_riverpod: ^3.0.3 flutter_riverpod: ^3.0.3
freezed_annotation: ^3.1.0 freezed_annotation: ^3.1.0
freezed: ^3.2.3 freezed: ^3.2.3
dio: ^5.9.0
country_code_picker: ^3.4.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

View File

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

View File

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

View File

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

View File

@@ -9,4 +9,5 @@ export 'src/snackbars/snackbar.dart';
export 'src/buttons/primary_button.dart'; export 'src/buttons/primary_button.dart';
export 'src/buttons/secondary_button.dart'; export 'src/buttons/secondary_button.dart';
export 'src/buttons/custom_text_button.dart'; export 'src/buttons/custom_text_button.dart';
export 'src/dropdowns/dropdown.dart'; export 'src/dropdowns/dropdown.dart';
export 'src/dropdowns/country_prefix_picker.dart';

View File

@@ -1,28 +1,27 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class CustomTextButton extends StatelessWidget{ class CustomTextButton extends StatelessWidget {
final VoidCallback onPressed;
final onPressed;
final String text; final String text;
final double size; final double size;
final FontWeight weight; final FontWeight weight;
final Color? color; final Color? color;
@override
const CustomTextButton({ const CustomTextButton({
super.key, super.key,
required this.onPressed, required this.onPressed,
required this.text, required this.text,
this.size = 14, this.size = 14,
this.weight = FontWeight.normal, this.weight = FontWeight.normal,
this.color this.color,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return TextButton( return TextButton(
style: ButtonStyle(padding: WidgetStatePropertyAll(EdgeInsets.zero)), style: const ButtonStyle(
padding: WidgetStatePropertyAll<EdgeInsetsGeometry>(EdgeInsets.zero),
),
onPressed: onPressed, onPressed: onPressed,
child: Text( child: Text(
text, text,
@@ -30,10 +29,10 @@ class CustomTextButton extends StatelessWidget{
fontSize: size, fontSize: size,
fontWeight: weight, fontWeight: weight,
letterSpacing: 0, letterSpacing: 0,
color: color?? Color(0xFF4B4B4B), color: color ?? const Color(0xFF4B4B4B),
decoration: TextDecoration.underline decoration: TextDecoration.underline,
), ),
) ),
); );
} }
} }

View File

@@ -1,9 +1,7 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class PrimaryButton extends StatelessWidget{ class PrimaryButton extends StatelessWidget {
final onPressed; final VoidCallback onPressed;
final String text; final String text;
final Color color; final Color color;
final double height; final double height;
@@ -12,7 +10,8 @@ class PrimaryButton extends StatelessWidget{
final double radius; final double radius;
final double padding; final double padding;
PrimaryButton({ const PrimaryButton({
super.key,
required this.onPressed, required this.onPressed,
required this.text, required this.text,
required this.color, required this.color,
@@ -25,30 +24,35 @@ class PrimaryButton extends StatelessWidget{
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FilledButton( return FilledButton(
style: ButtonStyle( style: ButtonStyle(
backgroundColor: WidgetStatePropertyAll<Color>(color), backgroundColor: WidgetStatePropertyAll<Color>(color),
padding: WidgetStatePropertyAll(EdgeInsets.symmetric(horizontal: padding)), padding: WidgetStatePropertyAll<EdgeInsetsGeometry>(
shape: WidgetStatePropertyAll(RoundedRectangleBorder( EdgeInsets.symmetric(horizontal: padding),
borderRadius: BorderRadius.all(Radius.circular(radius)), ),
)), shape: WidgetStatePropertyAll<OutlinedBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(radius)),
),
),
), ),
onPressed: onPressed, onPressed: onPressed,
child: SizedBox( child: SizedBox(
width: width, width: width,
height: height, height: height,
child: Center(child: Text( child: Center(
text, child: Text(
textAlign: TextAlign.center, text,
style: TextStyle( textAlign: TextAlign.center,
fontSize: size, style: TextStyle(
fontWeight: FontWeight.w500, fontSize: size,
letterSpacing: 0, fontWeight: FontWeight.w500,
color: Colors.white//theme.getColorFor(ThemeCode.textSecondary) letterSpacing: 0,
) color: Colors.white, // theme.getColorFor(ThemeCode.textSecondary)
)) ),
) ),
),
),
); );
} }
} }

View File

@@ -1,10 +1,7 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class SecondaryButton extends StatelessWidget { class SecondaryButton extends StatelessWidget {
final VoidCallback onPressed;
final onPressed;
final String? text; final String? text;
final IconData? icon; final IconData? icon;
final String? label; final String? label;
@@ -15,8 +12,8 @@ class SecondaryButton extends StatelessWidget {
final double? width; final double? width;
final double? size; final double? size;
@override const SecondaryButton({
SecondaryButton({ super.key,
required this.onPressed, required this.onPressed,
this.text, this.text,
this.icon, this.icon,
@@ -31,37 +28,43 @@ class SecondaryButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return OutlinedButton( return OutlinedButton(
style: ButtonStyle( style: ButtonStyle(
padding: WidgetStatePropertyAll(EdgeInsets.symmetric(horizontal: padding)), padding: WidgetStatePropertyAll(
shape: WidgetStatePropertyAll(RoundedRectangleBorder( EdgeInsets.symmetric(horizontal: padding),
borderRadius: BorderRadius.all(Radius.circular(radius)), ),
side: BorderSide(color: Color(0xFF4B4B4B)) shape: WidgetStatePropertyAll(
)), RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(radius)),
side: BorderSide(color: Color(0xFF4B4B4B)),
),
),
), ),
onPressed: onPressed, onPressed: onPressed,
child: SizedBox( child: SizedBox(
width: width, width: width,
height: height, height: height,
child: Center(child: text!=null ? Text( child: Center(
text!, child: text != null
textAlign: TextAlign.center, ? Text(
semanticsLabel: label, text!,
style: TextStyle( textAlign: TextAlign.center,
fontSize: size ?? 18, semanticsLabel: label,
fontWeight: FontWeight.w500, style: TextStyle(
letterSpacing: 0, fontSize: size ?? 18,
color: Color(0xFF4B4B4B) fontWeight: FontWeight.w500,
) letterSpacing: 0,
) : Icon( color: Color(0xFF4B4B4B),
icon, ),
semanticLabel: label, )
size: size ?? 24, : Icon(
color: color ?? Color(0xFF4B4B4B) icon,
)) semanticLabel: label,
) size: size ?? 24,
color: color ?? Color(0xFF4B4B4B),
),
),
),
); );
} }
}
}

View File

@@ -0,0 +1,79 @@
import 'package:country_code_picker/country_code_picker.dart';
import 'package:flutter/material.dart';
class CountryPrefixPicker extends StatelessWidget {
const CountryPrefixPicker({
super.key,
required this.onChanged,
this.initialCountryCode = '+34',
this.radius = 12,
this.width = 90,
this.height = 55,
this.borderColor = const Color(0xFF4B4B4B),
this.backgroundColor = Colors.white,
});
final ValueChanged<CountryCode> onChanged;
final String initialCountryCode;
final double radius;
final double width;
final double height;
final Color borderColor;
final Color backgroundColor;
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
height: height,
child: CountryCodePicker(
onChanged: onChanged,
initialSelection: initialCountryCode,
showFlag: false,
showDropDownButton: false,
hideMainText: true,
padding: EdgeInsets.zero,
builder: (CountryCode? country) {
if (country == null) {
return const SizedBox.shrink();
}
return InputDecorator(
decoration: InputDecoration(
isDense: false,
contentPadding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 16,
),
filled: true,
fillColor: backgroundColor,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(radius)),
borderSide: BorderSide(color: borderColor),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(radius)),
borderSide: BorderSide(color: borderColor),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (country.flagUri != null)
Image.asset(
country.flagUri!,
package: 'country_code_picker',
width: 24,
height: 24,
fit: BoxFit.cover,
),
const Icon(Icons.arrow_drop_down, size: 24),
],
),
);
},
),
);
}
}

View File

@@ -1,12 +1,10 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class CustomDropdown extends StatelessWidget{ class CustomDropdown extends StatelessWidget {
final List<Widget> items; final List<Widget> items;
final values; final List<dynamic>? values;
final onChanged; final ValueChanged<dynamic> onChanged;
final value; final dynamic value;
final String? hint; final String? hint;
final String? label; final String? label;
final double radius; final double radius;
@@ -25,43 +23,54 @@ class CustomDropdown extends StatelessWidget{
this.radius = 12, this.radius = 12,
this.width = double.infinity, this.width = double.infinity,
this.height = 70, this.height = 70,
this.color this.color,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
spacing: 8, spacing: 8,
children: [ children: [
if (label != null) Align( if (label != null)
alignment: Alignment.bottomLeft, Align(
child: Text( alignment: Alignment.bottomLeft,
label!, child: Text(
style: TextStyle(fontSize: 14, letterSpacing: 0), label!,
style: const TextStyle(fontSize: 14, letterSpacing: 0),
),
), ),
),
SizedBox( SizedBox(
width: width, width: width,
height: height, height: height,
child: Center(child: DropdownButtonFormField( child: Center(
dropdownColor: Colors.white, child: DropdownButtonFormField<dynamic>(
decoration: InputDecoration( dropdownColor: Colors.white,
enabledBorder: OutlineInputBorder( decoration: InputDecoration(
borderRadius: BorderRadius.all(Radius.circular(radius)), enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: color??Color(0xFF4B4B4B)) borderRadius: BorderRadius.all(Radius.circular(radius)),
) borderSide: BorderSide(
color: color ?? const Color(0xFF4B4B4B),
),
),
),
initialValue: value,
onChanged: onChanged,
hint: hint != null ? Text(hint!) : null,
items: List<DropdownMenuItem<dynamic>>.generate(items.length, (
int index,
) {
final dynamic itemValue = values != null
? values![index]
: index;
return DropdownMenuItem<dynamic>(
value: itemValue,
child: items[index],
);
}),
), ),
//underline: Container(), ),
initialValue: value, ),
onChanged: onChanged,
hint: Text(hint??""),
items: List<DropdownMenuItem>.generate(items.length, (int index){
return DropdownMenuItem(value: (values!=null)?values[index]:index, child: items[index]);
})
)),
)
], ],
) ; );
} }
} }

View File

@@ -1,18 +1,17 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class CustomTextField extends StatefulWidget{ class CustomTextField extends StatefulWidget {
bool? showPassword; final bool? showPassword;
final bool numeric; final bool numeric;
final String hint; final String hint;
final String label; final String label;
final int? lines; final int? lines;
final ValueChanged<String>? onChanged; final ValueChanged<String>? onChanged;
final int? length; final int? length;
final TextEditingController? controller;
CustomTextField({ const CustomTextField({
super.key, super.key,
this.showPassword, this.showPassword,
this.numeric = false, this.numeric = false,
@@ -21,63 +20,72 @@ class CustomTextField extends StatefulWidget{
this.lines, this.lines,
this.length, this.length,
this.onChanged, this.onChanged,
this.controller,
}); });
@override @override
State<CustomTextField> createState() => CustomTextFieldState(); State<CustomTextField> createState() => CustomTextFieldState();
} }
class CustomTextFieldState extends State<CustomTextField>{ class CustomTextFieldState extends State<CustomTextField> {
late bool _showPassword;
@override
void initState() {
super.initState();
_showPassword = widget.showPassword ?? true;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
spacing: 8, spacing: 8,
children: [ children: [
?widget.label == '' ? null : Align( if (widget.label.isNotEmpty)
alignment: Alignment.bottomLeft, Align(
child: Text( alignment: Alignment.bottomLeft,
widget.label, child: Text(
style: TextStyle(fontSize: 14, letterSpacing: 0), widget.label,
) style: const TextStyle(fontSize: 14, letterSpacing: 0),
), ),
),
TextFormField( TextFormField(
keyboardType: widget.numeric? TextInputType.number : TextInputType.text, keyboardType: widget.numeric
obscureText: !(widget.showPassword ?? true), ? TextInputType.number
enableSuggestions: widget.showPassword ?? true, : TextInputType.text,
autocorrect: !(widget.showPassword ?? false), obscureText: !_showPassword,
style: TextStyle(color: Color(0xFF4B4B4B)), enableSuggestions: _showPassword,
inputFormatters: widget.numeric? [ autocorrect: !_showPassword,
FilteringTextInputFormatter.digitsOnly style: const TextStyle(color: Color(0xFF4B4B4B)),
] : [], inputFormatters: widget.numeric
? <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly]
: const <TextInputFormatter>[],
decoration: InputDecoration( decoration: InputDecoration(
counterText: "", counterText: "",
hintText: widget.hint, hintText: widget.hint,
//labelText: widget.label, border: const OutlineInputBorder(
//floatingLabelBehavior: FloatingLabelBehavior.always,
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12)), borderRadius: BorderRadius.all(Radius.circular(12)),
borderSide: BorderSide(color: Color(0xFF4B4B4B)), borderSide: BorderSide(color: Color(0xFF4B4B4B)),
gapPadding: 16 gapPadding: 16,
), ),
suffixIcon: widget.showPassword!=null ? IconButton( suffixIcon: widget.showPassword != null
icon: Icon(widget.showPassword! ? IconButton(
? Icons.visibility_off icon: Icon(
: Icons.visibility), _showPassword ? Icons.visibility_off : Icons.visibility,
onPressed: () { ),
setState(() { onPressed: () {
widget.showPassword = !widget.showPassword!; setState(() {
}); _showPassword = !_showPassword;
}, });
) : null, },
)
: null,
), ),
minLines: widget.lines ?? 1, minLines: widget.lines ?? 1,
maxLines: widget.lines ?? 1, maxLines: widget.lines ?? 1,
maxLength: widget.length, maxLength: widget.length,
onChanged: widget.onChanged ?? (_)=>{}, onChanged: widget.onChanged,
) ),
], ],
); );
} }
} }

View File

@@ -1,8 +1,7 @@
import 'package:design_system/design_system.dart'; import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class ProgressBar extends ConsumerWidget{ class ProgressBar extends StatelessWidget {
final double max; final double max;
final double value; final double value;
final double height; final double height;
@@ -13,6 +12,7 @@ class ProgressBar extends ConsumerWidget{
final Color textColor; final Color textColor;
const ProgressBar({ const ProgressBar({
super.key,
required this.max, required this.max,
required this.value, required this.value,
required this.height, required this.height,
@@ -20,21 +20,19 @@ class ProgressBar extends ConsumerWidget{
required this.textSecondarySize, required this.textSecondarySize,
required this.backgroundColor, required this.backgroundColor,
required this.foregroundColor, required this.foregroundColor,
required this.textColor,} required this.textColor,
); });
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context) {
return Stack(
return children: [
Stack( LinearProgressIndicator(
children: [
LinearProgressIndicator(
value: value / max, value: value / max,
minHeight: height, minHeight: height,
borderRadius: BorderRadius.all(Radius.circular(24)), borderRadius: BorderRadius.all(Radius.circular(24)),
color: foregroundColor, color: foregroundColor,
backgroundColor: backgroundColor backgroundColor: backgroundColor,
), ),
FractionallySizedBox( FractionallySizedBox(
widthFactor: value / max, widthFactor: value / max,
@@ -47,10 +45,10 @@ class ProgressBar extends ConsumerWidget{
secondarySize: textSecondarySize, secondarySize: textSecondarySize,
color: textColor, color: textColor,
), ),
) ),
), ),
), ),
] ],
); );
} }
} }

View File

@@ -1,32 +1,20 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
enum MessageType { enum MessageType { info, error, warning, success }
info,
error,
warning,
success
}
class CustomSnackBar extends StatelessWidget{ class CustomSnackBar extends StatelessWidget {
final MessageType? type; final MessageType? type;
final String message; final String message;
const CustomSnackBar({ const CustomSnackBar({super.key, this.type, required this.message});
super.key,
this.type,
required this.message,
});
@override @override
SnackBar build(BuildContext context) { SnackBar build(BuildContext context) {
late final Color foregroundColor; late final Color foregroundColor;
late final Color backgroundColor; late final Color backgroundColor;
late final IconData icon; late final IconData icon;
switch (type??MessageType.info){ switch (type ?? MessageType.info) {
case MessageType.info: case MessageType.info:
backgroundColor = Color(0xFFE3EFFD); backgroundColor = Color(0xFFE3EFFD);
foregroundColor = Color(0xFF1F4ECF); foregroundColor = Color(0xFF1F4ECF);
@@ -50,15 +38,20 @@ class CustomSnackBar extends StatelessWidget{
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
side: BorderSide(color: foregroundColor, width: 1), side: BorderSide(color: foregroundColor, width: 1),
borderRadius: BorderRadius.all(Radius.circular(10)) borderRadius: BorderRadius.all(Radius.circular(10)),
), ),
content: Row( content: Row(
spacing: 8, spacing: 8,
children: [ children: [
Icon(icon, color: foregroundColor), Icon(icon, color: foregroundColor),
Expanded(child: Text(message, style: TextStyle(color: Color(0xFF4B4B4B), fontSize: 14))) Expanded(
], child: Text(
), message,
style: TextStyle(color: Color(0xFF4B4B4B), fontSize: 14),
),
),
],
),
); );
} }
} }

View File

@@ -24,7 +24,7 @@ class StepIndicator extends StatelessWidget{
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: isActive ? color : Colors.white, color: isActive ? color : Colors.white,
border: Border.all(color: color, width: 2), border: Border.all(color: color),
), ),
); );
}), }),

View File

@@ -16,6 +16,7 @@ dependencies:
path: ../utils path: ../utils
flutter_riverpod: ^3.0.3 flutter_riverpod: ^3.0.3
get_it: ^9.0.5 get_it: ^9.0.5
country_code_picker: ^3.4.1
fonts: fonts:
path: ../../packages/fonts path: ../../packages/fonts

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

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