diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
new file mode 100644
index 00000000..3b00a9d9
--- /dev/null
+++ b/.dart_tool/package_config.json
@@ -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"
+}
diff --git a/.dart_tool/package_graph.json b/.dart_tool/package_graph.json
new file mode 100644
index 00000000..c8198228
--- /dev/null
+++ b/.dart_tool/package_graph.json
@@ -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
+}
\ No newline at end of file
diff --git a/.dart_tool/pub/bin/melos/melos.dart-3.9.2.snapshot b/.dart_tool/pub/bin/melos/melos.dart-3.9.2.snapshot
new file mode 100644
index 00000000..4d144da8
Binary files /dev/null and b/.dart_tool/pub/bin/melos/melos.dart-3.9.2.snapshot differ
diff --git a/.dart_tool/version b/.dart_tool/version
new file mode 100644
index 00000000..e119acda
--- /dev/null
+++ b/.dart_tool/version
@@ -0,0 +1 @@
+3.35.7
\ No newline at end of file
diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies
new file mode 100644
index 00000000..44e20093
--- /dev/null
+++ b/.flutter-plugins-dependencies
@@ -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}}
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 8b896c94..2c5bd550 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -11,6 +11,7 @@
+
diff --git a/.idea/runConfigurations/melos_flutter_test_sf_infrastructure.xml b/.idea/runConfigurations/melos_flutter_test_sf_infrastructure.xml
new file mode 100644
index 00000000..ad91e09b
--- /dev/null
+++ b/.idea/runConfigurations/melos_flutter_test_sf_infrastructure.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/mobile_app/.env b/apps/mobile_app/.env
new file mode 100644
index 00000000..f8131abf
--- /dev/null
+++ b/apps/mobile_app/.env
@@ -0,0 +1 @@
+API_BASE_URL=https://api-neki-b2b.neki.es/gateway/api/
diff --git a/apps/mobile_app/lib/config/env/env.dart b/apps/mobile_app/lib/config/env/env.dart
new file mode 100644
index 00000000..adaf9578
--- /dev/null
+++ b/apps/mobile_app/lib/config/env/env.dart
@@ -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'] ?? '';
+}
diff --git a/apps/mobile_app/lib/config/env/questia_env_config.dart b/apps/mobile_app/lib/config/env/questia_env_config.dart
new file mode 100644
index 00000000..fee4026c
--- /dev/null
+++ b/apps/mobile_app/lib/config/env/questia_env_config.dart
@@ -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;
+}
diff --git a/apps/mobile_app/lib/main.dart b/apps/mobile_app/lib/main.dart
index d609c0e7..8d3ebb5d 100644
--- a/apps/mobile_app/lib/main.dart
+++ b/apps/mobile_app/lib/main.dart
@@ -1,9 +1,13 @@
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:design_system/design_system.dart';
+import 'package:sf_app_platform/config/env/questia_env_config.dart';
import 'package:sf_app_platform/navigation/app_router.dart';
import 'package:navigation/navigation_module.dart';
+import 'package:sf_infrastructure/sf_infrastructure.dart';
import 'package:sf_localizations/sf_localizations.dart';
Future main() async {
@@ -11,7 +15,9 @@ Future main() async {
navigationModule();
configureAppRouter();
themePackages();
+ await dotenv.load(fileName: '.env');
+ await configureDependencies(QuestiaEnvConfig(), log: kDebugMode);
runApp(const ProviderScope(child: PlatformApp()));
}
@@ -48,4 +54,4 @@ class PlatformApp extends ConsumerWidget {
},
);
}
-}
\ No newline at end of file
+}
diff --git a/apps/mobile_app/lib/navigation/app_router.dart b/apps/mobile_app/lib/navigation/app_router.dart
index 78155c03..237198d5 100644
--- a/apps/mobile_app/lib/navigation/app_router.dart
+++ b/apps/mobile_app/lib/navigation/app_router.dart
@@ -15,7 +15,7 @@ late final GoRouter appRouter;
void configureAppRouter() {
appRouter = GoRouter(
navigatorKey: rootNavigatorKey,
- initialLocation: AppRoutes.onboarding,
+ initialLocation: AppRoutes.linkPhone,
debugLogDiagnostics: true,
routes: [
GoRoute(
diff --git a/apps/mobile_app/pubspec.lock b/apps/mobile_app/pubspec.lock
index 5238aa8d..20e6e08c 100644
--- a/apps/mobile_app/pubspec.lock
+++ b/apps/mobile_app/pubspec.lock
@@ -214,6 +214,22 @@ packages:
relative: true
source: path
version: "0.0.1"
+ dio:
+ dependency: transitive
+ description:
+ name: dio
+ sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.9.0"
+ dio_web_adapter:
+ dependency: transitive
+ description:
+ name: dio_web_adapter
+ sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
equatable:
dependency: transitive
description:
@@ -259,6 +275,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ flutter_dotenv:
+ dependency: "direct main"
+ description:
+ name: flutter_dotenv
+ sha256: d4130c4a43e0b13fefc593bc3961f2cb46e30cb79e253d4a526b1b5d24ae1ce4
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.0"
flutter_lints:
dependency: "direct dev"
description:
@@ -597,6 +621,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.3"
+ sf_infrastructure:
+ dependency: "direct main"
+ description:
+ path: "../../packages/sf_infrastructure"
+ relative: true
+ source: path
+ version: "0.0.1"
sf_localizations:
dependency: "direct main"
description:
diff --git a/apps/mobile_app/pubspec.yaml b/apps/mobile_app/pubspec.yaml
index 1960a01a..09555ae8 100644
--- a/apps/mobile_app/pubspec.yaml
+++ b/apps/mobile_app/pubspec.yaml
@@ -57,12 +57,15 @@ dependencies:
path: ../../packages/sf_localizations
fonts:
path: ../../packages/fonts
+ sf_infrastructure:
+ path: ../../packages/sf_infrastructure
#dependencies go here
cupertino_icons: ^1.0.8
flutter_svg: ^2.2.1
go_router_builder: ^4.1.1
build_runner: ^2.7.1
+ flutter_dotenv: ^6.0.0
dev_dependencies:
flutter_test:
@@ -89,7 +92,7 @@ flutter:
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/ui/
-
+ - .env
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/to/resolution-aware-images
diff --git a/apps/mobile_app/pubspec_overrides.yaml b/apps/mobile_app/pubspec_overrides.yaml
index 377c31a3..41f777ad 100644
--- a/apps/mobile_app/pubspec_overrides.yaml
+++ b/apps/mobile_app/pubspec_overrides.yaml
@@ -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:
auth:
path: ../../modules/auth
@@ -16,6 +16,8 @@ dependency_overrides:
path: ../../modules/notifications
profile:
path: ../../modules/profile
+ sf_infrastructure:
+ path: ../../packages/sf_infrastructure
sf_localizations:
path: ../../packages/sf_localizations
sf_shared:
diff --git a/modules/auth/lib/auth.dart b/modules/auth/lib/auth.dart
index 8f61929c..e425af89 100644
--- a/modules/auth/lib/auth.dart
+++ b/modules/auth/lib/auth.dart
@@ -1,8 +1,8 @@
-export 'src/device_sign_up/link_watch/create_profile_screen.dart';
-export 'src/onboarding/onboarding_builder.dart';
-export 'src/login/link_phone_builder.dart';
-export 'src/login/phone_code_builder.dart';
-export 'src/login/login_builder.dart';
-export 'src/recover_password/recover_password_builder.dart';
-export 'src/device_sign_up/device_signup_builder.dart';
-export 'src/sign_up/signup_builder.dart';
\ No newline at end of file
+export 'src/features/device_sign_up/link_watch/create_profile_screen.dart';
+export 'src/features/onboarding/onboarding_builder.dart';
+export 'src/features/link_phone/link_phone_builder.dart';
+export 'src/features/login/phone_code_builder.dart';
+export 'src/features/login/login_builder.dart';
+export 'src/features/recover_password/recover_password_builder.dart';
+export 'src/features/device_sign_up/device_signup_builder.dart';
+export 'src/features/sign_up/signup_builder.dart';
diff --git a/modules/auth/lib/src/core/data/datasource/auth_remote_datasource.dart b/modules/auth/lib/src/core/data/datasource/auth_remote_datasource.dart
new file mode 100644
index 00000000..573fcef8
--- /dev/null
+++ b/modules/auth/lib/src/core/data/datasource/auth_remote_datasource.dart
@@ -0,0 +1,5 @@
+abstract class AuthRemoteDatasource {
+ Future requestPhoneCode({required String phone});
+
+ Future verifyPhoneCode({required String phone, required String code});
+}
diff --git a/modules/auth/lib/src/core/data/datasource/auth_remote_datasource_impl.dart b/modules/auth/lib/src/core/data/datasource/auth_remote_datasource_impl.dart
new file mode 100644
index 00000000..80b14c6d
--- /dev/null
+++ b/modules/auth/lib/src/core/data/datasource/auth_remote_datasource_impl.dart
@@ -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 requestPhoneCode({required String phone}) async {
+ try {
+ await _repository.post(
+ '/auth/link-phone/request-code',
+ body: {'phone': phone},
+ );
+ } on DioException catch (error) {
+ throw _mapDioError(error, defaultMessage: 'Error al solicitar el código');
+ }
+ }
+
+ @override
+ Future verifyPhoneCode({
+ required String phone,
+ required String code,
+ }) async {
+ try {
+ await _repository.post(
+ '/auth/link-phone/verify-code',
+ body: {'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) {
+ 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);
+ }
+}
diff --git a/modules/auth/lib/src/core/data/repositories/auth_repository_impl.dart b/modules/auth/lib/src/core/data/repositories/auth_repository_impl.dart
new file mode 100644
index 00000000..127c6070
--- /dev/null
+++ b/modules/auth/lib/src/core/data/repositories/auth_repository_impl.dart
@@ -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 requestPhoneCode({required String phone}) {
+ return _remote.requestPhoneCode(phone: phone);
+ }
+
+ @override
+ Future verifyPhoneCode({required String phone, required String code}) {
+ return _remote.verifyPhoneCode(phone: phone, code: code);
+ }
+}
diff --git a/modules/auth/lib/src/core/domain/repositories/auth_repository.dart b/modules/auth/lib/src/core/domain/repositories/auth_repository.dart
new file mode 100644
index 00000000..4584b9db
--- /dev/null
+++ b/modules/auth/lib/src/core/domain/repositories/auth_repository.dart
@@ -0,0 +1,5 @@
+abstract class AuthRepository {
+ Future requestPhoneCode({required String phone});
+
+ Future verifyPhoneCode({required String phone, required String code});
+}
diff --git a/modules/auth/lib/src/core/providers/auth_remote_datasource_provider.dart b/modules/auth/lib/src/core/providers/auth_remote_datasource_provider.dart
new file mode 100644
index 00000000..e4404b71
--- /dev/null
+++ b/modules/auth/lib/src/core/providers/auth_remote_datasource_provider.dart
@@ -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((ref) {
+ final questiaRepository = getIt();
+ return AuthRemoteDatasourceImpl(questiaRepository);
+});
diff --git a/modules/auth/lib/src/core/providers/auth_repository_provider.dart b/modules/auth/lib/src/core/providers/auth_repository_provider.dart
new file mode 100644
index 00000000..c373a93a
--- /dev/null
+++ b/modules/auth/lib/src/core/providers/auth_repository_provider.dart
@@ -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((ref) {
+ final remote = ref.read(authRemoteDatasourceProvider);
+ return AuthRepositoryImpl(remote);
+});
diff --git a/modules/auth/lib/src/device_sign_up/add_kid_screen.dart b/modules/auth/lib/src/features/device_sign_up/add_kid_screen.dart
similarity index 100%
rename from modules/auth/lib/src/device_sign_up/add_kid_screen.dart
rename to modules/auth/lib/src/features/device_sign_up/add_kid_screen.dart
diff --git a/modules/auth/lib/src/device_sign_up/contact_screen.dart b/modules/auth/lib/src/features/device_sign_up/contact_screen.dart
similarity index 100%
rename from modules/auth/lib/src/device_sign_up/contact_screen.dart
rename to modules/auth/lib/src/features/device_sign_up/contact_screen.dart
diff --git a/modules/auth/lib/src/device_sign_up/device_signup_builder.dart b/modules/auth/lib/src/features/device_sign_up/device_signup_builder.dart
similarity index 87%
rename from modules/auth/lib/src/device_sign_up/device_signup_builder.dart
rename to modules/auth/lib/src/features/device_sign_up/device_signup_builder.dart
index f9ed93c6..d2f648ff 100644
--- a/modules/auth/lib/src/device_sign_up/device_signup_builder.dart
+++ b/modules/auth/lib/src/features/device_sign_up/device_signup_builder.dart
@@ -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:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
diff --git a/modules/auth/lib/src/device_sign_up/device_signup_screen.dart b/modules/auth/lib/src/features/device_sign_up/device_signup_screen.dart
similarity index 51%
rename from modules/auth/lib/src/device_sign_up/device_signup_screen.dart
rename to modules/auth/lib/src/features/device_sign_up/device_signup_screen.dart
index 65a8fdd8..2db8e906 100644
--- a/modules/auth/lib/src/device_sign_up/device_signup_screen.dart
+++ b/modules/auth/lib/src/features/device_sign_up/device_signup_screen.dart
@@ -1,24 +1,25 @@
import 'package:auth/auth.dart';
-import 'package:auth/src/device_sign_up/add_kid_screen.dart';
-import 'package:auth/src/device_sign_up/link_watch/link_watch_screen.dart';
-import 'package:auth/src/device_sign_up/link_watch/link_watch_previous_screen.dart';
-import 'package:auth/src/sign_up/account_created_screen.dart';
+import 'package:auth/src/features/device_sign_up/add_kid_screen.dart';
+import 'package:auth/src/features/device_sign_up/link_watch/link_watch_screen.dart';
+import 'package:auth/src/features/device_sign_up/link_watch/link_watch_previous_screen.dart';
+import 'package:auth/src/features/sign_up/account_created_screen.dart';
import 'package:auth/src/widgets/layouts/form_step_layout.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:navigation/navigation.dart';
-class DeviceSignupScreen extends ConsumerStatefulWidget{
+class DeviceSignupScreen extends ConsumerStatefulWidget {
final NavigationContract navigationContract;
const DeviceSignupScreen({super.key, required this.navigationContract});
@override
- ConsumerState createState() => DeviceSignupScreenState(navigationContract);
+ ConsumerState createState() =>
+ DeviceSignupScreenState(navigationContract);
}
-class DeviceSignupScreenState extends ConsumerState{
+class DeviceSignupScreenState extends ConsumerState {
late int currentStep;
final NavigationContract navigationContract;
@@ -34,37 +35,46 @@ class DeviceSignupScreenState extends ConsumerState{
return getSteps()[currentStep];
}
- List getSteps(){
+ List getSteps() {
final theme = ref.watch(themePortProvider);
final continueBtn = Container(
color: theme.getColorFor(ThemeCode.backgroundPrimary),
child: PrimaryButton(
- onPressed: ()=>{setState(() {
- currentStep++;
- })},
+ onPressed: () => {
+ setState(() {
+ currentStep++;
+ }),
+ },
text: "Continuar",
- color: theme.getColorFor(ThemeCode.buttonPrimary)
- )
+ color: theme.getColorFor(ThemeCode.buttonPrimary),
+ ),
);
-
+
return [
- AddKidScreen(nextStep: ()=>{setState(() {
- currentStep++;
- })}),
+ AddKidScreen(
+ nextStep: () => {
+ setState(() {
+ currentStep++;
+ }),
+ },
+ ),
FormStepLayout(
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,
numSteps: 3,
body: [CreateProfileScreen()],
- footer: [Container(
- padding: EdgeInsets.all(24),
- color: theme.getColorFor(ThemeCode.backgroundPrimary),
- child: continueBtn
- )],
- nextStep: ()=>{},
- previousStep: ()=>{}
+ footer: [
+ Container(
+ padding: EdgeInsets.all(24),
+ color: theme.getColorFor(ThemeCode.backgroundPrimary),
+ child: continueBtn,
+ ),
+ ],
+ nextStep: () => {},
+ previousStep: () => {},
),
FormStepLayout(
title: "Vincula su correa y su reloj",
@@ -72,28 +82,31 @@ class DeviceSignupScreenState extends ConsumerState{
numSteps: 3,
body: [LinkWatchPreviousScreen()],
footer: [continueBtn],
- nextStep: ()=>{},
- previousStep: ()=>{}
+ nextStep: () => {},
+ previousStep: () => {},
),
FormStepLayout(
title: "Vincula su correa\ny su reloj",
currentStep: 2,
numSteps: 3,
- body: [LinkWatchScreen(step:1)],
+ body: [LinkWatchScreen(step: 1)],
footer: [continueBtn],
- nextStep: ()=>{},
- previousStep: ()=>{}
+ nextStep: () => {},
+ previousStep: () => {},
),
FormStepLayout(
title: "Vincula su correa\ny su reloj",
currentStep: 2,
numSteps: 3,
- body: [LinkWatchScreen(step:2)],
+ body: [LinkWatchScreen(step: 2)],
footer: [continueBtn],
- nextStep: ()=>{},
- previousStep: ()=>{}
+ nextStep: () => {},
+ previousStep: () => {},
+ ),
+ AccountCreatedScreen(
+ navigationContract: navigationContract,
+ kidAccount: true,
),
- AccountCreatedScreen(navigationContract: navigationContract, kidAccount: true)
];
}
-}
\ No newline at end of file
+}
diff --git a/modules/auth/lib/src/device_sign_up/link_watch/create_profile_screen.dart b/modules/auth/lib/src/features/device_sign_up/link_watch/create_profile_screen.dart
similarity index 100%
rename from modules/auth/lib/src/device_sign_up/link_watch/create_profile_screen.dart
rename to modules/auth/lib/src/features/device_sign_up/link_watch/create_profile_screen.dart
diff --git a/modules/auth/lib/src/device_sign_up/link_watch/link_watch_previous_screen.dart b/modules/auth/lib/src/features/device_sign_up/link_watch/link_watch_previous_screen.dart
similarity index 100%
rename from modules/auth/lib/src/device_sign_up/link_watch/link_watch_previous_screen.dart
rename to modules/auth/lib/src/features/device_sign_up/link_watch/link_watch_previous_screen.dart
diff --git a/modules/auth/lib/src/device_sign_up/link_watch/link_watch_screen.dart b/modules/auth/lib/src/features/device_sign_up/link_watch/link_watch_screen.dart
similarity index 100%
rename from modules/auth/lib/src/device_sign_up/link_watch/link_watch_screen.dart
rename to modules/auth/lib/src/features/device_sign_up/link_watch/link_watch_screen.dart
diff --git a/modules/auth/lib/src/features/link_phone/domain/use_cases/link_phone_use_case.dart b/modules/auth/lib/src/features/link_phone/domain/use_cases/link_phone_use_case.dart
new file mode 100644
index 00000000..fd7fce0e
--- /dev/null
+++ b/modules/auth/lib/src/features/link_phone/domain/use_cases/link_phone_use_case.dart
@@ -0,0 +1,5 @@
+abstract class LinkPhoneUseCase {
+ Future requestCode({required String phone});
+
+ Future verifyCode({required String phone, required String code});
+}
diff --git a/modules/auth/lib/src/features/link_phone/domain/use_cases/link_phone_use_case_impl.dart b/modules/auth/lib/src/features/link_phone/domain/use_cases/link_phone_use_case_impl.dart
new file mode 100644
index 00000000..18e3c34e
--- /dev/null
+++ b/modules/auth/lib/src/features/link_phone/domain/use_cases/link_phone_use_case_impl.dart
@@ -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 requestCode({required String phone}) {
+ return _repository.requestPhoneCode(phone: phone);
+ }
+
+ @override
+ Future verifyCode({required String phone, required String code}) {
+ return _repository.verifyPhoneCode(phone: phone, code: code);
+ }
+}
diff --git a/modules/auth/lib/src/login/link_phone_builder.dart b/modules/auth/lib/src/features/link_phone/link_phone_builder.dart
similarity index 86%
rename from modules/auth/lib/src/login/link_phone_builder.dart
rename to modules/auth/lib/src/features/link_phone/link_phone_builder.dart
index ee261810..543f46e5 100644
--- a/modules/auth/lib/src/login/link_phone_builder.dart
+++ b/modules/auth/lib/src/features/link_phone/link_phone_builder.dart
@@ -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:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
diff --git a/modules/auth/lib/src/features/link_phone/presentation/link_phone_screen.dart b/modules/auth/lib/src/features/link_phone/presentation/link_phone_screen.dart
new file mode 100644
index 00000000..938d7a71
--- /dev/null
+++ b/modules/auth/lib/src/features/link_phone/presentation/link_phone_screen.dart
@@ -0,0 +1,114 @@
+import 'package:auth/src/features/link_phone/presentation/link_phone_view_model.dart';
+import 'package:design_system/design_system.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:navigation/navigation.dart';
+import 'package:sf_localizations/sf_localizations.dart';
+
+class LinkPhoneScreen extends ConsumerWidget {
+ final NavigationContract navigationContract;
+
+ const LinkPhoneScreen({super.key, required this.navigationContract});
+
+ void _onCountryChanged(int? value) {}
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final theme = ref.watch(themePortProvider);
+
+ final viewModel = ref.watch(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: [
+ CustomDropdown(
+ value: 0,
+ items: const [
+ Icon(Icons.outlined_flag),
+ Icon(Icons.outlined_flag),
+ Icon(Icons.outlined_flag),
+ ],
+ onChanged: _onCountryChanged,
+ width: 80,
+ ),
+ Expanded(
+ child: CustomTextField(
+ // controller: viewModel.phoneNumberController,
+ hint: context.translate(I18n.phoneNumber),
+ numeric: true,
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+
+ const SizedBox(height: 16),
+
+ if (viewState.errorMessage?.isNotEmpty ?? false) ...[
+ const SizedBox(height: 4),
+ Text(
+ // context.translate(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),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_model.dart b/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_model.dart
new file mode 100644
index 00000000..c921380f
--- /dev/null
+++ b/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_model.dart
@@ -0,0 +1,61 @@
+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.new,
+ );
+
+class LinkPhoneViewModel extends Notifier {
+ 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: '');
+ }
+
+ Future requestCode() async {
+ final phone = phoneNumberController.text.trim();
+
+ if (phone.isEmpty) {
+ state = state.copyWith(errorMessage: 'El teléfono no puede estar vacío');
+ return;
+ }
+
+ state = state.copyWith(isLoading: true, errorMessage: '');
+
+ try {
+ await _linkPhoneUseCase.requestCode(phone: phone);
+ 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());
+ }
+ }
+
+ void disposeControllers() {
+ phoneNumberController.removeListener(_onPhoneNumberChanged);
+ phoneNumberController.dispose();
+ }
+}
diff --git a/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.dart b/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.dart
new file mode 100644
index 00000000..3d194348
--- /dev/null
+++ b/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.dart
@@ -0,0 +1,13 @@
+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,
+ String? errorMessage,
+ @Default(false) bool isLoading,
+ @Default(false) bool codeRequested,
+ }) = _LinkPhoneViewState;
+}
diff --git a/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.freezed.dart b/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.freezed.dart
new file mode 100644
index 00000000..1159a82f
--- /dev/null
+++ b/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.freezed.dart
@@ -0,0 +1,280 @@
+// 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 value) => value;
+/// @nodoc
+mixin _$LinkPhoneViewState {
+
+ String get phoneNumber; 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 get copyWith => _$LinkPhoneViewStateCopyWithImpl(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.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,errorMessage,isLoading,codeRequested);
+
+@override
+String toString() {
+ return 'LinkPhoneViewState(phoneNumber: $phoneNumber, 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? 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? errorMessage = freezed,Object? isLoading = null,Object? codeRequested = null,}) {
+ return _then(_self.copyWith(
+phoneNumber: null == phoneNumber ? _self.phoneNumber : phoneNumber // ignore: cast_nullable_to_non_nullable
+as String,errorMessage: freezed == 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 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 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? 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 Function( String phoneNumber, 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.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 Function( String phoneNumber, String? errorMessage, bool isLoading, bool codeRequested) $default,) {final _that = this;
+switch (_that) {
+case _LinkPhoneViewState():
+return $default(_that.phoneNumber,_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? Function( String phoneNumber, String? errorMessage, bool isLoading, bool codeRequested)? $default,) {final _that = this;
+switch (_that) {
+case _LinkPhoneViewState() when $default != null:
+return $default(_that.phoneNumber,_that.errorMessage,_that.isLoading,_that.codeRequested);case _:
+ return null;
+
+}
+}
+
+}
+
+/// @nodoc
+
+
+class _LinkPhoneViewState implements LinkPhoneViewState {
+ const _LinkPhoneViewState({this.phoneNumber = '', this.errorMessage, this.isLoading = false, this.codeRequested = false});
+
+
+@override@JsonKey() final String phoneNumber;
+@override 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.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,errorMessage,isLoading,codeRequested);
+
+@override
+String toString() {
+ return 'LinkPhoneViewState(phoneNumber: $phoneNumber, 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? 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? errorMessage = freezed,Object? isLoading = null,Object? codeRequested = null,}) {
+ return _then(_LinkPhoneViewState(
+phoneNumber: null == phoneNumber ? _self.phoneNumber : phoneNumber // ignore: cast_nullable_to_non_nullable
+as String,errorMessage: freezed == 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
diff --git a/modules/auth/lib/src/features/link_phone/presentation/providers/link_phone_provider.dart b/modules/auth/lib/src/features/link_phone/presentation/providers/link_phone_provider.dart
new file mode 100644
index 00000000..e0ceebc3
--- /dev/null
+++ b/modules/auth/lib/src/features/link_phone/presentation/providers/link_phone_provider.dart
@@ -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((ref) {
+ final authRepository = ref.read(authRepositoryProvider);
+ return LinkPhoneUseCaseImpl(authRepository);
+});
diff --git a/modules/auth/lib/src/login/login_builder.dart b/modules/auth/lib/src/features/login/login_builder.dart
similarity index 87%
rename from modules/auth/lib/src/login/login_builder.dart
rename to modules/auth/lib/src/features/login/login_builder.dart
index c72a184e..3d6b349d 100644
--- a/modules/auth/lib/src/login/login_builder.dart
+++ b/modules/auth/lib/src/features/login/login_builder.dart
@@ -1,4 +1,4 @@
-import 'package:auth/src/login/presentation/login_screen.dart';
+import 'package:auth/src/features/login/presentation/login_screen.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
diff --git a/modules/auth/lib/src/login/phone_code_builder.dart b/modules/auth/lib/src/features/login/phone_code_builder.dart
similarity index 86%
rename from modules/auth/lib/src/login/phone_code_builder.dart
rename to modules/auth/lib/src/features/login/phone_code_builder.dart
index 47a63946..e8a31e24 100644
--- a/modules/auth/lib/src/login/phone_code_builder.dart
+++ b/modules/auth/lib/src/features/login/phone_code_builder.dart
@@ -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:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
diff --git a/modules/auth/lib/src/login/presentation/loading_google_screen.dart b/modules/auth/lib/src/features/login/presentation/loading_google_screen.dart
similarity index 100%
rename from modules/auth/lib/src/login/presentation/loading_google_screen.dart
rename to modules/auth/lib/src/features/login/presentation/loading_google_screen.dart
diff --git a/modules/auth/lib/src/login/presentation/loading_screen.dart b/modules/auth/lib/src/features/login/presentation/loading_screen.dart
similarity index 100%
rename from modules/auth/lib/src/login/presentation/loading_screen.dart
rename to modules/auth/lib/src/features/login/presentation/loading_screen.dart
diff --git a/modules/auth/lib/src/login/presentation/login_screen.dart b/modules/auth/lib/src/features/login/presentation/login_screen.dart
similarity index 70%
rename from modules/auth/lib/src/login/presentation/login_screen.dart
rename to modules/auth/lib/src/features/login/presentation/login_screen.dart
index 3cf9cdfe..9758e619 100644
--- a/modules/auth/lib/src/login/presentation/login_screen.dart
+++ b/modules/auth/lib/src/features/login/presentation/login_screen.dart
@@ -1,5 +1,5 @@
-import 'package:auth/src/login/presentation/loading_google_screen.dart';
-import 'package:auth/src/sign_up/signup_screen.dart';
+import 'package:auth/src/features/login/presentation/loading_google_screen.dart';
+import 'package:auth/src/features/sign_up/signup_screen.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -15,7 +15,7 @@ class LoginScreen extends ConsumerStatefulWidget {
ConsumerState createState() => _LoginScreenState();
}
-class _LoginScreenState extends ConsumerState{
+class _LoginScreenState extends ConsumerState {
bool passwordVisible = false;
@override
@@ -28,7 +28,11 @@ class _LoginScreenState extends ConsumerState{
Column(
spacing: 8,
children: [
- Icon(Icons.check, color: theme.getColorFor(ThemeCode.buttonPrimary), size: 50),
+ Icon(
+ Icons.check,
+ color: theme.getColorFor(ThemeCode.buttonPrimary),
+ size: 50,
+ ),
Text(
// context.translate(I18n.example)
"¡Te damos la bienvenida!",
@@ -51,43 +55,48 @@ class _LoginScreenState extends ConsumerState{
spacing: 12,
children: [
CustomTextField(
- showPassword: passwordVisible,
- label: "Contraseña",
- hint: "********"
+ showPassword: passwordVisible,
+ label: "Contraseña",
+ hint: "********",
),
Align(
alignment: Alignment.topLeft,
child: CustomTextButton(
text: "¿Has olvidado la contraseña?",
- onPressed: () =>
- widget.navigationContract.pushTo(AppRoutes.recoverPassword),
+ onPressed: () => widget.navigationContract.pushTo(
+ AppRoutes.recoverPassword,
+ ),
size: 16,
- )),
+ ),
+ ),
],
- )
+ ),
],
),
PrimaryButton(
- onPressed: () => widget.navigationContract.goTo(AppRoutes.dashboardHome),
- text: "Iniciar sesión",
- color: theme.getColorFor(ThemeCode.buttonPrimary)
+ onPressed: () =>
+ widget.navigationContract.goTo(AppRoutes.dashboardHome),
+ text: "Iniciar sesión",
+ color: theme.getColorFor(ThemeCode.buttonPrimary),
),
Container(
padding: EdgeInsets.only(top: 24),
child: Column(
spacing: 24,
children: [
- Stack(children: [
- Divider(endIndent: 74, indent: 74),
- Align(
+ Stack(
+ children: [
+ Divider(endIndent: 74, indent: 74),
+ Align(
alignment: Alignment.center,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 14),
color: theme.getColorFor(ThemeCode.backgroundPrimary),
child: Text("o continúa con"),
- )
- )
- ]),
+ ),
+ ),
+ ],
+ ),
Row(
spacing: 20,
children: [
@@ -105,7 +114,7 @@ class _LoginScreenState extends ConsumerState{
label: "Google",
),
SecondaryButton(
- onPressed: ()=>{},
+ onPressed: () => {},
radius: 16,
padding: 44,
icon: Icons.apple,
@@ -121,18 +130,23 @@ class _LoginScreenState extends ConsumerState{
spacing: 8,
children: [
Text(
- "¿No tienes cuenta?",
- style: TextStyle(fontSize: 18, letterSpacing: 0)
+ "¿No tienes cuenta?",
+ style: TextStyle(fontSize: 18, letterSpacing: 0),
),
TextButton(
- onPressed: () => widget.navigationContract.goTo(AppRoutes.signup),
+ onPressed: () =>
+ widget.navigationContract.goTo(AppRoutes.signup),
child: Text(
"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{
itemCount: content.length,
),
),
- )
- )
+ ),
+ ),
);
}
}
diff --git a/modules/auth/lib/src/login/presentation/phone_code_screen.dart b/modules/auth/lib/src/features/login/presentation/phone_code_screen.dart
similarity index 84%
rename from modules/auth/lib/src/login/presentation/phone_code_screen.dart
rename to modules/auth/lib/src/features/login/presentation/phone_code_screen.dart
index 9ee6c3e1..5a8f4b71 100644
--- a/modules/auth/lib/src/login/presentation/phone_code_screen.dart
+++ b/modules/auth/lib/src/features/login/presentation/phone_code_screen.dart
@@ -5,12 +5,10 @@ import 'package:navigation/navigation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class PhoneCodeScreen extends ConsumerWidget {
- // final String phone;
final NavigationContract navigationContract;
PhoneCodeScreen({super.key, required this.navigationContract});
- // class PhoneCodeScreenState extends State {
final focusNodes = List.generate(6, (int i) {
return FocusNode();
});
@@ -34,7 +32,10 @@ class PhoneCodeScreen extends ConsumerWidget {
children: [
Text(
"Conéctate",
- style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
+ style: TextStyle(
+ fontWeight: FontWeight.bold,
+ fontSize: 30,
+ ),
),
Text.rich(
TextSpan(
@@ -55,7 +56,9 @@ class PhoneCodeScreen extends ConsumerWidget {
child: TextField(
focusNode: focusNodes[i],
keyboardType: TextInputType.number,
- inputFormatters: [FilteringTextInputFormatter.digitsOnly],
+ inputFormatters: [
+ FilteringTextInputFormatter.digitsOnly,
+ ],
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: "0",
@@ -78,7 +81,9 @@ class PhoneCodeScreen extends ConsumerWidget {
spacing: 24,
children: [
PrimaryButton(
- onPressed: () => {navigationContract.pushTo(AppRoutes.login)},
+ onPressed: () => {
+ navigationContract.pushTo(AppRoutes.login),
+ },
text: "Entrar",
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
@@ -87,7 +92,11 @@ class PhoneCodeScreen extends ConsumerWidget {
children: [
Text(
"¿No lo has recibido?",
- style: TextStyle(fontSize: 18, letterSpacing: 0, height: 1.5),
+ style: TextStyle(
+ fontSize: 18,
+ letterSpacing: 0,
+ height: 1.5,
+ ),
),
CustomTextButton(
onPressed: () => {},
@@ -96,7 +105,7 @@ class PhoneCodeScreen extends ConsumerWidget {
weight: FontWeight.w500,
),
],
- )
+ ),
],
),
Spacer(flex: 10),
diff --git a/modules/auth/lib/src/onboarding/domain/onboarding_page.dart b/modules/auth/lib/src/features/onboarding/domain/onboarding_page.dart
similarity index 100%
rename from modules/auth/lib/src/onboarding/domain/onboarding_page.dart
rename to modules/auth/lib/src/features/onboarding/domain/onboarding_page.dart
diff --git a/modules/auth/lib/src/onboarding/onboarding_builder.dart b/modules/auth/lib/src/features/onboarding/onboarding_builder.dart
similarity index 86%
rename from modules/auth/lib/src/onboarding/onboarding_builder.dart
rename to modules/auth/lib/src/features/onboarding/onboarding_builder.dart
index 45c469ec..b313931e 100644
--- a/modules/auth/lib/src/onboarding/onboarding_builder.dart
+++ b/modules/auth/lib/src/features/onboarding/onboarding_builder.dart
@@ -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:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
diff --git a/modules/auth/lib/src/features/onboarding/presentation/onboarding_screen.dart b/modules/auth/lib/src/features/onboarding/presentation/onboarding_screen.dart
new file mode 100644
index 00000000..a263d04c
--- /dev/null
+++ b/modules/auth/lib/src/features/onboarding/presentation/onboarding_screen.dart
@@ -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((
+ ref,
+) {
+ final controller = PageController();
+ ref.onDispose(controller.dispose);
+ return controller;
+});
+
+class OnboardingScreen extends ConsumerWidget {
+ final NavigationContract navigationContract;
+
+ const OnboardingScreen({super.key, required this.navigationContract});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final state = ref.watch(onBoardingViewModelProvider);
+ final viewModel = ref.read(onBoardingViewModelProvider.notifier);
+ final pageController = ref.watch(onboardingPageControllerProvider);
+
+ final isLast = state.cardIndex >= onboardingPages.length - 1;
+
+ void goToNext() {
+ if (isLast) {
+ navigationContract.goTo(AppRoutes.linkPhone);
+ } else {
+ pageController.nextPage(
+ duration: const Duration(milliseconds: 400),
+ curve: Curves.easeOut,
+ );
+ }
+ }
+
+ return Scaffold(
+ backgroundColor: Colors.white,
+ body: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ child: Column(
+ children: [
+ Expanded(
+ child: PageView.builder(
+ controller: pageController,
+ itemCount: onboardingPages.length,
+ onPageChanged: viewModel.onPageChanged,
+ itemBuilder: (context, index) {
+ final page = onboardingPages[index];
+ return OnboardingContent(
+ image: page.image,
+ title: page.title,
+ subtitle: page.subtitle,
+ );
+ },
+ ),
+ ),
+ StepIndicator(
+ current: state.cardIndex + 1,
+ total: onboardingPages.length,
+ color: const Color(0xFF4A4A4A),
+ ),
+ const SizedBox(height: 38),
+ Container(
+ padding: const EdgeInsets.symmetric(horizontal: 24),
+ width: double.infinity,
+ child: TextButton(
+ onPressed: goToNext,
+ style: TextButton.styleFrom(
+ backgroundColor: isLast
+ ? const Color(0xFF329E95)
+ : const Color(0xFF333333),
+ foregroundColor: Colors.white,
+ padding: const EdgeInsets.symmetric(
+ vertical: 16,
+ horizontal: 24,
+ ),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(18),
+ ),
+ ),
+ child: Text(
+ isLast
+ ? context.translate(I18n.start)
+ : context.translate(I18n.next),
+ style: const TextStyle(
+ fontSize: 18,
+ fontWeight: FontWeight.w600,
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(height: 8),
+ SizedBox(
+ height: 48,
+ child: Center(
+ child: isLast
+ ? const SizedBox.shrink()
+ : TextButton(
+ onPressed: () =>
+ navigationContract.goTo(AppRoutes.linkPhone),
+ child: Text(
+ context.translate(I18n.skip),
+ style: TextStyle(
+ color: Color(0xFF333333),
+ decoration: TextDecoration.underline,
+ fontWeight: FontWeight.w500,
+ fontSize: 18,
+ ),
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(height: 48),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/modules/auth/lib/src/onboarding/presentation/onboarding_view_model.dart b/modules/auth/lib/src/features/onboarding/presentation/onboarding_view_model.dart
similarity index 83%
rename from modules/auth/lib/src/onboarding/presentation/onboarding_view_model.dart
rename to modules/auth/lib/src/features/onboarding/presentation/onboarding_view_model.dart
index 640e645d..426bf412 100644
--- a/modules/auth/lib/src/onboarding/presentation/onboarding_view_model.dart
+++ b/modules/auth/lib/src/features/onboarding/presentation/onboarding_view_model.dart
@@ -1,6 +1,6 @@
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 =
NotifierProvider.autoDispose(
diff --git a/modules/auth/lib/src/onboarding/presentation/onboarding_view_state.dart b/modules/auth/lib/src/features/onboarding/presentation/onboarding_view_state.dart
similarity index 100%
rename from modules/auth/lib/src/onboarding/presentation/onboarding_view_state.dart
rename to modules/auth/lib/src/features/onboarding/presentation/onboarding_view_state.dart
diff --git a/modules/auth/lib/src/onboarding/presentation/onboarding_view_state.freezed.dart b/modules/auth/lib/src/features/onboarding/presentation/onboarding_view_state.freezed.dart
similarity index 100%
rename from modules/auth/lib/src/onboarding/presentation/onboarding_view_state.freezed.dart
rename to modules/auth/lib/src/features/onboarding/presentation/onboarding_view_state.freezed.dart
diff --git a/modules/auth/lib/src/onboarding/presentation/welcome_screen.dart b/modules/auth/lib/src/features/onboarding/presentation/welcome_screen.dart
similarity index 100%
rename from modules/auth/lib/src/onboarding/presentation/welcome_screen.dart
rename to modules/auth/lib/src/features/onboarding/presentation/welcome_screen.dart
diff --git a/modules/auth/lib/src/onboarding/presentation/widgets/onboarding_content.dart b/modules/auth/lib/src/features/onboarding/presentation/widgets/onboarding_content.dart
similarity index 100%
rename from modules/auth/lib/src/onboarding/presentation/widgets/onboarding_content.dart
rename to modules/auth/lib/src/features/onboarding/presentation/widgets/onboarding_content.dart
diff --git a/modules/auth/lib/src/recover_password/presentation/new_password_screen.dart b/modules/auth/lib/src/features/recover_password/presentation/new_password_screen.dart
similarity index 100%
rename from modules/auth/lib/src/recover_password/presentation/new_password_screen.dart
rename to modules/auth/lib/src/features/recover_password/presentation/new_password_screen.dart
diff --git a/modules/auth/lib/src/recover_password/presentation/restore_password_screen.dart b/modules/auth/lib/src/features/recover_password/presentation/restore_password_screen.dart
similarity index 72%
rename from modules/auth/lib/src/recover_password/presentation/restore_password_screen.dart
rename to modules/auth/lib/src/features/recover_password/presentation/restore_password_screen.dart
index c3f1bfcd..7068801f 100644
--- a/modules/auth/lib/src/recover_password/presentation/restore_password_screen.dart
+++ b/modules/auth/lib/src/features/recover_password/presentation/restore_password_screen.dart
@@ -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:flutter/material.dart';
import 'package:navigation/navigation.dart';
@@ -47,26 +47,30 @@ class RestorePasswordScreen extends ConsumerWidget {
spacing: 8,
children: [
Align(
- alignment: Alignment.bottomLeft,
- child: Text(
- "Teléfono móvil",
- style: TextStyle(fontSize: 14, letterSpacing: 0),
- )
+ 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)=> {},
+ 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
- )
+ child: CustomTextField(
+ hint: "Teléfono",
+ numeric: true,
+ ),
),
],
),
@@ -75,11 +79,14 @@ class RestorePasswordScreen extends ConsumerWidget {
Row(
spacing: 20,
children: [
- Expanded( child: SecondaryButton(
+ Expanded(
+ child: SecondaryButton(
onPressed: () => {Navigator.pop(context)},
- text: "Volver"
- )),
- Expanded( child: PrimaryButton(
+ text: "Volver",
+ ),
+ ),
+ Expanded(
+ child: PrimaryButton(
onPressed: () => {
Navigator.push(
context,
@@ -90,8 +97,9 @@ class RestorePasswordScreen extends ConsumerWidget {
},
text: "Enviar",
size: 16,
- color: theme.getColorFor(ThemeCode.buttonSecondary)
- )),
+ color: theme.getColorFor(ThemeCode.buttonSecondary),
+ ),
+ ),
],
),
],
diff --git a/modules/auth/lib/src/recover_password/presentation/sent_screen.dart b/modules/auth/lib/src/features/recover_password/presentation/sent_screen.dart
similarity index 59%
rename from modules/auth/lib/src/recover_password/presentation/sent_screen.dart
rename to modules/auth/lib/src/features/recover_password/presentation/sent_screen.dart
index 2dc4e3f6..f386c359 100644
--- a/modules/auth/lib/src/recover_password/presentation/sent_screen.dart
+++ b/modules/auth/lib/src/features/recover_password/presentation/sent_screen.dart
@@ -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:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -6,10 +6,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
class SentScreen extends ConsumerWidget {
final String format;
- const SentScreen({
- super.key,
- required this.format
- });
+ const SentScreen({super.key, required this.format});
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -27,7 +24,11 @@ class SentScreen extends ConsumerWidget {
Text(
"Recuperar contraseña",
textAlign: TextAlign.center,
- style: TextStyle(fontWeight: FontWeight.w500, fontSize: 30, letterSpacing: 0),
+ style: TextStyle(
+ fontWeight: FontWeight.w500,
+ fontSize: 30,
+ letterSpacing: 0,
+ ),
),
Row(
spacing: 10,
@@ -38,9 +39,9 @@ class SentScreen extends ConsumerWidget {
color: theme.getColorFor(ThemeCode.buttonPrimary),
),
Text(
- format=="email"
- ?"Correo enviado correctamente"
- :"SMS enviado correctamente",
+ format == "email"
+ ? "Correo enviado correctamente"
+ : "SMS enviado correctamente",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
Spacer(),
@@ -50,16 +51,16 @@ class SentScreen extends ConsumerWidget {
spacing: 16,
children: [
Text(
- format=="email"
- ?"Revisa tu email y haz clic en el enlace para crear una nueva contraseña."
- :"Revisa tu móvil y sigue las instrucciones para crear una nueva contraseña.",
+ format == "email"
+ ? "Revisa tu email y haz clic en el enlace para crear una nueva contraseña."
+ : "Revisa tu móvil y sigue las instrucciones para crear una nueva contraseña.",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18, letterSpacing: 0),
),
Text(
- format=="email"
- ?"Si no recibes el correo en unos minutos, revisa tu carpeta de spam o pulsa \"Reenviar correo\"."
- :"Si no recibes el SMS en unos minutos, asegúrate de tener cobertura o pulsa \"Reenviar SMS \".",
+ format == "email"
+ ? "Si no recibes el correo en unos minutos, revisa tu carpeta de spam o pulsa \"Reenviar correo\"."
+ : "Si no recibes el SMS en unos minutos, asegúrate de tener cobertura o pulsa \"Reenviar SMS \".",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, letterSpacing: 0),
),
@@ -68,22 +69,22 @@ class SentScreen extends ConsumerWidget {
Row(
spacing: 10,
children: [
- Expanded( child: SecondaryButton(
- onPressed: () => {},
- text: format=="email"
- ?"Reenviar correo"
- :"Reenviar SMS"
- )),
+ Expanded(
+ child: SecondaryButton(
+ onPressed: () => {},
+ text: format == "email"
+ ? "Reenviar correo"
+ : "Reenviar SMS",
+ ),
+ ),
Expanded(
child: PrimaryButton(
- onPressed: ()=>Navigator.push(
+ onPressed: () => Navigator.push(
context,
- MaterialPageRoute(
- builder: (_) => NewPasswordScreen(),
- ),
+ MaterialPageRoute(builder: (_) => NewPasswordScreen()),
),
text: "Continuar",
- color: theme.getColorFor(ThemeCode.buttonSecondary)
+ color: theme.getColorFor(ThemeCode.buttonSecondary),
),
),
],
diff --git a/modules/auth/lib/src/recover_password/recover_password_builder.dart b/modules/auth/lib/src/features/recover_password/recover_password_builder.dart
similarity index 84%
rename from modules/auth/lib/src/recover_password/recover_password_builder.dart
rename to modules/auth/lib/src/features/recover_password/recover_password_builder.dart
index 9741c4fc..930f53ba 100644
--- a/modules/auth/lib/src/recover_password/recover_password_builder.dart
+++ b/modules/auth/lib/src/features/recover_password/recover_password_builder.dart
@@ -1,4 +1,4 @@
-import 'package:auth/src/recover_password/presentation/restore_password_screen.dart';
+import 'package:auth/src/features/recover_password/presentation/restore_password_screen.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
diff --git a/modules/auth/lib/src/sign_up/account_created_screen.dart b/modules/auth/lib/src/features/sign_up/account_created_screen.dart
similarity index 100%
rename from modules/auth/lib/src/sign_up/account_created_screen.dart
rename to modules/auth/lib/src/features/sign_up/account_created_screen.dart
diff --git a/modules/auth/lib/src/sign_up/signup_address_screen.dart b/modules/auth/lib/src/features/sign_up/signup_address_screen.dart
similarity index 100%
rename from modules/auth/lib/src/sign_up/signup_address_screen.dart
rename to modules/auth/lib/src/features/sign_up/signup_address_screen.dart
diff --git a/modules/auth/lib/src/sign_up/signup_builder.dart b/modules/auth/lib/src/features/sign_up/signup_builder.dart
similarity index 88%
rename from modules/auth/lib/src/sign_up/signup_builder.dart
rename to modules/auth/lib/src/features/sign_up/signup_builder.dart
index 4356b0b7..70834e73 100644
--- a/modules/auth/lib/src/sign_up/signup_builder.dart
+++ b/modules/auth/lib/src/features/sign_up/signup_builder.dart
@@ -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:go_router/go_router.dart';
import 'package:get_it/get_it.dart';
diff --git a/modules/auth/lib/src/sign_up/signup_personal_screen.dart b/modules/auth/lib/src/features/sign_up/signup_personal_screen.dart
similarity index 100%
rename from modules/auth/lib/src/sign_up/signup_personal_screen.dart
rename to modules/auth/lib/src/features/sign_up/signup_personal_screen.dart
diff --git a/modules/auth/lib/src/sign_up/signup_screen.dart b/modules/auth/lib/src/features/sign_up/signup_screen.dart
similarity index 50%
rename from modules/auth/lib/src/sign_up/signup_screen.dart
rename to modules/auth/lib/src/features/sign_up/signup_screen.dart
index 6de7de56..e1fdc9f9 100644
--- a/modules/auth/lib/src/sign_up/signup_screen.dart
+++ b/modules/auth/lib/src/features/sign_up/signup_screen.dart
@@ -1,9 +1,9 @@
import 'package:auth/src/widgets/layouts/form_step_layout.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
-import 'package:auth/src/sign_up/signup_address_screen.dart';
-import 'package:auth/src/sign_up/signup_personal_screen.dart';
-import 'package:auth/src/sign_up/signup_user_screen.dart';
+import 'package:auth/src/features/sign_up/signup_address_screen.dart';
+import 'package:auth/src/features/sign_up/signup_personal_screen.dart';
+import 'package:auth/src/features/sign_up/signup_user_screen.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:get_it/get_it.dart';
import 'package:navigation/navigation.dart';
@@ -13,10 +13,7 @@ import 'account_created_screen.dart';
class SignupScreen extends ConsumerStatefulWidget {
NavigationContract navigationContract;
- SignupScreen({
- super.key,
- required this.navigationContract
- });
+ SignupScreen({super.key, required this.navigationContract});
@override
ConsumerState createState() => _SignupScreenState();
@@ -37,8 +34,9 @@ class _SignupScreenState extends ConsumerState {
return [
FormStepLayout(
title: "Crea tu usuario",
- subtitle: "Con tu email y tu número podremos mantenerte siempre informado",
- supertitle: "Usuario y contacto",
+ subtitle:
+ "Con tu email y tu número podremos mantenerte siempre informado",
+ supertitle: "Usuario y contacto",
currentStep: 1,
numSteps: 3,
body: [SignupPersonalScreen()],
@@ -46,20 +44,26 @@ class _SignupScreenState extends ConsumerState {
Row(
spacing: 16,
children: [
- Expanded(child: SecondaryButton(
- onPressed: ()=>{},
- text: "Atrás",
- size: 16,
- )),
- Expanded(child: PrimaryButton(
- onPressed: ()=>{setState(() {
- currentStep++;
- })},
- text: "Siguiente",
- size: 16,
- color: theme.getColorFor(ThemeCode.buttonSecondary)
- ))
- ]
+ Expanded(
+ child: SecondaryButton(
+ onPressed: () => {},
+ text: "Atrás",
+ size: 16,
+ ),
+ ),
+ Expanded(
+ child: PrimaryButton(
+ onPressed: () => {
+ setState(() {
+ currentStep++;
+ }),
+ },
+ text: "Siguiente",
+ size: 16,
+ color: theme.getColorFor(ThemeCode.buttonSecondary),
+ ),
+ ),
+ ],
),
CheckboxListTile(
value: acceptTerms,
@@ -74,42 +78,57 @@ class _SignupScreenState extends ConsumerState {
contentPadding: EdgeInsets.zero,
activeColor: theme.getColorFor(ThemeCode.buttonPrimary),
controlAffinity: ListTileControlAffinity.leading,
- )
+ ),
],
- nextStep: ()=>{setState(() {
- currentStep++;
- })},
- previousStep: ()=>{},
+ nextStep: () => {
+ setState(() {
+ currentStep++;
+ }),
+ },
+ previousStep: () => {},
),
FormStepLayout(
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",
currentStep: 2,
numSteps: 3,
body: [SignupAddressScreen()],
- nextStep: ()=>{setState(() {
- currentStep++;
- })},
- previousStep: ()=>{setState(() {
- currentStep--;
- })},
+ nextStep: () => {
+ setState(() {
+ currentStep++;
+ }),
+ },
+ previousStep: () => {
+ setState(() {
+ currentStep--;
+ }),
+ },
),
FormStepLayout(
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",
currentStep: 3,
numSteps: 3,
body: [SignupUserScreen()],
- nextStep: ()=>{setState(() {
- currentStep++;
- })},
- previousStep: ()=>{setState(() {
- currentStep--;
- })},
+ nextStep: () => {
+ setState(() {
+ currentStep++;
+ }),
+ },
+ previousStep: () => {
+ setState(() {
+ currentStep--;
+ }),
+ },
+ ),
+ AccountCreatedScreen(
+ navigationContract: widget.navigationContract,
+ kidAccount: false,
),
- AccountCreatedScreen(navigationContract: widget.navigationContract, kidAccount: false)
];
}
}
diff --git a/modules/auth/lib/src/sign_up/signup_user_screen.dart b/modules/auth/lib/src/features/sign_up/signup_user_screen.dart
similarity index 100%
rename from modules/auth/lib/src/sign_up/signup_user_screen.dart
rename to modules/auth/lib/src/features/sign_up/signup_user_screen.dart
diff --git a/modules/auth/lib/src/login/presentation/link_phone_screen.dart b/modules/auth/lib/src/login/presentation/link_phone_screen.dart
deleted file mode 100644
index 79e2bea6..00000000
--- a/modules/auth/lib/src/login/presentation/link_phone_screen.dart
+++ /dev/null
@@ -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)
- ],
- ),
- ),
- ),
- ),
- ));
- }
-}
diff --git a/modules/auth/lib/src/onboarding/presentation/onboarding_screen.dart b/modules/auth/lib/src/onboarding/presentation/onboarding_screen.dart
deleted file mode 100644
index bef2e8f7..00000000
--- a/modules/auth/lib/src/onboarding/presentation/onboarding_screen.dart
+++ /dev/null
@@ -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((
- 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),
- ],
- ),
- ),
- ),
- );
- }
-}
diff --git a/modules/auth/pubspec.yaml b/modules/auth/pubspec.yaml
index feffab44..70be6cdf 100644
--- a/modules/auth/pubspec.yaml
+++ b/modules/auth/pubspec.yaml
@@ -22,6 +22,8 @@ dependencies:
path: ../../packages/navigation
sf_localizations:
path: ../../packages/sf_localizations
+ sf_infrastructure:
+ path: ../../packages/sf_infrastructure
#dependencies go here
flutter_svg: ^2.2.1
get_it: ^9.0.5
@@ -29,6 +31,7 @@ dependencies:
flutter_riverpod: ^3.0.3
freezed_annotation: ^3.1.0
freezed: ^3.2.3
+ dio: ^5.9.0
dev_dependencies:
flutter_test:
diff --git a/modules/auth/pubspec_overrides.yaml b/modules/auth/pubspec_overrides.yaml
index 65569a2b..9d5c49cf 100644
--- a/modules/auth/pubspec_overrides.yaml
+++ b/modules/auth/pubspec_overrides.yaml
@@ -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:
dashboard_shell:
path: ../dashboard_shell
@@ -14,6 +14,8 @@ dependency_overrides:
path: ../notifications
profile:
path: ../profile
+ sf_infrastructure:
+ path: ../../packages/sf_infrastructure
sf_localizations:
path: ../../packages/sf_localizations
sf_shared:
diff --git a/modules/dashboard_shell/pubspec_overrides.yaml b/modules/dashboard_shell/pubspec_overrides.yaml
index 4d8ae344..e06af733 100644
--- a/modules/dashboard_shell/pubspec_overrides.yaml
+++ b/modules/dashboard_shell/pubspec_overrides.yaml
@@ -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:
auth:
path: ../auth
@@ -14,6 +14,8 @@ dependency_overrides:
path: ../notifications
profile:
path: ../profile
+ sf_infrastructure:
+ path: ../../packages/sf_infrastructure
sf_localizations:
path: ../../packages/sf_localizations
sf_shared:
diff --git a/modules/home/pubspec_overrides.yaml b/modules/home/pubspec_overrides.yaml
index 2e540dc0..10a9562f 100644
--- a/modules/home/pubspec_overrides.yaml
+++ b/modules/home/pubspec_overrides.yaml
@@ -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:
auth:
path: ../auth
@@ -14,6 +14,8 @@ dependency_overrides:
path: ../notifications
profile:
path: ../profile
+ sf_infrastructure:
+ path: ../../packages/sf_infrastructure
sf_localizations:
path: ../../packages/sf_localizations
sf_shared:
diff --git a/packages/sf_infrastructure/.gitignore b/packages/sf_infrastructure/.gitignore
new file mode 100644
index 00000000..dd5eb989
--- /dev/null
+++ b/packages/sf_infrastructure/.gitignore
@@ -0,0 +1,31 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+.flutter-plugins-dependencies
+/build/
+/coverage/
diff --git a/packages/sf_infrastructure/.metadata b/packages/sf_infrastructure/.metadata
new file mode 100644
index 00000000..d7469f07
--- /dev/null
+++ b/packages/sf_infrastructure/.metadata
@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: "adc901062556672b4138e18a4dc62a4be8f4b3c2"
+ channel: "stable"
+
+project_type: package
diff --git a/packages/sf_infrastructure/CHANGELOG.md b/packages/sf_infrastructure/CHANGELOG.md
new file mode 100644
index 00000000..41cc7d81
--- /dev/null
+++ b/packages/sf_infrastructure/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.0.1
+
+* TODO: Describe initial release.
diff --git a/packages/sf_infrastructure/LICENSE b/packages/sf_infrastructure/LICENSE
new file mode 100644
index 00000000..ba75c69f
--- /dev/null
+++ b/packages/sf_infrastructure/LICENSE
@@ -0,0 +1 @@
+TODO: Add your license here.
diff --git a/packages/sf_infrastructure/README.md b/packages/sf_infrastructure/README.md
new file mode 100644
index 00000000..4a260d8d
--- /dev/null
+++ b/packages/sf_infrastructure/README.md
@@ -0,0 +1,39 @@
+
+
+TODO: Put a short description of the package here that helps potential users
+know whether this package might be useful for them.
+
+## Features
+
+TODO: List what your package can do. Maybe include images, gifs, or videos.
+
+## Getting started
+
+TODO: List prerequisites and provide or point to information on how to
+start using the package.
+
+## Usage
+
+TODO: Include short and useful examples for package users. Add longer examples
+to `/example` folder.
+
+```dart
+const like = 'sample';
+```
+
+## Additional information
+
+TODO: Tell users more about the package: where to find more information, how to
+contribute to the package, how to file issues, what response they can expect
+from the package authors, and more.
diff --git a/packages/sf_infrastructure/analysis_options.yaml b/packages/sf_infrastructure/analysis_options.yaml
new file mode 100644
index 00000000..a5744c1c
--- /dev/null
+++ b/packages/sf_infrastructure/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: package:flutter_lints/flutter.yaml
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/packages/sf_infrastructure/lib/configure_dependencies.dart b/packages/sf_infrastructure/lib/configure_dependencies.dart
new file mode 100644
index 00000000..7f18f3cc
--- /dev/null
+++ b/packages/sf_infrastructure/lib/configure_dependencies.dart
@@ -0,0 +1,26 @@
+import 'package:dio/dio.dart';
+import 'package:get_it/get_it.dart';
+
+import 'src/network/dio_client.dart';
+import 'src/env/env_contract.dart';
+import 'src/api/questia_api.dart';
+import 'src/repositories/questia_repository_impl.dart';
+import 'src/repositories/questia_repository.dart';
+
+export 'src/repositories/questia_repository.dart';
+
+final getIt = GetIt.instance;
+
+Future configureDependencies(EnvConfig env, {bool log = false}) async {
+ getIt.registerLazySingleton(
+ () => buildDioClient(
+ baseUrl: env.apiBaseUrl,
+ // apiKey: env.apiKey,
+ log: log,
+ ),
+ );
+ getIt.registerLazySingleton(() => QuestiaApi(getIt()));
+ getIt.registerLazySingleton(
+ () => QuestiaRepositoryImpl(getIt()),
+ );
+}
diff --git a/packages/sf_infrastructure/lib/sf_infrastructure.dart b/packages/sf_infrastructure/lib/sf_infrastructure.dart
new file mode 100644
index 00000000..72a8359b
--- /dev/null
+++ b/packages/sf_infrastructure/lib/sf_infrastructure.dart
@@ -0,0 +1,2 @@
+export 'src/env/env_contract.dart';
+export 'configure_dependencies.dart';
diff --git a/packages/sf_infrastructure/lib/src/api/questia_api.dart b/packages/sf_infrastructure/lib/src/api/questia_api.dart
new file mode 100644
index 00000000..9a27d95f
--- /dev/null
+++ b/packages/sf_infrastructure/lib/src/api/questia_api.dart
@@ -0,0 +1,79 @@
+import 'package:dio/dio.dart';
+
+class QuestiaApi {
+ final Dio _dio;
+
+ QuestiaApi(this._dio);
+
+ Future> get(
+ String path, {
+ Map? queryParameters,
+ Options? options,
+ CancelToken? cancelToken,
+ ProgressCallback? onReceiveProgress,
+ }) {
+ return _dio.get(
+ path,
+ queryParameters: queryParameters,
+ options: options,
+ cancelToken: cancelToken,
+ onReceiveProgress: onReceiveProgress,
+ );
+ }
+
+ Future> post(
+ String path, {
+ dynamic data,
+ Map? queryParameters,
+ Options? options,
+ CancelToken? cancelToken,
+ ProgressCallback? onSendProgress,
+ ProgressCallback? onReceiveProgress,
+ }) {
+ return _dio.post(
+ path,
+ data: data,
+ queryParameters: queryParameters,
+ options: options,
+ cancelToken: cancelToken,
+ onSendProgress: onSendProgress,
+ onReceiveProgress: onReceiveProgress,
+ );
+ }
+
+ Future> put(
+ String path, {
+ dynamic data,
+ Map? queryParameters,
+ Options? options,
+ CancelToken? cancelToken,
+ ProgressCallback? onSendProgress,
+ ProgressCallback? onReceiveProgress,
+ }) {
+ return _dio.put(
+ path,
+ data: data,
+ queryParameters: queryParameters,
+ options: options,
+ cancelToken: cancelToken,
+ onSendProgress: onSendProgress,
+ onReceiveProgress: onReceiveProgress,
+ );
+ }
+
+ Future> delete(
+ String path, {
+ dynamic data,
+ Map? queryParameters,
+ Options? options,
+ CancelToken? cancelToken,
+ }) {
+ return _dio.delete(
+ path,
+ data: data,
+ queryParameters: queryParameters,
+ options: options,
+ cancelToken: cancelToken,
+ );
+ }
+}
diff --git a/packages/sf_infrastructure/lib/src/env/env_contract.dart b/packages/sf_infrastructure/lib/src/env/env_contract.dart
new file mode 100644
index 00000000..a39a2c26
--- /dev/null
+++ b/packages/sf_infrastructure/lib/src/env/env_contract.dart
@@ -0,0 +1,4 @@
+abstract class EnvConfig {
+ String get apiBaseUrl;
+ // String get apiKey;
+}
diff --git a/packages/sf_infrastructure/lib/src/network/dio_client.dart b/packages/sf_infrastructure/lib/src/network/dio_client.dart
new file mode 100644
index 00000000..c56f8963
--- /dev/null
+++ b/packages/sf_infrastructure/lib/src/network/dio_client.dart
@@ -0,0 +1,36 @@
+import 'package:dio/dio.dart';
+
+Dio buildDioClient({
+ required String baseUrl,
+ // required String apiKey,
+ bool log = false,
+}) {
+ final dio = Dio(
+ BaseOptions(
+ baseUrl: baseUrl,
+ connectTimeout: const Duration(seconds: 20),
+ receiveTimeout: const Duration(seconds: 20),
+ sendTimeout: const Duration(seconds: 20),
+ headers: {
+ // if (apiKey.isNotEmpty) 'x-api-key': apiKey,
+ 'accept': 'application/json',
+ 'content-type': 'application/json',
+ },
+ ),
+ );
+
+ if (log) {
+ dio.interceptors.add(
+ LogInterceptor(
+ request: true,
+ requestHeader: false,
+ requestBody: true,
+ responseHeader: false,
+ responseBody: true,
+ error: true,
+ ),
+ );
+ }
+
+ return dio;
+}
diff --git a/packages/sf_infrastructure/lib/src/repositories/questia_repository.dart b/packages/sf_infrastructure/lib/src/repositories/questia_repository.dart
new file mode 100644
index 00000000..fb0b6727
--- /dev/null
+++ b/packages/sf_infrastructure/lib/src/repositories/questia_repository.dart
@@ -0,0 +1,26 @@
+import 'package:dio/dio.dart';
+
+abstract class QuestiaRepository {
+ Future> get(
+ String path, {
+ Map? queryParameters,
+ });
+
+ Future> post(
+ String path, {
+ dynamic body,
+ Map? queryParameters,
+ });
+
+ Future> put(
+ String path, {
+ dynamic body,
+ Map? queryParameters,
+ });
+
+ Future> delete(
+ String path, {
+ dynamic body,
+ Map? queryParameters,
+ });
+}
diff --git a/packages/sf_infrastructure/lib/src/repositories/questia_repository_impl.dart b/packages/sf_infrastructure/lib/src/repositories/questia_repository_impl.dart
new file mode 100644
index 00000000..15fcdebb
--- /dev/null
+++ b/packages/sf_infrastructure/lib/src/repositories/questia_repository_impl.dart
@@ -0,0 +1,45 @@
+import 'package:dio/dio.dart';
+
+import '../api/questia_api.dart';
+import 'questia_repository.dart';
+
+class QuestiaRepositoryImpl implements QuestiaRepository {
+ final QuestiaApi _api;
+
+ QuestiaRepositoryImpl(this._api);
+
+ @override
+ Future> get(
+ String path, {
+ Map? queryParameters,
+ }) {
+ return _api.get(path, queryParameters: queryParameters);
+ }
+
+ @override
+ Future> post(
+ String path, {
+ dynamic body,
+ Map? queryParameters,
+ }) {
+ return _api.post(path, data: body, queryParameters: queryParameters);
+ }
+
+ @override
+ Future> put(
+ String path, {
+ dynamic body,
+ Map? queryParameters,
+ }) {
+ return _api.put(path, data: body, queryParameters: queryParameters);
+ }
+
+ @override
+ Future> delete(
+ String path, {
+ dynamic body,
+ Map? queryParameters,
+ }) {
+ return _api.delete(path, data: body, queryParameters: queryParameters);
+ }
+}
diff --git a/packages/sf_infrastructure/pubspec.yaml b/packages/sf_infrastructure/pubspec.yaml
new file mode 100644
index 00000000..58b9290d
--- /dev/null
+++ b/packages/sf_infrastructure/pubspec.yaml
@@ -0,0 +1,56 @@
+name: sf_infrastructure
+description: "A new Flutter package project."
+version: 0.0.1
+homepage:
+
+environment:
+ sdk: ^3.9.2
+ flutter: ">=1.17.0"
+
+dependencies:
+ dio: ^5.9.0
+ flutter:
+ sdk: flutter
+ get_it: ^9.0.5
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ flutter_lints: ^5.0.0
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+
+ # To add assets to your package, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+ #
+ # For details regarding assets in packages, see
+ # https://flutter.dev/to/asset-from-package
+ #
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/to/resolution-aware-images
+
+ # To add custom fonts to your package, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts in packages, see
+ # https://flutter.dev/to/font-from-package
diff --git a/packages/sf_localizations/assets/l10n/de.json b/packages/sf_localizations/assets/l10n/de.json
index 72c4b830..932a19c9 100644
--- a/packages/sf_localizations/assets/l10n/de.json
+++ b/packages/sf_localizations/assets/l10n/de.json
@@ -8,5 +8,9 @@
"onboardingTitle2": "Gelassenheit bei jeder Zahlung",
"onboardingSubtitle2": "Überwache ihre Ausgaben, setze Limits und begleite sie bei jedem Schritt",
"onboardingTitle3": "Einfache und sichere Zahlungen in ihren Händen",
- "onboardingSubtitle3": "Sie können mit ihrer Uhr bezahlen.\nGanz ohne Handy und Bargeld"
+ "onboardingSubtitle3": "Sie können mit ihrer Uhr bezahlen.\nGanz ohne Handy und Bargeld",
+ "linkPhoneTitle": "Wir freuen uns sehr, dass du hier bist!",
+ "linkPhoneSubtitle": "Um dich sicher anzumelden, senden wir dir einen Code an deine Telefonnummer",
+ "mobilePhone": "Mobiltelefon",
+ "phoneNumber": "Telefonnummer"
}
\ No newline at end of file
diff --git a/packages/sf_localizations/assets/l10n/en.json b/packages/sf_localizations/assets/l10n/en.json
index bcbf501b..74e26676 100755
--- a/packages/sf_localizations/assets/l10n/en.json
+++ b/packages/sf_localizations/assets/l10n/en.json
@@ -8,5 +8,9 @@
"onboardingSubtitle3": "They can pay from their watch.\nNo phone or cash needed",
"start": "Start",
"next": "Next",
- "skip": "Skip"
+ "skip": "Skip",
+ "linkPhoneTitle": "We're really happy to have you here!",
+ "linkPhoneSubtitle": "To sign in securely, we'll send a code to your phone number",
+ "mobilePhone": "Mobile phone",
+ "phoneNumber": "Phone number"
}
\ No newline at end of file
diff --git a/packages/sf_localizations/assets/l10n/es.json b/packages/sf_localizations/assets/l10n/es.json
index 01aa6084..174f4f9a 100644
--- a/packages/sf_localizations/assets/l10n/es.json
+++ b/packages/sf_localizations/assets/l10n/es.json
@@ -8,5 +8,9 @@
"onboardingSubtitle3": "Podrá pagar desde su reloj.\nSin móvil ni efectivo",
"start": "Comenzar",
"next": "Siguiente",
- "skip": "Omitir"
+ "skip": "Omitir",
+ "linkPhoneTitle": "¡Nos alegra mucho tenerte por aquí!",
+ "linkPhoneSubtitle": "Para poder entrar de forma segura, te vamos a enviar un código al teléfono",
+ "mobilePhone": "Teléfono móvil",
+ "phoneNumber": "Teléfono"
}
\ No newline at end of file
diff --git a/packages/sf_localizations/assets/l10n/fr.json b/packages/sf_localizations/assets/l10n/fr.json
index bf1c9a68..5cf274d5 100644
--- a/packages/sf_localizations/assets/l10n/fr.json
+++ b/packages/sf_localizations/assets/l10n/fr.json
@@ -8,5 +8,9 @@
"onboardingTitle2": "La tranquillité à chaque paiement",
"onboardingSubtitle2": "Surveillez leurs dépenses, fixez des limites et accompagnez-les à chaque étape",
"onboardingTitle3": "Des paiements faciles et sécurisés entre leurs mains",
- "onboardingSubtitle3": "Ils peuvent payer avec leur montre.\nSans téléphone ni espèces"
+ "onboardingSubtitle3": "Ils peuvent payer avec leur montre.\nSans téléphone ni espèces",
+ "linkPhoneTitle": "Nous sommes ravis de te compter parmi nous !",
+ "linkPhoneSubtitle": "Pour te connecter en toute sécurité, nous allons envoyer un code sur ton téléphone",
+ "mobilePhone": "Téléphone portable",
+ "phoneNumber": "Numéro de téléphone"
}
\ No newline at end of file
diff --git a/packages/sf_localizations/assets/l10n/it.json b/packages/sf_localizations/assets/l10n/it.json
index 78ef0c94..f32a8895 100644
--- a/packages/sf_localizations/assets/l10n/it.json
+++ b/packages/sf_localizations/assets/l10n/it.json
@@ -8,5 +8,9 @@
"onboardingTitle2": "Tranquillità in ogni pagamento",
"onboardingSubtitle2": "Monitora le sue spese, imposta limiti e accompagnalo in ogni passo",
"onboardingTitle3": "Pagamenti facili e sicuri nelle sue mani",
- "onboardingSubtitle3": "Potrà pagare dal suo orologio.\nSenza telefono né contanti"
+ "onboardingSubtitle3": "Potrà pagare dal suo orologio.\nSenza telefono né contanti",
+ "linkPhoneTitle": "Siamo molto felici di averti qui!",
+ "linkPhoneSubtitle": "Per accedere in modo sicuro, ti invieremo un codice al tuo telefono",
+ "mobilePhone": "Telefono cellulare",
+ "phoneNumber": "Numero di telefono"
}
\ No newline at end of file
diff --git a/packages/sf_localizations/assets/l10n/pt.json b/packages/sf_localizations/assets/l10n/pt.json
index 8b8e7c94..bcd5436b 100644
--- a/packages/sf_localizations/assets/l10n/pt.json
+++ b/packages/sf_localizations/assets/l10n/pt.json
@@ -8,5 +8,9 @@
"onboardingTitle2": "Tranquilidade em cada pagamento",
"onboardingSubtitle2": "Monitore os gastos deles, defina limites e acompanhe cada passo",
"onboardingTitle3": "Pagamentos fáceis e seguros nas mãos deles",
- "onboardingSubtitle3": "Eles poderão pagar pelo relógio.\nSem celular nem dinheiro em espécie"
+ "onboardingSubtitle3": "Eles poderão pagar pelo relógio.\nSem celular nem dinheiro em espécie",
+ "linkPhoneTitle": "Ficamos muito felizes em ter você aqui!",
+ "linkPhoneSubtitle": "Para entrar com segurança, vamos enviar um código para o seu telefone",
+ "mobilePhone": "Telefone celular",
+ "phoneNumber": "Número de telefone"
}
\ No newline at end of file
diff --git a/packages/sf_localizations/lib/src/generated/i18n.dart b/packages/sf_localizations/lib/src/generated/i18n.dart
index a3cb657a..3c7d3d1a 100755
--- a/packages/sf_localizations/lib/src/generated/i18n.dart
+++ b/packages/sf_localizations/lib/src/generated/i18n.dart
@@ -13,4 +13,8 @@ class I18n {
static const String onboardingSubtitle2 = 'onboardingSubtitle2';
static const String onboardingTitle3 = 'onboardingTitle3';
static const String onboardingSubtitle3 = 'onboardingSubtitle3';
+ static const String linkPhoneTitle = 'linkPhoneTitle';
+ static const String linkPhoneSubtitle = 'linkPhoneSubtitle';
+ static const String mobilePhone = 'mobilePhone';
+ static const String phoneNumber = 'phoneNumber';
}