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,135 @@
1
+ /*
2
+ * Core value objects for domain-driven design
3
+ * Provides base classes for type-safe value objects:
4
+ * - ValueObject<T>: Abstract base for all value objects
5
+ * - Concrete implementations for common types (strings, emails, etc.)
6
+ * - Validation integration with Either types
7
+ * - Immutability and equality built-in
8
+ *
9
+ * Value objects ensure data integrity and type safety
10
+ * throughout the domain layer by encapsulating validation logic
11
+ */
12
+
13
+ import 'package:dartz/dartz.dart';
14
+ import 'package:meta/meta.dart';
15
+ import 'package:uuid/uuid.dart';
16
+ import 'package:[[project_name]]/core/model/common_interfaces.dart';
17
+ import 'package:[[project_name]]/core/model/errors.dart';
18
+ import 'package:[[project_name]]/core/model/failures.dart';
19
+ import 'package:[[project_name]]/core/model/value_validators.dart';
20
+
21
+ // Used for attributes within the domain. Each attribute is a ValueObject<T>
22
+ // Example:
23
+ //
24
+ // Note({required Description description})
25
+ //
26
+ // Description extends a ValueObject<String> and has a validator that ensures, for example, that the string is not empty
27
+ //
28
+ // NOTE: All domain ValueObjects extend ValueObject<T>
29
+ // Each domain ValueObject has a validator specified in core/value_validators.dart
30
+ // Validators return an Either<ValueFailure<T>, T> representing the result of the validation
31
+ // If validation fails, a ValueFailure<T> is returned with the reason for the failure
32
+ // If validation succeeds, the valid value of type T is returned
33
+ //
34
+ // The Failures defined in the domain folder (e.g. note_failure.dart) are instead used to represent specific errors
35
+ // that can occur during the execution of operations in the domain (e.g. error while saving a note)
36
+ // These Failures are used to handle errors at the application level and not for data validation
37
+
38
+ @immutable
39
+ abstract class ValueObject<T> implements IValidatable {
40
+ const ValueObject();
41
+ Either<ValueFailure<T>, T> get value;
42
+
43
+ /// Throws [UnexpectedValueError] containing the [ValueFailure]
44
+ T getOrCrash() {
45
+ // id = identity - same as writing (right) => right
46
+ return value.fold((ValueFailure<T> f) => throw UnexpectedValueError(f), id);
47
+ }
48
+
49
+ T getOrElse(T dflt) {
50
+ return value.getOrElse(() => dflt);
51
+ }
52
+
53
+ /// Returns the [ValueFailure] if the value is invalid, otherwise returns [Unit]
54
+ ///
55
+ /// Example:
56
+ ///
57
+ /// extension NoteX on Note {
58
+ /// Option<ValueFailure<dynamic>> get failureOption {
59
+ /// return description.failureOrUnit
60
+ /// .andThen(color.failureOrUnit)
61
+ /// .andThen(todos.failureOrUnit)
62
+ /// .andThen(
63
+ /// todos
64
+ /// .getOrCrash()
65
+ /// .map((todoItem) => todoItem.failureOption)
66
+ /// .filter((o) => o.isSome())
67
+ /// .getOrElse(0, (_) => none())
68
+ /// .fold(() => right(unit), (f) => left(f)),
69
+ /// )
70
+ /// .fold((f) => some(f), (_) => none());
71
+ /// }
72
+ /// }
73
+ ///
74
+ /// Usage in a Widget:
75
+ /// if (note.failureOption.isSome()) {
76
+ /// return ErrorNoteCard(note: note);
77
+ /// }
78
+ Either<ValueFailure<dynamic>, Unit> get failureOrUnit {
79
+ return value.fold(
80
+ (ValueFailure<T> l) => left(l),
81
+ (T r) => right(unit),
82
+ );
83
+ }
84
+
85
+ @override
86
+ bool isValid() {
87
+ return value.isRight();
88
+ }
89
+
90
+ @override
91
+ bool operator ==(Object o) {
92
+ if (identical(this, o)) return true;
93
+ return o is ValueObject<T> && o.value == value;
94
+ }
95
+
96
+ @override
97
+ int get hashCode => value.hashCode;
98
+
99
+ @override
100
+ String toString() => 'Value([[value]])';
101
+ }
102
+
103
+ class UniqueId extends ValueObject<String> {
104
+ @override
105
+ final Either<ValueFailure<String>, String> value;
106
+
107
+ // We cannot let a simple String be passed in. This would allow for possible non-unique IDs.
108
+ factory UniqueId() {
109
+ return UniqueId._(
110
+ right(const Uuid().v1()),
111
+ );
112
+ }
113
+
114
+ /// Used with strings we trust are unique, such as database IDs.
115
+ factory UniqueId.fromUniqueString(String uniqueIdStr) {
116
+ return UniqueId._(
117
+ right(uniqueIdStr),
118
+ );
119
+ }
120
+
121
+ const UniqueId._(this.value);
122
+ }
123
+
124
+ class StringSingleLine extends ValueObject<String> {
125
+ @override
126
+ final Either<ValueFailure<String>, String> value;
127
+
128
+ factory StringSingleLine(String input) {
129
+ return StringSingleLine._(
130
+ validateSingleLine(input),
131
+ );
132
+ }
133
+
134
+ const StringSingleLine._(this.value);
135
+ }
@@ -0,0 +1,105 @@
1
+ import 'package:dartz/dartz.dart';
2
+ import 'package:[[project_name]]/core/model/failures.dart';
3
+
4
+ // Used to validate ValueObjects within the domain
5
+ // Example:
6
+ //
7
+ // Note({required Description description})
8
+ //
9
+ // Description extends a ValueObject<String> and has a validator that ensures, for example, that the string is not empty
10
+ //
11
+ //
12
+ // class Description extends ValueObject<String> {
13
+ // @override
14
+ // final Either<ValueFailure<String>, String> value;
15
+ // static const maxLength = 1000;
16
+ // factory Description(String input) {
17
+ // assert(input != null);
18
+ // return Description._(
19
+ // validateMaxStringLength(input, maxLength).flatMap(validateStringNotEmpty),
20
+ // );
21
+ // }
22
+ // const Description._(this.value);
23
+ // }
24
+ //
25
+ //
26
+ // NOTE: All domain ValueObjects extend ValueObject<T>
27
+ // Each domain ValueObject has a validator specified in core/value_validators.dart
28
+ // Validators return an Either<ValueFailure<T>, T> representing the result of the validation
29
+ // If validation fails, a ValueFailure<T> is returned with the reason for the failure
30
+ // If validation succeeds, the valid value of type T is returned
31
+ //
32
+ // The Failures defined in the domain folder (e.g. note_failure.dart) are instead used to represent specific errors
33
+ // that can occur during the execution of operations in the domain (e.g. error while saving a note)
34
+ // These Failures are used to handle errors at the application level and not for data validation
35
+
36
+ Either<ValueFailure<String>, String> validateMaxStringLength(
37
+ String input,
38
+ int maxLength,
39
+ ) {
40
+ if (input.length <= maxLength) {
41
+ return right(input);
42
+ } else {
43
+ return left(ValueFailure<String>.exceedingLength(
44
+ failedValue: input,
45
+ max: maxLength,
46
+ ));
47
+ }
48
+ }
49
+
50
+ Either<ValueFailure<String>, String> validateStringNotEmpty(String input) {
51
+ if (input.isEmpty) {
52
+ return left(ValueFailure<String>.empty(failedValue: input));
53
+ } else {
54
+ return right(input);
55
+ }
56
+ }
57
+
58
+ Either<ValueFailure<String>, String> validateSingleLine(String input) {
59
+ if (input.contains('\n')) {
60
+ return left(ValueFailure<String>.multiline(failedValue: input));
61
+ } else {
62
+ return right(input);
63
+ }
64
+ }
65
+
66
+ Either<ValueFailure<List<T>>, List<T>> validateMaxListLength<T>(List<T> input, int maxLength) {
67
+ if (input.length <= maxLength) {
68
+ return right(input);
69
+ } else {
70
+ return left(ValueFailure<List<T>>.listTooLong(
71
+ failedValue: input,
72
+ max: maxLength,
73
+ ));
74
+ }
75
+ }
76
+
77
+ Either<ValueFailure<String>, String> validateEmailAddress(String input) {
78
+ // Maybe not the most robust way of email validation but it__STRING_3__*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+__STRING_4__";
79
+ const String emailRegex = r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$";
80
+ if (RegExp(emailRegex).hasMatch(input)) {
81
+ return right(input);
82
+ } else {
83
+ return left(ValueFailure<String>.invalidEmail(failedValue: input));
84
+ }
85
+ }
86
+
87
+ Either<ValueFailure<String>, String> validatePassword(String input) {
88
+ // You can also add some advanced password checks (uppercase/lowercase, at least 1 number, ...)
89
+ if (input.length >= 6) {
90
+ return right(input);
91
+ } else {
92
+ return left(ValueFailure<String>.shortPassword(failedValue: input));
93
+ }
94
+ }
95
+
96
+ Either<ValueFailure<String>, String> validateConfirmPassword(
97
+ String password,
98
+ String confirmPassword,
99
+ ) {
100
+ if (password == confirmPassword) {
101
+ return right(confirmPassword);
102
+ } else {
103
+ return left(ValueFailure<String>.passwordsDoNotMatch(failedValue: confirmPassword));
104
+ }
105
+ }
@@ -0,0 +1,52 @@
1
+ import 'package:caravaggio_ui/caravaggio_ui.dart';
2
+ import 'package:flutter/material.dart';
3
+ import 'package:go_router/go_router.dart';
4
+ import 'package:[[project_name]]/features/home/home_page.dart';
5
+ {% for drawer_item in drawer_items %}
6
+ import 'package:[[project_name]]/[[drawer_item.name]]/presentation/[[drawer_item.name]]_page.dart';
7
+ {% endfor %}
8
+
9
+ class AppDrawer extends StatelessWidget {
10
+ const AppDrawer({super.key});
11
+
12
+ @override
13
+ Widget build(BuildContext context) {
14
+ return Drawer(
15
+ child: ListView(
16
+ padding: EdgeInsets.zero,
17
+ children: <Widget>[
18
+ const DrawerHeader(
19
+ decoration: BoxDecoration(
20
+ color: Colors.blue,
21
+ ),
22
+ child: Text(
23
+ 'App Navigation',
24
+ style: TextStyle(
25
+ color: Colors.white,
26
+ fontSize: 24,
27
+ ),
28
+ ),
29
+ ),
30
+ CTile.simple(
31
+ title: 'Home',
32
+ icon: Icons.home,
33
+ onTap: () {
34
+ Navigator.of(context).pop();
35
+ context.go(HomePage.routeName);
36
+ },
37
+ ),
38
+ {% for drawer_item in drawer_items %}
39
+ CTile.simple(
40
+ title: '[[drawer_item.name|replace("_", " ")|title]]',
41
+ icon: Icons.star,
42
+ onTap: () {
43
+ Navigator.of(context).pop();
44
+ context.go([[drawer_item.name|title]]Page.routeName);
45
+ },
46
+ ),
47
+ {% endfor %}
48
+ ],
49
+ ),
50
+ );
51
+ }
52
+ }
@@ -0,0 +1,41 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:caravaggio_ui/caravaggio_ui.dart';
3
+ import 'package:flutter_bloc/flutter_bloc.dart';
4
+ import 'package:provider/single_child_widget.dart' show SingleChildWidget;
5
+ {% if has_login %}
6
+ import 'package:[[project_name]]/features/auth/application/auth_bloc.dart';
7
+ import 'package:[[project_name]]/injection.dart';
8
+ import 'package:[[project_name]]/domain/auth/model/i_auth_facade.dart';
9
+ {% endif %}
10
+ import 'package:[[project_name]]/router.dart';
11
+
12
+ class AppWidget extends StatelessWidget {
13
+ const AppWidget({super.key});
14
+
15
+ @override
16
+ Widget build(BuildContext context) {
17
+ return MultiBlocProvider(
18
+ providers: <SingleChildWidget>[
19
+ {% if has_login %}
20
+ BlocProvider<AuthBloc>(create: (BuildContext context) => AuthBloc(getIt<IAuthFacade>())..add(const AuthCheckRequested())),
21
+ {% endif %}
22
+ // Add blocs here if needed
23
+ ],
24
+ child: const _App(),
25
+ );
26
+ }
27
+ }
28
+
29
+ class _App extends StatelessWidget {
30
+ const _App();
31
+
32
+ @override
33
+ Widget build(BuildContext context) {
34
+ return MaterialApp.router(
35
+ title: '[[project_name]]',
36
+ theme: CUI.themeData,
37
+ routerConfig: router,
38
+ );
39
+ }
40
+ }
41
+
@@ -0,0 +1,32 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ class BottomNavBar extends StatelessWidget {
4
+ final int currentIndex;
5
+ final Function(int) onTap;
6
+
7
+ const BottomNavBar({
8
+ super.key,
9
+ required this.currentIndex,
10
+ required this.onTap,
11
+ });
12
+
13
+ @override
14
+ Widget build(BuildContext context) {
15
+ return BottomNavigationBar(
16
+ currentIndex: currentIndex,
17
+ onTap: onTap,
18
+ items: const [
19
+ BottomNavigationBarItem(
20
+ icon: Icon(Icons.home),
21
+ label: 'Home',
22
+ ),
23
+ {% for nav_item in nav_items %}
24
+ BottomNavigationBarItem(
25
+ icon: Icon(Icons.star),
26
+ label: '[[nav_item.name|replace("_", " ")|title]]',
27
+ ),
28
+ {% endfor %}
29
+ ],
30
+ );
31
+ }
32
+ }
@@ -0,0 +1,39 @@
1
+ /*
2
+ * Mapper template for DTO-Domain conversions
3
+ * Maps between DTO (API layer) and Domain Entity (business layer)
4
+ *
5
+ * This mapper ensures proper conversion between:
6
+ * - DTO (from/to API)
7
+ * - Domain Entity (business logic)
8
+ *
9
+ * Extends BaseMapper which provides default toDomainList/toDtoList implementations.
10
+ */
11
+
12
+ import 'package:injectable/injectable.dart';
13
+ import 'package:[[project_name]]/core/model/value_objects.dart';
14
+ import 'package:[[project_name]]/core/infrastructure/base_mapper.dart';
15
+ import 'package:[[project_name]]/[[import_prefix]]/model/[[file_name]].dart';
16
+ import 'package:[[project_name]]/[[import_prefix]]/model/value_objects.dart';
17
+ import 'package:[[project_name]]/[[import_prefix]]/infrastructure/[[file_name]]_dto.dart';
18
+ [[mapper_imports]]
19
+
20
+ @lazySingleton
21
+ class [[entity_name]]Mapper extends BaseMapper<[[entity_name]], [[entity_name]]Dto> {
22
+ [[mapper_fields]]
23
+
24
+ [[entity_name]]Mapper([[mapper_constructor_params]]);
25
+
26
+ @override
27
+ [[entity_name]] toDomain([[entity_name]]Dto dto) {
28
+ return [[entity_name]](
29
+ [[from_dto_fields]]
30
+ );
31
+ }
32
+
33
+ @override
34
+ [[entity_name]]Dto toDto([[entity_name]] entity) {
35
+ return [[entity_name]]Dto(
36
+ [[to_dto_fields]]
37
+ );
38
+ }
39
+ }
@@ -0,0 +1,46 @@
1
+ /*
2
+ * Remote [[entity_name]] service — Retrofit REST client.
3
+ */
4
+
5
+ import 'package:dio/dio.dart';
6
+ import 'package:injectable/injectable.dart';
7
+ import 'package:retrofit/retrofit.dart';
8
+ import 'package:[[project_name]]/apis/common/constants.dart';
9
+ import 'package:[[project_name]]/[[import_prefix]]/infrastructure/[[file_name]]_dto.dart';
10
+ import 'package:[[project_name]]/[[import_prefix]]/infrastructure/i_[[file_name]]_service.dart';
11
+
12
+ part '[[file_name]]_remote_service.g.dart';
13
+
14
+ @module
15
+ abstract class [[entity_name]]RemoteModule {
16
+ @lazySingleton
17
+ [[entity_name]]RemoteService [[entity_name_camel]]RemoteService(Dio dio) {
18
+ return [[entity_name]]RemoteService(dio, baseUrl: Constants.dioBaseUrl);
19
+ }
20
+ }
21
+
22
+ @RestApi()
23
+ abstract class [[entity_name]]RemoteService implements I[[entity_name]]Service {
24
+ @factoryMethod
25
+ factory [[entity_name]]RemoteService(Dio dio, {String baseUrl}) = _[[entity_name]]RemoteService;
26
+
27
+ @override
28
+ @GET('/[[kebab_name]]s')
29
+ Future<List<[[entity_name]]Dto>> getAll();
30
+
31
+ @override
32
+ @GET('/[[kebab_name]]s/{id}')
33
+ Future<[[entity_name]]Dto> getById(@Path('id') String id);
34
+
35
+ @override
36
+ @POST('/[[kebab_name]]s')
37
+ Future<[[entity_name]]Dto> create(@Body() [[entity_name]]Dto [[entity_name_camel]]Dto);
38
+
39
+ @override
40
+ @PUT('/[[kebab_name]]s/{id}')
41
+ Future<[[entity_name]]Dto> update(@Path('id') String id, @Body() [[entity_name]]Dto [[entity_name_camel]]Dto);
42
+
43
+ @override
44
+ @DELETE('/[[kebab_name]]s/{id}')
45
+ Future<void> delete(@Path('id') String id);
46
+ }
@@ -0,0 +1,90 @@
1
+ /*
2
+ * Repository implementation template
3
+ * Provides concrete implementation of repository interface with:
4
+ * - BaseRepositoryMixin for centralized error handling
5
+ * - Service for API calls
6
+ * - Mapper for DTO-Domain conversions
7
+ * - Injectable dependency registration
8
+ *
9
+ * This repository acts as a bridge between the domain layer and the API layer
10
+ */
11
+
12
+ import 'package:dartz/dartz.dart';
13
+ import 'package:injectable/injectable.dart';
14
+ import 'package:[[project_name]]/core/infrastructure/base_repository_mixin.dart';
15
+ import 'package:[[project_name]]/[[import_prefix]]/model/[[file_name]].dart';
16
+ import 'package:[[project_name]]/[[import_prefix]]/model/[[file_name]]_failure.dart';
17
+ import 'package:[[project_name]]/[[import_prefix]]/model/i_[[file_name]]_repository.dart';
18
+ import 'package:[[project_name]]/[[import_prefix]]/infrastructure/[[file_name]]_dto.dart';
19
+ import 'package:[[project_name]]/[[import_prefix]]/infrastructure/i_[[file_name]]_service.dart';
20
+ import 'package:[[project_name]]/[[import_prefix]]/infrastructure/[[file_name]]_mapper.dart';
21
+
22
+ @Injectable(as: I[[entity_name]]Repository)
23
+ class [[entity_name]]Repository with BaseRepositoryMixin<[[entity_name]]Failure> implements I[[entity_name]]Repository {
24
+ final I[[entity_name]]Service _service;
25
+ final [[entity_name]]Mapper _mapper;
26
+
27
+ [[entity_name]]Repository(
28
+ this._service,
29
+ this._mapper,
30
+ );
31
+
32
+ @override
33
+ Future<Either<[[entity_name]]Failure, List<[[entity_name]]>>> getAll() {
34
+ return handleListCall(
35
+ serviceCall: () => _service.getAll(),
36
+ mapToDomainList: (List<[[entity_name]]Dto> dtos) => _mapper.toDomainList(dtos),
37
+ onUnexpected: () => const [[entity_name]]Failure.unexpected(),
38
+ onNotFound: () => const [[entity_name]]Failure.notFound(),
39
+ onInsufficientPermission: () => const [[entity_name]]Failure.insufficientPermission(),
40
+ );
41
+ }
42
+
43
+ @override
44
+ Future<Either<[[entity_name]]Failure, [[entity_name]]>> getById(String id) {
45
+ return handleSimpleCall(
46
+ serviceCall: () => _service.getById(id),
47
+ mapToDomain: ([[entity_name]]Dto dto) => _mapper.toDomain(dto),
48
+ onUnexpected: () => const [[entity_name]]Failure.unexpected(),
49
+ onNotFound: () => const [[entity_name]]Failure.notFound(),
50
+ onInsufficientPermission: () => const [[entity_name]]Failure.insufficientPermission(),
51
+ );
52
+ }
53
+
54
+ @override
55
+ Future<Either<[[entity_name]]Failure, Unit>> create([[entity_name]] item) {
56
+ return handleUnitCall(
57
+ serviceCall: () async {
58
+ final [[entity_name]]Dto dto = _mapper.toDto(item);
59
+ await _service.create(dto);
60
+ },
61
+ onUnexpected: () => const [[entity_name]]Failure.unexpected(),
62
+ onNotFound: () => const [[entity_name]]Failure.notFound(),
63
+ onInsufficientPermission: () => const [[entity_name]]Failure.insufficientPermission(),
64
+ );
65
+ }
66
+
67
+ @override
68
+ Future<Either<[[entity_name]]Failure, Unit>> update([[entity_name]] item) {
69
+ return handleUnitCall(
70
+ serviceCall: () async {
71
+ final [[entity_name]]Dto dto = _mapper.toDto(item);
72
+ await _service.update(item.id.getOrCrash(), dto);
73
+ },
74
+ onUnexpected: () => const [[entity_name]]Failure.unexpected(),
75
+ onNotFound: () => const [[entity_name]]Failure.notFound(),
76
+ onInsufficientPermission: () => const [[entity_name]]Failure.insufficientPermission(),
77
+ onUnableToUpdate: () => const [[entity_name]]Failure.unableToUpdate(),
78
+ );
79
+ }
80
+
81
+ @override
82
+ Future<Either<[[entity_name]]Failure, Unit>> delete(String id) {
83
+ return handleUnitCall(
84
+ serviceCall: () => _service.delete(id),
85
+ onUnexpected: () => const [[entity_name]]Failure.unexpected(),
86
+ onNotFound: () => const [[entity_name]]Failure.notFound(),
87
+ onInsufficientPermission: () => const [[entity_name]]Failure.insufficientPermission(),
88
+ );
89
+ }
90
+ }
@@ -0,0 +1,23 @@
1
+ /*
2
+ * Binds I[[entity_name]]Service to mock or remote implementation per DataSourceConfig.
3
+ */
4
+
5
+ import 'package:injectable/injectable.dart';
6
+ import 'package:[[project_name]]/apis/common/data_source_config.dart';
7
+ import 'package:[[project_name]]/[[import_prefix]]/infrastructure/i_[[file_name]]_service.dart';
8
+ import 'package:[[project_name]]/[[import_prefix]]/infrastructure/[[file_name]]_remote_service.dart';
9
+ import 'package:[[project_name]]/[[import_prefix]]/infrastructure/mock_[[file_name]]_service.dart';
10
+
11
+ @module
12
+ abstract class [[entity_name]]ServiceModule {
13
+ @lazySingleton
14
+ I[[entity_name]]Service [[entity_name_camel]]Service(
15
+ [[entity_name]]RemoteService remote,
16
+ Mock[[entity_name]]Service mock,
17
+ ) {
18
+ if (DataSourceConfig.useMock('[[file_name]]')) {
19
+ return mock;
20
+ }
21
+ return remote;
22
+ }
23
+ }
@@ -0,0 +1,3 @@
1
+ enum [[enum_name]] {
2
+ [[values]],
3
+ }
@@ -0,0 +1,18 @@
1
+ /*
2
+ * Service interface for [[entity_name]] data access.
3
+ * Implemented by mock (JSON) and remote (Retrofit) services.
4
+ */
5
+
6
+ import 'package:[[project_name]]/[[import_prefix]]/infrastructure/[[file_name]]_dto.dart';
7
+
8
+ abstract class I[[entity_name]]Service {
9
+ Future<List<[[entity_name]]Dto>> getAll();
10
+
11
+ Future<[[entity_name]]Dto> getById(String id);
12
+
13
+ Future<[[entity_name]]Dto> create([[entity_name]]Dto [[entity_name_camel]]Dto);
14
+
15
+ Future<[[entity_name]]Dto> update(String id, [[entity_name]]Dto [[entity_name_camel]]Dto);
16
+
17
+ Future<void> delete(String id);
18
+ }
@@ -0,0 +1,73 @@
1
+ /*
2
+ * Mock [[entity_name]] service — loads seed data from assets/mock/[[file_name]].json.
3
+ * CRUD mutations are kept in memory for the current app session.
4
+ */
5
+
6
+ import 'dart:convert';
7
+
8
+ import 'package:flutter/services.dart';
9
+ import 'package:injectable/injectable.dart';
10
+ import 'package:[[project_name]]/[[import_prefix]]/infrastructure/[[file_name]]_dto.dart';
11
+ import 'package:[[project_name]]/[[import_prefix]]/infrastructure/i_[[file_name]]_service.dart';
12
+
13
+ @lazySingleton
14
+ class Mock[[entity_name]]Service implements I[[entity_name]]Service {
15
+ static const String _assetPath = 'assets/mock/[[file_name]].json';
16
+
17
+ List<[[entity_name]]Dto>? _items;
18
+
19
+ Future<List<[[entity_name]]Dto>> _loadItems() async {
20
+ if (_items != null) {
21
+ return _items!;
22
+ }
23
+ final String raw = await rootBundle.loadString(_assetPath);
24
+ final Map<String, dynamic> decoded = jsonDecode(raw) as Map<String, dynamic>;
25
+ final List<dynamic> list = decoded['items'] as List<dynamic>? ?? <dynamic>[];
26
+ _items = list
27
+ .map((dynamic e) => [[entity_name]]Dto.fromJson(Map<String, dynamic>.from(e as Map)))
28
+ .toList();
29
+ return _items!;
30
+ }
31
+
32
+ @override
33
+ Future<List<[[entity_name]]Dto>> getAll() => _loadItems();
34
+
35
+ @override
36
+ Future<[[entity_name]]Dto> getById(String id) async {
37
+ final List<[[entity_name]]Dto> items = await _loadItems();
38
+ for (final [[entity_name]]Dto item in items) {
39
+ if (item.id == id) {
40
+ return item;
41
+ }
42
+ }
43
+ throw StateError('[[entity_name]] not found: $id');
44
+ }
45
+
46
+ @override
47
+ Future<[[entity_name]]Dto> create([[entity_name]]Dto [[entity_name_camel]]Dto) async {
48
+ final List<[[entity_name]]Dto> items = await _loadItems();
49
+ items.add([[entity_name_camel]]Dto);
50
+ return [[entity_name_camel]]Dto;
51
+ }
52
+
53
+ @override
54
+ Future<[[entity_name]]Dto> update(String id, [[entity_name]]Dto [[entity_name_camel]]Dto) async {
55
+ final List<[[entity_name]]Dto> items = await _loadItems();
56
+ final int index = items.indexWhere(([[entity_name]]Dto e) => e.id == id);
57
+ if (index < 0) {
58
+ throw StateError('[[entity_name]] not found: $id');
59
+ }
60
+ items[index] = [[entity_name_camel]]Dto;
61
+ return [[entity_name_camel]]Dto;
62
+ }
63
+
64
+ @override
65
+ Future<void> delete(String id) async {
66
+ final List<[[entity_name]]Dto> items = await _loadItems();
67
+ final int before = items.length;
68
+ items.removeWhere(([[entity_name]]Dto e) => e.id == id);
69
+ if (items.length == before) {
70
+ throw StateError('[[entity_name]] not found: $id');
71
+ }
72
+ }
73
+ }