coregen-cli 3.1.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. coregen.py +1585 -0
  2. coregen_cli-3.1.6.dist-info/METADATA +1211 -0
  3. coregen_cli-3.1.6.dist-info/RECORD +172 -0
  4. coregen_cli-3.1.6.dist-info/WHEEL +5 -0
  5. coregen_cli-3.1.6.dist-info/entry_points.txt +2 -0
  6. coregen_cli-3.1.6.dist-info/top_level.txt +2 -0
  7. generators/__init__.py +1 -0
  8. generators/assets/__init__.py +1 -0
  9. generators/assets/assets.py +30 -0
  10. generators/assets/main.py +4 -0
  11. generators/config/__init__.py +1 -0
  12. generators/config/analisy_options.py +42 -0
  13. generators/config/main.py +11 -0
  14. generators/config/pubspec.py +139 -0
  15. generators/cursor/__init__.py +3 -0
  16. generators/cursor/setup.py +87 -0
  17. generators/helpers/__init__.py +56 -0
  18. generators/helpers/component.py +1030 -0
  19. generators/helpers/config.py +255 -0
  20. generators/helpers/data_source.py +180 -0
  21. generators/helpers/domain.py +469 -0
  22. generators/helpers/feature.py +654 -0
  23. generators/helpers/navigation.py +274 -0
  24. generators/helpers/page.py +63 -0
  25. generators/helpers/project.py +33 -0
  26. generators/helpers/utils.py +338 -0
  27. generators/helpers/validation.py +364 -0
  28. generators/initializator.py +68 -0
  29. generators/main.py +81 -0
  30. generators/static/assets/logo.png +0 -0
  31. generators/static/assets/mock/auth_user.json +5 -0
  32. generators/static/assets/svgs/apple.svg +4 -0
  33. generators/static/assets/svgs/google.svg +2 -0
  34. generators/static/cursor/AGENTS.md.jinja +37 -0
  35. generators/static/cursor/agents/doc-writer.md +20 -0
  36. generators/static/cursor/agents/epic-orchestrator.md +28 -0
  37. generators/static/cursor/agents/feature-implementer.md +29 -0
  38. generators/static/cursor/agents/integration-wiring.md +30 -0
  39. generators/static/cursor/agents/layer-application.md +21 -0
  40. generators/static/cursor/agents/layer-guardian.md +21 -0
  41. generators/static/cursor/agents/layer-infrastructure.md +31 -0
  42. generators/static/cursor/agents/layer-model.md +24 -0
  43. generators/static/cursor/agents/layer-presentation.md +33 -0
  44. generators/static/cursor/docs/architecture/APIS_AND_INTEGRATION.md.jinja +42 -0
  45. generators/static/cursor/docs/architecture/CARAVAGGIO_COMPONENTS.md.jinja +317 -0
  46. generators/static/cursor/docs/architecture/DDD_LAYERS.md.jinja +32 -0
  47. generators/static/cursor/docs/architecture/FILE_TEMPLATES.md.jinja +76 -0
  48. generators/static/cursor/docs/architecture/MOCK_AND_REMOTE_DATA.md.jinja +87 -0
  49. generators/static/cursor/docs/architecture/REFERENCE_IMPLEMENTATIONS.md.jinja +48 -0
  50. generators/static/cursor/docs/architecture/WIDGETS_AND_CARAVAGGIO.md.jinja +76 -0
  51. generators/static/cursor/docs/epics/README.md +17 -0
  52. generators/static/cursor/lib/domain/AGENTS.md +23 -0
  53. generators/static/cursor/lib/features/AGENTS.md +23 -0
  54. generators/static/cursor/lib/widgets/AGENTS.md +21 -0
  55. generators/static/cursor/rules/apis-layer.mdc.jinja +21 -0
  56. generators/static/cursor/rules/application-layer.mdc.jinja +12 -0
  57. generators/static/cursor/rules/architecture-core.mdc.jinja +43 -0
  58. generators/static/cursor/rules/dart-conventions.mdc.jinja +14 -0
  59. generators/static/cursor/rules/domain-layer.mdc.jinja +24 -0
  60. generators/static/cursor/rules/login-context.mdc.jinja +16 -0
  61. generators/static/cursor/rules/presentation-layer.mdc.jinja +15 -0
  62. generators/static/cursor/rules/quality-gate.mdc.jinja +14 -0
  63. generators/static/cursor/rules/ui-caravaggio.mdc.jinja +22 -0
  64. generators/static/cursor/skills/caravaggio-ui/SKILL.md +138 -0
  65. generators/static/cursor/skills/epic-delivery/SKILL.md +74 -0
  66. generators/static/cursor/skills/epic-delivery/templates/epic-status.md +14 -0
  67. generators/static/cursor/skills/epic-delivery/templates/epic.md +36 -0
  68. generators/static/templates/apis/common/constants_template.jinja +48 -0
  69. generators/static/templates/apis/common/data_source_config_template.jinja +47 -0
  70. generators/static/templates/apis/core/api_injectable_module_template.jinja +14 -0
  71. generators/static/templates/apis/interceptors/api_logger_template.jinja +135 -0
  72. generators/static/templates/apis/interceptors/auth_interceptor_template.jinja +207 -0
  73. generators/static/templates/auth/application/auth_bloc_template.jinja +36 -0
  74. generators/static/templates/auth/application/auth_event_template.jinja +7 -0
  75. generators/static/templates/auth/application/auth_state_template.jinja +8 -0
  76. generators/static/templates/auth/infrastructure/auth_facade_module_template.jinja +16 -0
  77. generators/static/templates/auth/infrastructure/auth_facade_template.jinja +288 -0
  78. generators/static/templates/auth/infrastructure/mock_auth_facade_template.jinja +106 -0
  79. generators/static/templates/auth/model/auth_failure_template.jinja +11 -0
  80. generators/static/templates/auth/model/i_auth_facade_template.jinja +24 -0
  81. generators/static/templates/auth/model/user_template.jinja +34 -0
  82. generators/static/templates/auth/model/value_objects_template.jinja +54 -0
  83. generators/static/templates/auth/presentation/login_screen_template.jinja +24 -0
  84. generators/static/templates/auth/sign_in_form/application/sign_in_form_bloc_template.jinja +139 -0
  85. generators/static/templates/auth/sign_in_form/application/sign_in_form_event_template.jinja +15 -0
  86. generators/static/templates/auth/sign_in_form/application/sign_in_form_state_template.jinja +26 -0
  87. generators/static/templates/auth/sign_in_form/presentation/apple_form_template.jinja +16 -0
  88. generators/static/templates/auth/sign_in_form/presentation/email_password_form_template_INACTIVE.jinja +94 -0
  89. generators/static/templates/auth/sign_in_form/presentation/google_form_template.jinja +15 -0
  90. generators/static/templates/auth/sign_in_form/presentation/sign_in_form_template.jinja +381 -0
  91. generators/static/templates/component/component_bloc_template.jinja +49 -0
  92. generators/static/templates/component/component_event_template.jinja +7 -0
  93. generators/static/templates/component/component_form_bloc_template.jinja +56 -0
  94. generators/static/templates/component/component_form_event_template.jinja +7 -0
  95. generators/static/templates/component/component_form_state_template.jinja +18 -0
  96. generators/static/templates/component/component_form_widget_template.jinja +130 -0
  97. generators/static/templates/component/component_list_widget_template.jinja +95 -0
  98. generators/static/templates/component/component_state_template.jinja +9 -0
  99. generators/static/templates/component/component_widget_template.jinja +113 -0
  100. generators/static/templates/core/bloc/base_form_bloc_template.jinja +32 -0
  101. generators/static/templates/core/errors/error_localizer_template.jinja +61 -0
  102. generators/static/templates/core/infrastructure/base_mapper_template.jinja +21 -0
  103. generators/static/templates/core/infrastructure/base_repository_mixin_template.jinja +78 -0
  104. generators/static/templates/core/infrastructure/firebase_injectable_module_template.jinja +11 -0
  105. generators/static/templates/core/infrastructure/firestore_helpers_template.jinja +22 -0
  106. generators/static/templates/core/infrastructure/repository_error_handler_template.jinja +44 -0
  107. generators/static/templates/core/model/common_interfaces_template.jinja +3 -0
  108. generators/static/templates/core/model/entity_template.jinja +5 -0
  109. generators/static/templates/core/model/errors_template.jinja +15 -0
  110. generators/static/templates/core/model/failures_template.jinja +97 -0
  111. generators/static/templates/core/model/value_objects_template.jinja +135 -0
  112. generators/static/templates/core/model/value_validators_template.jinja +105 -0
  113. generators/static/templates/core/presentation/app_drawer_template.jinja +52 -0
  114. generators/static/templates/core/presentation/app_widget_template.jinja +41 -0
  115. generators/static/templates/core/presentation/bottom_nav_bar_template.jinja +32 -0
  116. generators/static/templates/domain/domain_mapper_template.jinja +39 -0
  117. generators/static/templates/domain/domain_remote_service_template.jinja +46 -0
  118. generators/static/templates/domain/domain_repository_template.jinja +90 -0
  119. generators/static/templates/domain/domain_service_module_template.jinja +23 -0
  120. generators/static/templates/domain/enum_template.jinja +3 -0
  121. generators/static/templates/domain/i_domain_service_template.jinja +18 -0
  122. generators/static/templates/domain/mock_domain_service_template.jinja +73 -0
  123. generators/static/templates/feature/feature_bloc_template.jinja +104 -0
  124. generators/static/templates/feature/feature_dto_template.jinja +26 -0
  125. generators/static/templates/feature/feature_entity_template.jinja +23 -0
  126. generators/static/templates/feature/feature_event_template.jinja +20 -0
  127. generators/static/templates/feature/feature_extensions_template.jinja +25 -0
  128. generators/static/templates/feature/feature_failure_template.jinja +11 -0
  129. generators/static/templates/feature/feature_page_template.jinja +113 -0
  130. generators/static/templates/feature/feature_repository_template.jinja +112 -0
  131. generators/static/templates/feature/feature_state_template.jinja +20 -0
  132. generators/static/templates/feature/i_feature_repository_template.jinja +21 -0
  133. generators/static/templates/feature/value_object_field_template.jinja +0 -0
  134. generators/static/templates/feature/value_object_template.jinja +0 -0
  135. generators/static/templates/feature/value_validators_template.jinja +57 -0
  136. generators/static/templates/home/home_screen_template.jinja +68 -0
  137. generators/static/templates/home/presentation/home_screen_template.jinja +52 -0
  138. generators/static/templates/home/screen_template.jinja +12 -0
  139. generators/static/templates/injection_template.jinja +8 -0
  140. generators/static/templates/logging/analytics_logging_template.jinja +42 -0
  141. generators/static/templates/logging/console_template.jinja +50 -0
  142. generators/static/templates/logging/logger_injectable_module_template.jinja +24 -0
  143. generators/static/templates/logging/logger_template.jinja +118 -0
  144. generators/static/templates/main_template.jinja +43 -0
  145. generators/static/templates/page_template.jinja +29 -0
  146. generators/static/templates/router_template.jinja +26 -0
  147. generators/static/templates/splash/presentation/splash_screen_auth_template.jinja +97 -0
  148. generators/static/templates/splash/presentation/splash_screen_template.jinja +51 -0
  149. generators/static/templates/storage/storage_repository_template.jinja +80 -0
  150. generators/static/templates/user_profile/infrastructure/user_profile_dto_template.jinja +27 -0
  151. generators/static/templates/user_profile/infrastructure/user_profile_mapper_template.jinja +43 -0
  152. generators/static/templates/user_profile/infrastructure/user_profile_repository_template.jinja +82 -0
  153. generators/static/templates/user_profile/infrastructure/user_profile_service_template.jinja +27 -0
  154. generators/static/templates/user_profile/model/i_user_profile_repository_template.jinja +34 -0
  155. generators/static/templates/user_profile/model/user_profile_failure_template.jinja +11 -0
  156. generators/static/templates/user_profile/model/user_profile_template.jinja +31 -0
  157. generators/static/templates/widgets/common/custom_scaffold_template.jinja +52 -0
  158. generators/static/templates/widgets/common/error_widget_template.jinja +22 -0
  159. generators/static/templates/widgets/common/loading_widget_template.jinja +14 -0
  160. generators/static/templates/widgets/common/unknown_state_widget_template.jinja +23 -0
  161. generators/templates/__init__.py +1 -0
  162. generators/templates/_core/core_generator.py +121 -0
  163. generators/templates/apis/apis_generator.py +50 -0
  164. generators/templates/auth/auth_generator.py +40 -0
  165. generators/templates/auth/sign_in_form_generator.py +22 -0
  166. generators/templates/copier.py +51 -0
  167. generators/templates/home/home_generator.py +26 -0
  168. generators/templates/lib/lib_generator.py +40 -0
  169. generators/templates/logging/logging_generator.py +28 -0
  170. generators/templates/main.py +75 -0
  171. generators/templates/splash/splash_generator.py +20 -0
  172. generators/templates/storage/storage_generator.py +5 -0
@@ -0,0 +1,12 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ class [[screen_name]]Screen extends StatelessWidget {
4
+ const [[screen_name]]Screen({super.key});
5
+
6
+ @override
7
+ Widget build(BuildContext context) {
8
+ return const Center(
9
+ child: Text('[[screen_title]] Page'),
10
+ );
11
+ }
12
+ }
@@ -0,0 +1,8 @@
1
+ import 'package:[[project_name]]/injection.config.dart';
2
+ import 'package:get_it/get_it.dart';
3
+ import 'package:injectable/injectable.dart';
4
+
5
+ final GetIt getIt = GetIt.instance;
6
+
7
+ @InjectableInit()
8
+ Future<void> configureDependencies(String env) async => getIt.init(environment: env);
@@ -0,0 +1,42 @@
1
+ /*
2
+ * Analytics logging integration for production monitoring
3
+ * Implements LoggingIntegration for external analytics services:
4
+ * - Placeholder for analytics service integration
5
+ * - Different handling per log level
6
+ * - Ready for services like Firebase Analytics, Crashlytics, etc.
7
+ *
8
+ * Customize the switch cases to integrate with your
9
+ * preferred analytics and crash reporting services
10
+ */
11
+
12
+ import 'package:[[project_name]]/logging/logger.dart';
13
+
14
+ class AnalyticsLoggingIntegration implements LoggingIntegration {
15
+ const AnalyticsLoggingIntegration();
16
+
17
+ {% raw %}
18
+ @override
19
+ void log(String message, LoggerLevel level, {dynamic ex, dynamic st}) async {
20
+ switch (level) {
21
+ case LoggerLevel.fatal:
22
+ // TODO: recordFatal($message, error: $ex, stack: $st);
23
+ break;
24
+ case LoggerLevel.error:
25
+ // TODO: recordError($message, error: $ex, stack: $st);
26
+ break;
27
+ case LoggerLevel.warning:
28
+ break;
29
+ case LoggerLevel.info:
30
+ break;
31
+ case LoggerLevel.verbose:
32
+ break;
33
+ case LoggerLevel.success:
34
+ break;
35
+ case LoggerLevel.apiReq:
36
+ break;
37
+ case LoggerLevel.apiRes:
38
+ break;
39
+ }
40
+ }
41
+ {% endraw %}
42
+ }
@@ -0,0 +1,50 @@
1
+ /*
2
+ * Console logging integration for development
3
+ * Implements LoggingIntegration for console output with:
4
+ * - Colored console output for different log levels
5
+ * - The [message] argument already includes prefix and body; for error/fatal the
6
+ * [Logger] appends exception and stack trace so one $message line is enough here.
7
+ *
8
+ * Perfect for development and debugging scenarios
9
+ * with color-coded messages for easy identification
10
+ */
11
+
12
+ import 'dart:developer' as developer;
13
+
14
+ import 'package:[[project_name]]/logging/logger.dart';
15
+
16
+ class ConsoleLoggingIntegration implements LoggingIntegration {
17
+ const ConsoleLoggingIntegration();
18
+
19
+ {% raw %}
20
+ @override
21
+ void log(String message, LoggerLevel level, {dynamic ex, dynamic st}) {
22
+ switch (level) {
23
+ case LoggerLevel.fatal:
24
+ developer.log('\x1B[31m$message\x1B[0m');
25
+ break;
26
+ case LoggerLevel.error:
27
+ developer.log('\x1B[91m$message\x1B[0m');
28
+ break;
29
+ case LoggerLevel.warning:
30
+ developer.log('\x1B[33m$message\x1B[0m');
31
+ break;
32
+ case LoggerLevel.info:
33
+ developer.log('\x1B[37m$message\x1B[0m');
34
+ break;
35
+ case LoggerLevel.verbose:
36
+ developer.log('\x1B[34m$message\x1B[0m');
37
+ break;
38
+ case LoggerLevel.success:
39
+ developer.log('\x1B[32m$message\x1B[0m');
40
+ break;
41
+ case LoggerLevel.apiReq:
42
+ developer.log('\x1B[93m$message\x1B[0m');
43
+ break;
44
+ case LoggerLevel.apiRes:
45
+ developer.log('\x1B[32m$message\x1B[0m');
46
+ break;
47
+ }
48
+ }
49
+ {% endraw %}
50
+ }
@@ -0,0 +1,24 @@
1
+ /*
2
+ * Logger dependency injection module
3
+ * Registers logging integrations with Injectable:
4
+ * - Analytics logging integration
5
+ * - Console logging integration
6
+ * - Configurable list of logging backends
7
+ *
8
+ * This module makes logging integrations available
9
+ * throughout the app via dependency injection
10
+ */
11
+
12
+ import 'package:injectable/injectable.dart';
13
+ import 'package:[[project_name]]/logging/analytics_logging.dart';
14
+ import 'package:[[project_name]]/logging/console.dart';
15
+ import 'package:[[project_name]]/logging/logger.dart';
16
+
17
+ @module
18
+ abstract class RegisterModule {
19
+ @lazySingleton
20
+ List<LoggingIntegration> get loggingIntegrations => <LoggingIntegration>[
21
+ const AnalyticsLoggingIntegration(),
22
+ const ConsoleLoggingIntegration(),
23
+ ];
24
+ }
@@ -0,0 +1,118 @@
1
+ /*
2
+ * Centralized logger template for application-wide logging
3
+ * Provides structured logging with:
4
+ * - Multiple log levels (verbose, info, warning, error, etc.)
5
+ * - Pluggable logging integrations (console, analytics, etc.)
6
+ * - Dependency injection support
7
+ * - Consistent logging interface across the app
8
+ *
9
+ * For error/fatal, the text passed to integrations includes $message plus
10
+ * optional lines for $ex and $st when provided.
11
+ */
12
+
13
+ import 'package:injectable/injectable.dart';
14
+
15
+ enum LoggerLevel {
16
+ verbose,
17
+ info,
18
+ warning,
19
+ error,
20
+ fatal,
21
+ success,
22
+ apiReq,
23
+ apiRes,
24
+ }
25
+
26
+ abstract class LoggingIntegration {
27
+ void log(String message, LoggerLevel level, {dynamic ex, dynamic st});
28
+ }
29
+
30
+ @injectable
31
+ class Logger {
32
+ Logger(this._integrations);
33
+
34
+ final List<LoggingIntegration> _integrations;
35
+
36
+ void logVerbose(String message) {
37
+ _log(message, LoggerLevel.verbose);
38
+ }
39
+
40
+ void logInfo(String message) {
41
+ _log(message, LoggerLevel.info);
42
+ }
43
+
44
+ void logWarning(String message) {
45
+ _log(message, LoggerLevel.warning);
46
+ }
47
+
48
+ void logSuccess(String message) {
49
+ _log(message, LoggerLevel.success);
50
+ }
51
+
52
+ void logError(String message, {dynamic ex, dynamic st}) {
53
+ _log(message, LoggerLevel.error, ex: ex, st: st);
54
+ }
55
+
56
+ void logFatal(String message, {dynamic ex, dynamic st}) {
57
+ _log(message, LoggerLevel.fatal, ex: ex, st: st);
58
+ }
59
+
60
+ void logApiReq(String message) {
61
+ _log(message, LoggerLevel.apiReq);
62
+ }
63
+
64
+ void logApiRes(String message) {
65
+ _log(message, LoggerLevel.apiRes);
66
+ }
67
+
68
+ {% raw %}
69
+ void _log(String message, LoggerLevel level, {dynamic ex, dynamic st}) {
70
+ final StringBuffer buffer = StringBuffer();
71
+ final String prefix = _getLogPrefix(level);
72
+ if (prefix.isNotEmpty) {
73
+ buffer.write(prefix);
74
+ buffer.write(' ');
75
+ }
76
+ buffer.write(message);
77
+
78
+ if (level == LoggerLevel.error || level == LoggerLevel.fatal) {
79
+ if (ex != null) {
80
+ buffer.writeln();
81
+ buffer.write('Exception: $ex');
82
+ }
83
+ if (st != null) {
84
+ buffer.writeln();
85
+ buffer.write('StackTrace: $st');
86
+ }
87
+ }
88
+
89
+ final String finalMessage = buffer.toString();
90
+
91
+ for (final LoggingIntegration integration in _integrations) {
92
+ integration.log(finalMessage, level, ex: ex, st: st);
93
+ }
94
+ }
95
+ {% endraw %}
96
+
97
+ // Returns the log prefix for the given LoggingLevel
98
+ String _getLogPrefix(LoggerLevel level) {
99
+ switch (level) {
100
+ case LoggerLevel.verbose:
101
+ return "[Verbose]";
102
+ case LoggerLevel.info:
103
+ return "[Info]";
104
+ case LoggerLevel.warning:
105
+ return "[Warning]";
106
+ case LoggerLevel.error:
107
+ return "[Error]";
108
+ case LoggerLevel.fatal:
109
+ return "[Fatal error]";
110
+ case LoggerLevel.success:
111
+ return "[Done]";
112
+ case LoggerLevel.apiReq:
113
+ return "[API Request]";
114
+ case LoggerLevel.apiRes:
115
+ return "[API Response]";
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,43 @@
1
+ import 'package:caravaggio_ui/caravaggio_ui.dart';
2
+ import 'package:flutter/material.dart';
3
+ import 'package:injectable/injectable.dart';
4
+ import 'package:intl/date_symbol_data_local.dart';
5
+ import 'package:[[project_name]]/apis/common/constants.dart';
6
+ import 'package:[[project_name]]/apis/common/data_source_config.dart';
7
+ import 'package:[[project_name]]/injection.dart';
8
+ import 'package:[[project_name]]/core/presentation/app_widget.dart';
9
+
10
+ Future<void> main() async {
11
+ CaravaggioUI.initialize(
12
+ primaryColor: const [[primary_color]], // From coregen.yaml styling.primary_color
13
+ secondaryColor: const [[secondary_color]] // From coregen.yaml styling.secondary_color
14
+ // fontFamily: 'Noto Sans', // Uncomment and set your desired font family
15
+ );
16
+
17
+ WidgetsFlutterBinding.ensureInitialized();
18
+ await initializeDateFormatting('en_US');
19
+
20
+ initEnvironment();
21
+ validateDataSources();
22
+
23
+ await configureDependencies(Environment.dev);
24
+
25
+ runApp(const AppWidget());
26
+ }
27
+
28
+ void initEnvironment() {
29
+ // Mock-only default: leave [Constants.apiUrl] empty — no backend required.
30
+ // Set a real URL only when switching entities to DataSource.remote in
31
+ // lib/apis/common/data_source_config.dart.
32
+ Constants.apiUrl = '';
33
+
34
+ // Optional per-environment overrides (uncomment when API is ready):
35
+ // const String environment = String.fromEnvironment('ENV', defaultValue: 'local');
36
+ // if (environment.toLowerCase() == 'production') {
37
+ // Constants.apiUrl = 'https://api.example.com';
38
+ // }
39
+ }
40
+
41
+ void validateDataSources() {
42
+ DataSourceConfig.validateRemoteRequiresApiUrl();
43
+ }
@@ -0,0 +1,29 @@
1
+ /*
2
+ * Simple page template for Flutter screens
3
+ * Generates a basic StatelessWidget page with:
4
+ * - CustomScaffold with page title
5
+ * - Centered content placeholder
6
+ * - Route name constant for navigation
7
+ * Use this template for creating simple, static pages
8
+ */
9
+
10
+ import 'package:caravaggio_ui/caravaggio_ui.dart';
11
+ import 'package:flutter/material.dart';
12
+ import 'package:[[project_name]]/widgets/common/custom_scaffold.dart';
13
+
14
+ class [[page_name|title]]Page extends StatelessWidget {
15
+ static const String routeName = '/[[page_name]]';
16
+
17
+ const [[page_name|title]]Page({super.key});
18
+
19
+ @override
20
+ Widget build(BuildContext context) {
21
+ return CustomScaffold(
22
+ title: CText.title('[[page_name|replace("_", " ")|title]]', size: TextSize.small),
23
+ showBackButton: true,
24
+ body: Center(
25
+ child: CText.body('[[page_name|replace("_", " ")|title]] Page'),
26
+ ),
27
+ );
28
+ }
29
+ }
@@ -0,0 +1,26 @@
1
+ {% if has_login %}
2
+ import 'package:[[project_name]]/features/auth/presentation/login_page.dart';
3
+ {% endif %}
4
+ import 'package:[[project_name]]/features/home/home_page.dart';
5
+ import 'package:flutter/material.dart';
6
+ import 'package:[[project_name]]/features/splash/splash_page.dart';
7
+ import 'package:go_router/go_router.dart';
8
+
9
+ final GoRouter router = GoRouter(
10
+ routes: <RouteBase>[
11
+ {% if has_login %}
12
+ GoRoute(
13
+ path: LoginPage.routeName,
14
+ builder: (BuildContext context, GoRouterState state) => const LoginPage(),
15
+ ),
16
+ {% endif %}
17
+ GoRoute(
18
+ path: HomePage.routeName,
19
+ builder: (BuildContext context, GoRouterState state) => const HomePage(),
20
+ ),
21
+ GoRoute(
22
+ path: SplashPage.routeName,
23
+ builder: (BuildContext context, GoRouterState state) => const SplashPage(),
24
+ ),
25
+ ],
26
+ );
@@ -0,0 +1,97 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:flutter_bloc/flutter_bloc.dart';
3
+ import 'package:[[project_name]]/features/auth/application/auth_bloc.dart';
4
+ import 'package:[[project_name]]/features/auth/presentation/login_page.dart';
5
+ import 'package:[[project_name]]/features/home/home_page.dart';
6
+ import 'package:[[project_name]]/widgets/common/custom_scaffold.dart';
7
+ import 'package:go_router/go_router.dart';
8
+
9
+ class SplashPage extends StatefulWidget {
10
+ static const String routeName = '/';
11
+
12
+ const SplashPage({super.key});
13
+
14
+ @override
15
+ State<SplashPage> createState() => _SplashPageState();
16
+ }
17
+
18
+ class _SplashPageState extends State<SplashPage> {
19
+ static const Duration _splashMinDuration = Duration(seconds: 1);
20
+
21
+ bool _minDelayElapsed = false;
22
+ bool _hasNavigated = false;
23
+ AuthState? _resolvedAuthState;
24
+ int _animation = 0;
25
+
26
+ @override
27
+ void initState() {
28
+ super.initState();
29
+ WidgetsBinding.instance.addPostFrameCallback((_) {
30
+ setState(() {
31
+ _animation = 1;
32
+ });
33
+ });
34
+ Future<void>.delayed(_splashMinDuration, () {
35
+ if (!mounted) {
36
+ return;
37
+ }
38
+ setState(() {
39
+ _minDelayElapsed = true;
40
+ });
41
+ _tryNavigate();
42
+ });
43
+ }
44
+
45
+ void _onAuthStateChanged(AuthState state) {
46
+ if (_hasNavigated) {
47
+ return;
48
+ }
49
+ if (state is Authenticated || state is Unauthenticated) {
50
+ _resolvedAuthState = state;
51
+ _tryNavigate();
52
+ }
53
+ }
54
+
55
+ void _tryNavigate() {
56
+ if (_hasNavigated || _resolvedAuthState == null) {
57
+ return;
58
+ }
59
+
60
+ final AuthState state = _resolvedAuthState!;
61
+
62
+ if (state is Authenticated) {
63
+ _hasNavigated = true;
64
+ context.replace(HomePage.routeName);
65
+ return;
66
+ }
67
+
68
+ if (state is Unauthenticated && _minDelayElapsed) {
69
+ _hasNavigated = true;
70
+ context.pushReplacement(LoginPage.routeName);
71
+ }
72
+ }
73
+
74
+ @override
75
+ Widget build(BuildContext context) {
76
+ return BlocListener<AuthBloc, AuthState>(
77
+ listener: (BuildContext context, AuthState state) => _onAuthStateChanged(state),
78
+ child: CustomScaffold(
79
+ showBackButton: false,
80
+ body: Center(
81
+ child: Hero(
82
+ tag: LoginPage.logoHeroTag,
83
+ child: Material(
84
+ type: MaterialType.transparency,
85
+ child: AnimatedContainer(
86
+ duration: const Duration(seconds: 1),
87
+ curve: Curves.easeInOut,
88
+ width: _animation == 1 ? 200 : 100,
89
+ child: Image.asset('assets/logo.png'),
90
+ ),
91
+ ),
92
+ ),
93
+ ),
94
+ ),
95
+ );
96
+ }
97
+ }
@@ -0,0 +1,51 @@
1
+ /*
2
+ * Splash screen template for app initialization
3
+ * Creates an initial loading screen that:
4
+ * - Shows app logo or branding
5
+ * - Performs initialization tasks
6
+ * - Automatically navigates to home screen after delay
7
+ * - Provides smooth app startup experience
8
+ *
9
+ * Perfect for app startup, loading assets, or initialization processes
10
+ */
11
+
12
+ import 'package:flutter/material.dart';
13
+ import 'package:go_router/go_router.dart';
14
+ import 'package:[[project_name]]/features/home/home_page.dart';
15
+ import 'package:[[project_name]]/widgets/common/custom_scaffold.dart';
16
+
17
+ class SplashPage extends StatefulWidget {
18
+ static const String routeName = '/';
19
+
20
+ const SplashPage({super.key});
21
+
22
+ @override
23
+ State<SplashPage> createState() => _SplashPageState();
24
+ }
25
+
26
+ class _SplashPageState extends State<SplashPage> {
27
+ @override
28
+ void initState() {
29
+ super.initState();
30
+ WidgetsBinding.instance.addPostFrameCallback((_) {
31
+ // After 500 milliseconds, navigate to the next screen
32
+ Future<void>.delayed(const Duration(milliseconds: 500), () {
33
+ // Navigate to the next screen or perform any action here
34
+ context.replace(HomePage.routeName);
35
+ });
36
+ });
37
+ }
38
+
39
+ @override
40
+ Widget build(BuildContext context) {
41
+ return CustomScaffold(
42
+ showBackButton: false,
43
+ body: Center(
44
+ child: SizedBox(
45
+ width: 200,
46
+ child: Image.asset('assets/logo.png'),
47
+ ),
48
+ ),
49
+ );
50
+ }
51
+ }
@@ -0,0 +1,80 @@
1
+ import 'package:injectable/injectable.dart';
2
+ import 'package:shared_preferences/shared_preferences.dart';
3
+
4
+ const String tokenDataKey = 'token_key';
5
+ const String refreshTokenDataKey = 'refresh_token_key';
6
+ const String guestTokenKey = 'guest_token_key';
7
+ const String isGuestModeKey = 'is_guest_mode_key';
8
+
9
+ /// Access + optional refresh token bundle (used by [AuthInterceptor] after refresh).
10
+ class TokenData {
11
+ const TokenData({required this.token, this.refreshToken = ''});
12
+
13
+ final String token;
14
+ final String refreshToken;
15
+
16
+ bool get hasRefresh => refreshToken.isNotEmpty;
17
+ }
18
+
19
+ @Singleton()
20
+ class StorageRepository {
21
+ Future<bool> hasToken() async {
22
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
23
+ final String? token = prefs.getString(tokenDataKey);
24
+ return token != null && token.isNotEmpty;
25
+ }
26
+
27
+ /// Persists access token and optional refresh token (SharedPreferences — consider flutter_secure_storage for production).
28
+ Future<void> saveToken(TokenData data) async {
29
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
30
+ await prefs.setString(tokenDataKey, data.token);
31
+ await prefs.setString(refreshTokenDataKey, data.refreshToken);
32
+ }
33
+
34
+ Future<String?> getToken() async {
35
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
36
+ return prefs.getString(tokenDataKey);
37
+ }
38
+
39
+ Future<String?> getRefreshToken() async {
40
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
41
+ return prefs.getString(refreshTokenDataKey);
42
+ }
43
+
44
+ Future<void> removeToken() async {
45
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
46
+ await prefs.remove(tokenDataKey);
47
+ await prefs.remove(refreshTokenDataKey);
48
+ }
49
+
50
+ Future<void> saveGuestToken(String guestToken) async {
51
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
52
+ await prefs.setString(guestTokenKey, guestToken);
53
+ await prefs.setBool(isGuestModeKey, true);
54
+ await prefs.remove(tokenDataKey);
55
+ await prefs.remove(refreshTokenDataKey);
56
+ }
57
+
58
+ Future<String?> getGuestToken() async {
59
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
60
+ return prefs.getString(guestTokenKey);
61
+ }
62
+
63
+ Future<bool> isGuestMode() async {
64
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
65
+ return prefs.getBool(isGuestModeKey) ?? false;
66
+ }
67
+
68
+ Future<void> clearGuestToken() async {
69
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
70
+ await prefs.remove(guestTokenKey);
71
+ await prefs.remove(isGuestModeKey);
72
+ }
73
+
74
+ Future<void> clearAll() async {
75
+ await removeToken();
76
+ await clearGuestToken();
77
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
78
+ await prefs.clear();
79
+ }
80
+ }
@@ -0,0 +1,27 @@
1
+ /*
2
+ * UserProfile DTO (Data Transfer Object)
3
+ * Used for API communication when syncing UserProfile with backend
4
+ *
5
+ * DTOs represent the data structure expected by the API
6
+ * and are mapped to/from domain entities via UserProfileMapper
7
+ */
8
+
9
+ import 'package:freezed_annotation/freezed_annotation.dart';
10
+
11
+ part 'user_profile_dto.freezed.dart';
12
+ part 'user_profile_dto.g.dart';
13
+
14
+ @freezed
15
+ abstract class UserProfileDto with _$UserProfileDto {
16
+ const factory UserProfileDto({
17
+ required String id,
18
+ required String name,
19
+ required String emailAddress,
20
+ @JsonKey(name: 'avatar_url') String? avatarUrl,
21
+ @JsonKey(name: 'created_at') required String createdAt,
22
+ @JsonKey(name: 'updated_at') required String updatedAt,
23
+ }) = _UserProfileDto;
24
+
25
+ factory UserProfileDto.fromJson(Map<String, dynamic> json) =>
26
+ _$UserProfileDtoFromJson(json);
27
+ }
@@ -0,0 +1,43 @@
1
+ /*
2
+ * UserProfile mapper
3
+ * Maps between UserProfile DTO (API layer) and UserProfile Domain Entity
4
+ *
5
+ * Extends BaseMapper which provides default toDomainList/toDtoList implementations.
6
+ */
7
+
8
+ import 'package:dartz/dartz.dart';
9
+ import 'package:injectable/injectable.dart';
10
+ import 'package:[[project_name]]/core/infrastructure/base_mapper.dart';
11
+ import 'package:[[project_name]]/core/model/value_objects.dart';
12
+ import 'package:[[project_name]]/domain/auth/model/value_objects.dart';
13
+ import 'package:[[project_name]]/domain/user_profile/infrastructure/user_profile_dto.dart';
14
+ import 'package:[[project_name]]/domain/user_profile/model/user_profile.dart';
15
+
16
+ @lazySingleton
17
+ class UserProfileMapper extends BaseMapper<UserProfile, UserProfileDto> {
18
+ UserProfileMapper();
19
+
20
+ @override
21
+ UserProfile toDomain(UserProfileDto dto) {
22
+ return UserProfile(
23
+ id: UniqueId.fromUniqueString(dto.id),
24
+ name: StringSingleLine(dto.name),
25
+ emailAddress: EmailAddress(dto.emailAddress),
26
+ avatarUrl: dto.avatarUrl != null ? some(dto.avatarUrl!) : none(),
27
+ createdAt: DateTime.parse(dto.createdAt),
28
+ updatedAt: DateTime.parse(dto.updatedAt),
29
+ );
30
+ }
31
+
32
+ @override
33
+ UserProfileDto toDto(UserProfile entity) {
34
+ return UserProfileDto(
35
+ id: entity.id.getOrCrash(),
36
+ name: entity.name.getOrCrash(),
37
+ emailAddress: entity.emailAddress.getOrCrash(),
38
+ avatarUrl: entity.avatarUrl.fold(() => null, (String url) => url),
39
+ createdAt: entity.createdAt.toIso8601String(),
40
+ updatedAt: entity.updatedAt.toIso8601String(),
41
+ );
42
+ }
43
+ }