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,381 @@
1
+ import 'package:another_flushbar/flushbar_helper.dart';
2
+ import 'package:caravaggio_ui/caravaggio_ui.dart';
3
+ import 'package:flutter/foundation.dart';
4
+ import 'package:flutter/material.dart';
5
+ import 'package:flutter_bloc/flutter_bloc.dart';
6
+ import 'package:flutter_svg/flutter_svg.dart';
7
+ import 'package:[[project_name]]/features/auth/application/auth_bloc.dart';
8
+ import 'package:[[project_name]]/features/auth/presentation/login_page.dart';
9
+ import 'package:[[project_name]]/features/auth/sign_in_form/application/sign_in_form_bloc.dart';
10
+ import 'package:[[project_name]]/features/home/home_page.dart';
11
+ import 'package:[[project_name]]/domain/auth/model/auth_failure.dart';
12
+ import 'package:[[project_name]]/core/errors/error_localizer.dart';
13
+ import 'package:go_router/go_router.dart';
14
+ import 'package:[[project_name]]/core/model/failures.dart';
15
+ import 'package:[[project_name]]/domain/auth/model/value_objects.dart';
16
+ import 'package:dartz/dartz.dart' as dz;
17
+
18
+ class SignInForm extends StatelessWidget {
19
+ const SignInForm({super.key});
20
+
21
+ @override
22
+ Widget build(BuildContext context) {
23
+ return BlocConsumer<SignInFormBloc, SignInFormState>(
24
+ listener: (BuildContext context, SignInFormState state) {
25
+ state.authFailureOrSuccessOption.fold(
26
+ () {},
27
+ (dz.Either<AuthFailure, dz.Unit> either) {
28
+ either.fold(
29
+ (AuthFailure failure) {
30
+ FlushbarHelper.createError(
31
+ message: ErrorLocalizer.localizeAuthFailure(context, failure),
32
+ ).show(context);
33
+ },
34
+ (_) {
35
+ context.read<AuthBloc>().add(const AuthEvent.authCheckRequested());
36
+ context.go(HomePage.routeName);
37
+ },
38
+ );
39
+ },
40
+ );
41
+ },
42
+ builder: (BuildContext context, SignInFormState state) {
43
+ final ThemeData theme = Theme.of(context);
44
+ final Color? muted = theme.textTheme.bodySmall?.color?.withValues(alpha: 0.7);
45
+ final bool isSignIn = state.signInAction == SignInAction.signIn;
46
+
47
+ return DecoratedBox(
48
+ decoration: BoxDecoration(
49
+ gradient: CGradient.primaryLightToSecondaryLight,
50
+ ),
51
+ child: SafeArea(
52
+ child: Center(
53
+ child: SingleChildScrollView(
54
+ padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24),
55
+ child: ConstrainedBox(
56
+ constraints: const BoxConstraints(maxWidth: 420),
57
+ child: Column(
58
+ mainAxisSize: MainAxisSize.min,
59
+ children: <Widget>[
60
+ Hero(
61
+ tag: LoginPage.logoHeroTag,
62
+ child: Material(
63
+ type: MaterialType.transparency,
64
+ child: Image.asset(
65
+ 'assets/logo.png',
66
+ height: 58,
67
+ ),
68
+ ),
69
+ ),
70
+ const SizedBox(height: 20),
71
+ CWrapper(
72
+ loading: state.isSubmitting,
73
+ message: isSignIn ? 'Accesso in corso...' : 'Registrazione in corso...',
74
+ borderRadius: const BorderRadius.all(AppRadius.l),
75
+ child: Container(
76
+ decoration: BoxDecoration(
77
+ color: theme.cardColor,
78
+ borderRadius: const BorderRadius.all(AppRadius.l),
79
+ boxShadow: AppShadow.sm,
80
+ ),
81
+ child: Column(
82
+ crossAxisAlignment: CrossAxisAlignment.stretch,
83
+ children: <Widget>[
84
+ Padding(
85
+ padding: const EdgeInsets.fromLTRB(20, 20, 20, 0),
86
+ child: Row(
87
+ children: <Widget>[
88
+ Expanded(
89
+ child: _AuthModeTab(
90
+ label: 'Accedi',
91
+ selected: isSignIn,
92
+ onTap: state.isSubmitting
93
+ ? null
94
+ : () {
95
+ if (!isSignIn) {
96
+ context.read<SignInFormBloc>().add(
97
+ const SignInFormEvent.toggleSignInAction(),
98
+ );
99
+ }
100
+ },
101
+ ),
102
+ ),
103
+ const SizedBox(width: 8),
104
+ Expanded(
105
+ child: _AuthModeTab(
106
+ label: 'Registrati',
107
+ selected: !isSignIn,
108
+ onTap: state.isSubmitting
109
+ ? null
110
+ : () {
111
+ if (isSignIn) {
112
+ context.read<SignInFormBloc>().add(
113
+ const SignInFormEvent.toggleSignInAction(),
114
+ );
115
+ }
116
+ },
117
+ ),
118
+ ),
119
+ ],
120
+ ),
121
+ ),
122
+ Padding(
123
+ padding: const EdgeInsets.all(20),
124
+ child: Row(
125
+ children: <Widget>[
126
+ Container(
127
+ width: 52,
128
+ height: 52,
129
+ decoration: BoxDecoration(
130
+ borderRadius: const BorderRadius.all(AppRadius.m),
131
+ gradient: CGradient.primaryToSecondary,
132
+ ),
133
+ child: Icon(
134
+ isSignIn ? Icons.lock_outline : Icons.person_add_outlined,
135
+ color: Colors.white,
136
+ size: 28,
137
+ ),
138
+ ),
139
+ const SizedBox(width: 14),
140
+ Expanded(
141
+ child: Column(
142
+ crossAxisAlignment: CrossAxisAlignment.start,
143
+ children: <Widget>[
144
+ CText.headline(
145
+ isSignIn ? 'Bentornato' : 'Crea il tuo account',
146
+ size: TextSize.small,
147
+ ).bold,
148
+ const SizedBox(height: 4),
149
+ CText.body(
150
+ isSignIn
151
+ ? 'Inserisci le tue credenziali per continuare'
152
+ : 'Compila i campi per registrarti',
153
+ size: TextSize.small,
154
+ style: TextStyle(color: muted),
155
+ ),
156
+ ],
157
+ ),
158
+ ),
159
+ ],
160
+ ),
161
+ ),
162
+ Divider(height: 1, color: CColors.secondary.shade200),
163
+ Padding(
164
+ padding: const EdgeInsets.all(20),
165
+ child: Form(
166
+ autovalidateMode: state.showErrorMessages
167
+ ? AutovalidateMode.always
168
+ : AutovalidateMode.disabled,
169
+ child: Column(
170
+ crossAxisAlignment: CrossAxisAlignment.stretch,
171
+ children: <Widget>[
172
+ CText.label('Account', size: TextSize.small).bold,
173
+ const SizedBox(height: 12),
174
+ CTextField.filled(
175
+ decoration: const CFieldDecoration(
176
+ labelText: 'Email',
177
+ hintText: 'nome@esempio.com',
178
+ prefixIcon: Icon(Icons.email_outlined),
179
+ ),
180
+ initialValue: kDebugMode ? 'email@example.com' : null,
181
+ autocorrect: false,
182
+ keyboardType: TextInputType.emailAddress,
183
+ textInputAction: TextInputAction.next,
184
+ enabled: !state.isSubmitting,
185
+ onChanged: (String value) {
186
+ context.read<SignInFormBloc>().add(SignInFormEvent.emailChanged(value));
187
+ },
188
+ validator: (String? value) {
189
+ return EmailAddress(value ?? '').value.fold(
190
+ (ValueFailure<String> failure) => failure.title(context, failure),
191
+ (_) => null,
192
+ );
193
+ },
194
+ ),
195
+ const SizedBox(height: 14),
196
+ CTextField.filled(
197
+ decoration: const CFieldDecoration(
198
+ labelText: 'Password',
199
+ hintText: 'Inserisci la password',
200
+ labelIcon: Icon(Icons.lock_outline, color: Color(0xFF6B7280)),
201
+ ),
202
+ obscureText: true,
203
+ autocorrect: false,
204
+ textInputAction: isSignIn ? TextInputAction.done : TextInputAction.next,
205
+ initialValue: kDebugMode ? 'password' : null,
206
+ enabled: !state.isSubmitting,
207
+ onChanged: (String value) {
208
+ context.read<SignInFormBloc>().add(SignInFormEvent.passwordChanged(value));
209
+ },
210
+ validator: (String? value) {
211
+ return Password(value ?? '').value.fold(
212
+ (ValueFailure<String> failure) => failure.title(context, failure),
213
+ (_) => null,
214
+ );
215
+ },
216
+ ),
217
+ if (!isSignIn) ...<Widget>[
218
+ const SizedBox(height: 14),
219
+ CTextField.filled(
220
+ decoration: const CFieldDecoration(
221
+ labelText: 'Conferma password',
222
+ hintText: 'Ripeti la password',
223
+ labelIcon: Icon(Icons.lock_outline, color: Color(0xFF6B7280)),
224
+ ),
225
+ obscureText: true,
226
+ autocorrect: false,
227
+ textInputAction: TextInputAction.done,
228
+ initialValue: kDebugMode ? 'password' : null,
229
+ enabled: !state.isSubmitting,
230
+ onChanged: (String value) {
231
+ context.read<SignInFormBloc>().add(
232
+ SignInFormEvent.confirmPasswordChanged(value),
233
+ );
234
+ },
235
+ validator: (String? value) {
236
+ return ConfirmPassword(
237
+ context.read<SignInFormBloc>().state.password.rawValue,
238
+ value ?? '',
239
+ ).value.fold(
240
+ (ValueFailure<String> failure) => failure.title(context, failure),
241
+ (_) => null,
242
+ );
243
+ },
244
+ ),
245
+ ],
246
+ const SizedBox(height: 20),
247
+ CButton.elevated(
248
+ radius: AppRadius.s,
249
+ onPressed: state.isSubmitting
250
+ ? null
251
+ : () {
252
+ if (isSignIn) {
253
+ context.read<SignInFormBloc>().add(
254
+ const SignInFormEvent.signInWithEmailAndPasswordPressed(),
255
+ );
256
+ } else {
257
+ context.read<SignInFormBloc>().add(
258
+ const SignInFormEvent.registerWithEmailAndPasswordPressed(),
259
+ );
260
+ }
261
+ },
262
+ child: CText.label(isSignIn ? 'Accedi' : 'Registrati').bold,
263
+ ),
264
+ const SizedBox(height: 20),
265
+ Row(
266
+ children: <Widget>[
267
+ Expanded(child: Divider(color: CColors.secondary.shade200)),
268
+ Padding(
269
+ padding: const EdgeInsets.symmetric(horizontal: 12),
270
+ child: CText.label(
271
+ 'oppure continua con',
272
+ size: TextSize.small,
273
+ style: TextStyle(color: muted),
274
+ ),
275
+ ),
276
+ Expanded(child: Divider(color: CColors.secondary.shade200)),
277
+ ],
278
+ ),
279
+ const SizedBox(height: 16),
280
+ CButton.outlined(
281
+ onPressed: state.isSubmitting
282
+ ? null
283
+ : () {
284
+ if (isSignIn) {
285
+ context.read<SignInFormBloc>().add(
286
+ const SignInFormEvent.signInWithGooglePressed(),
287
+ );
288
+ } else {
289
+ context.read<SignInFormBloc>().add(
290
+ const SignInFormEvent.registerWithGooglePressed(),
291
+ );
292
+ }
293
+ },
294
+ radius: AppRadius.s,
295
+ color: Colors.grey,
296
+ suffixIcon: SvgPicture.asset(
297
+ 'assets/svgs/google.svg',
298
+ semanticsLabel: 'Google logo',
299
+ width: 24,
300
+ height: 24,
301
+ ),
302
+ child: CText.label('Google').bold,
303
+ ),
304
+ const SizedBox(height: 8),
305
+ CButton.elevated(
306
+ onPressed: state.isSubmitting
307
+ ? null
308
+ : () {
309
+ if (isSignIn) {
310
+ context.read<SignInFormBloc>().add(
311
+ const SignInFormEvent.signInWithApplePressed(),
312
+ );
313
+ } else {
314
+ context.read<SignInFormBloc>().add(
315
+ const SignInFormEvent.registerWithApplePressed(),
316
+ );
317
+ }
318
+ },
319
+ color: Colors.black,
320
+ radius: AppRadius.s,
321
+ suffixIcon: SvgPicture.asset(
322
+ 'assets/svgs/apple.svg',
323
+ semanticsLabel: 'Apple logo',
324
+ color: Colors.white,
325
+ width: 24,
326
+ height: 24,
327
+ ),
328
+ child: CText.label('Apple').white.bold,
329
+ ),
330
+ ],
331
+ ),
332
+ ),
333
+ ),
334
+ ],
335
+ ),
336
+ ),
337
+ ),
338
+ ],
339
+ ),
340
+ ),
341
+ ),
342
+ ),
343
+ ),
344
+ );
345
+ },
346
+ );
347
+ }
348
+ }
349
+
350
+ class _AuthModeTab extends StatelessWidget {
351
+ const _AuthModeTab({
352
+ required this.label,
353
+ required this.selected,
354
+ required this.onTap,
355
+ });
356
+
357
+ final String label;
358
+ final bool selected;
359
+ final VoidCallback? onTap;
360
+
361
+ @override
362
+ Widget build(BuildContext context) {
363
+ return Material(
364
+ color: selected ? CColors.primaryColor.withValues(alpha: 0.12) : Colors.transparent,
365
+ borderRadius: const BorderRadius.all(AppRadius.s),
366
+ child: InkWell(
367
+ onTap: onTap,
368
+ borderRadius: const BorderRadius.all(AppRadius.s),
369
+ child: Padding(
370
+ padding: const EdgeInsets.symmetric(vertical: 10),
371
+ child: Center(
372
+ child: CText.label(
373
+ label,
374
+ size: TextSize.small,
375
+ ).withColor(selected ? CColors.primaryColor : CColors.secondary.shade500).bold,
376
+ ),
377
+ ),
378
+ ),
379
+ );
380
+ }
381
+ }
@@ -0,0 +1,49 @@
1
+ /*
2
+ * Component BLoC template for UI component state management
3
+ * Generates a lightweight BLoC for component-level logic:
4
+ * - Load and Delete operations (basic component actions)
5
+ * - Commented repository calls for customization
6
+ *
7
+ * This is simpler than feature BLoCs, focused on component-specific
8
+ * state management without full CRUD operations
9
+ */
10
+
11
+ import 'package:bloc/bloc.dart';
12
+ import 'package:freezed_annotation/freezed_annotation.dart';
13
+
14
+ part '[[feature_name]]_event.dart';
15
+ part '[[feature_name]]_state.dart';
16
+
17
+ part '[[feature_name]]_bloc.freezed.dart';
18
+
19
+ class [[feature_name.capitalize()]]Bloc extends Bloc<[[feature_name.capitalize()]]Event, [[feature_name.capitalize()]]State> {
20
+
21
+ [[feature_name.capitalize()]]Bloc() : super(const [[feature_name.capitalize()]]State.initial()) {
22
+ on<LoadRequested>(_onLoadRequested);
23
+ on<DeleteRequested>(_onDeleteRequested);
24
+ }
25
+
26
+ void _onLoadRequested(LoadRequested event, Emitter<[[feature_name.capitalize()]]State> emit) async {
27
+ emit(const [[feature_name.capitalize()]]State.loading());
28
+ // final result = await _repository.getAll();
29
+ // result.fold(
30
+ // (failure) => emit([[feature_name.capitalize()]]State.error(failure.toString())),
31
+ // (items) => emit([[feature_name.capitalize()]]State.loaded(items)),
32
+ // );
33
+ }
34
+
35
+ void _onDeleteRequested(DeleteRequested event, Emitter<[[feature_name.capitalize()]]State> emit) async {
36
+ emit(const [[feature_name.capitalize()]]State.loading());
37
+ // final result = await _repository.delete(event.id);
38
+ // result.fold(
39
+ // (failure) => emit([[feature_name.capitalize()]]State.error(failure.toString())),
40
+ // (_) async {
41
+ // final itemsResult = await _repository.getAll();
42
+ // itemsResult.fold(
43
+ // (failure) => emit([[feature_name.capitalize()]]State.error(failure.toString())),
44
+ // (items) => emit([[feature_name.capitalize()]]State.loaded(items)),
45
+ // );
46
+ // },
47
+ // );
48
+ }
49
+ }
@@ -0,0 +1,7 @@
1
+ part of '[[feature_name]]_bloc.dart';
2
+
3
+ @freezed
4
+ abstract class [[feature_name.capitalize()]]Event with _$[[feature_name.capitalize()]]Event {
5
+ const factory [[feature_name.capitalize()]]Event.loadRequested() = LoadRequested;
6
+ const factory [[feature_name.capitalize()]]Event.deleteRequested(String id) = DeleteRequested;
7
+ }
@@ -0,0 +1,56 @@
1
+ import 'package:bloc/bloc.dart';
2
+ import 'package:dartz/dartz.dart';
3
+ import 'package:freezed_annotation/freezed_annotation.dart';
4
+ {% if domain_value_objects_import %}
5
+ import 'package:[[project_name]]/[[domain_value_objects_import]]';
6
+ {% endif %}
7
+ {% if extra_imports %}
8
+ [[extra_imports]]
9
+ {% endif %}
10
+
11
+ part '[[component_name]]_form_event.dart';
12
+ part '[[component_name]]_form_state.dart';
13
+ part '[[component_name]]_form_bloc.freezed.dart';
14
+
15
+ class [[pascal_name]]FormBloc extends Bloc<[[pascal_name]]FormEvent, [[pascal_name]]FormState> {
16
+ [[pascal_name]]FormBloc() : super([[pascal_name]]FormState.initial()) {
17
+ {% for field in field_list %}
18
+ {% if field.name != 'id' %}
19
+ on<[[field.pascal_name]]Changed>(_on[[field.pascal_name]]Changed);
20
+ {% endif %}
21
+ {% endfor %}
22
+ on<Submit>(_onSubmit);
23
+ }
24
+
25
+ {% for field in field_list %}
26
+ {% if field.name != 'id' %}
27
+ {% if field.is_enum %}
28
+ void _on[[field.pascal_name]]Changed([[field.pascal_name]]Changed event, Emitter<[[pascal_name]]FormState> emit) {
29
+ emit(state.copyWith(
30
+ [[field.name]]: [[field.bloc_parse_expr]],
31
+ showErrorMessages: false,
32
+ ));
33
+ }
34
+ {% elif field.has_value_object %}
35
+ void _on[[field.pascal_name]]Changed([[field.pascal_name]]Changed event, Emitter<[[pascal_name]]FormState> emit) {
36
+ emit(state.copyWith(
37
+ [[field.name]]: [[field.pascal_name]]([[field.bloc_parse_expr]]),
38
+ showErrorMessages: false,
39
+ ));
40
+ }
41
+ {% endif %}
42
+ {% endif %}
43
+ {% endfor %}
44
+
45
+ Future<void> _onSubmit(Submit event, Emitter<[[pascal_name]]FormState> emit) async {
46
+ emit(state.copyWith(isSubmitting: true, showErrorMessages: false));
47
+
48
+ // TODO: Replace with actual submit operation
49
+ final Either<String, Unit> result = await Future.value(right(unit));
50
+
51
+ emit(state.copyWith(
52
+ isSubmitting: false,
53
+ failureOrSuccessOption: some(result),
54
+ ));
55
+ }
56
+ }
@@ -0,0 +1,7 @@
1
+ part of '[[component_name]]_form_bloc.dart';
2
+
3
+ @freezed
4
+ abstract class [[pascal_name]]FormEvent with _$[[pascal_name]]FormEvent {
5
+ [[field_events]]
6
+ const factory [[pascal_name]]FormEvent.submit() = Submit;
7
+ }
@@ -0,0 +1,18 @@
1
+ part of '[[component_name]]_form_bloc.dart';
2
+
3
+ @freezed
4
+ abstract class [[pascal_name]]FormState with _$[[pascal_name]]FormState {
5
+ const factory [[pascal_name]]FormState({
6
+ [[field_declarations]]
7
+ required bool showErrorMessages,
8
+ required bool isSubmitting,
9
+ required Option<Either<String, Unit>> failureOrSuccessOption,
10
+ }) = _[[pascal_name]]FormState;
11
+
12
+ factory [[pascal_name]]FormState.initial() => [[pascal_name]]FormState(
13
+ [[initial_values]]
14
+ showErrorMessages: false,
15
+ isSubmitting: false,
16
+ failureOrSuccessOption: none(),
17
+ );
18
+ }
@@ -0,0 +1,130 @@
1
+ import 'package:caravaggio_ui/caravaggio_ui.dart';
2
+ import 'package:flutter/material.dart' hide Title;
3
+ import 'package:flutter_bloc/flutter_bloc.dart';
4
+ {% if field_list %}
5
+ import 'package:[[project_name]]/core/model/failures.dart';
6
+ {% endif %}
7
+ {% if domain_value_objects_import %}
8
+ import 'package:[[project_name]]/[[domain_value_objects_import]]';
9
+ {% endif %}
10
+ [[bloc_import]]
11
+ {% if extra_imports %}
12
+ [[extra_imports]]
13
+ {% endif %}
14
+
15
+ class [[pascal_name]]Component extends StatelessWidget {
16
+ const [[pascal_name]]Component({super.key});
17
+
18
+ @override
19
+ Widget build(BuildContext context) {
20
+ return BlocBuilder<[[bloc_name]], [[state_name]]>(
21
+ builder: (BuildContext context, [[state_name]] state) {
22
+ return Form(
23
+ autovalidateMode: state.showErrorMessages ? AutovalidateMode.always : AutovalidateMode.disabled,
24
+ child: SingleChildScrollView(
25
+ child: Padding(
26
+ padding: const EdgeInsets.all(16.0),
27
+ child: Column(
28
+ mainAxisSize: MainAxisSize.min,
29
+ children: <Widget>[
30
+ {% for field in field_list %}
31
+ {% if field.name != 'id' %}
32
+ {% if field.control_type == 'text' %}
33
+ CTextField.bordered(
34
+ decoration: const CFieldDecoration(
35
+ labelText: '[[field.label]]',
36
+ ),
37
+ onChanged: (String value) {
38
+ context.read<[[bloc_name]]>().add([[pascal_name]]FormEvent.[[field.name]]Changed(value));
39
+ },
40
+ validator: (String? value) {
41
+ return [[field.pascal_name]](value ?? '').value.fold(
42
+ (ValueFailure<String> failure) => failure.title(context, failure),
43
+ (_) => null,
44
+ );
45
+ },
46
+ ),
47
+ const SizedBox(height: 8),
48
+ {% elif field.control_type == 'number' %}
49
+ CTextField.bordered(
50
+ decoration: const CFieldDecoration(
51
+ labelText: '[[field.label]]',
52
+ ),
53
+ keyboardType: [[field.keyboard_type]],
54
+ onChanged: (String value) {
55
+ context.read<[[bloc_name]]>().add([[pascal_name]]FormEvent.[[field.name]]Changed(value));
56
+ },
57
+ validator: (String? value) {
58
+ return [[field.pascal_name]]([[field.validator_input_expr]]).value.fold(
59
+ (ValueFailure<[[field.mapped_field_type]]> failure) => failure.title(context, failure),
60
+ (_) => null,
61
+ );
62
+ },
63
+ ),
64
+ const SizedBox(height: 8),
65
+ {% elif field.control_type == 'checkbox' %}
66
+ CCheckbox(
67
+ controller: CCheckboxController(initialValue: state.[[field.name]].getOrCrash()),
68
+ label: '[[field.label]]',
69
+ onChanged: (bool? value) {
70
+ if (value != null) {
71
+ context.read<[[bloc_name]]>().add([[pascal_name]]FormEvent.[[field.name]]Changed(value));
72
+ }
73
+ },
74
+ ),
75
+ const SizedBox(height: 8),
76
+ {% elif field.control_type == 'date' %}
77
+ CDatePicker(
78
+ mode: CustomDatePickerMode.single,
79
+ onChanged: (List<DateTime> dates) {
80
+ if (dates.isNotEmpty) {
81
+ context.read<[[bloc_name]]>().add([[pascal_name]]FormEvent.[[field.name]]Changed(dates.first));
82
+ }
83
+ },
84
+ ),
85
+ const SizedBox(height: 8),
86
+ {% elif field.control_type == 'dropdown' %}
87
+ CDropdown<[[field.enum_class]]>.bordered(
88
+ items: [[field.enum_class]].values
89
+ .map(([[field.enum_class]] e) =>
90
+ CDropdownItemModel<[[field.enum_class]]>(value: e, text: e.name))
91
+ .toList(),
92
+ decoration: const CFieldDecoration(
93
+ labelText: '[[field.label]]',
94
+ ),
95
+ onChanged: ([[field.enum_class]]? value) {
96
+ if (value != null) {
97
+ context.read<[[bloc_name]]>().add([[pascal_name]]FormEvent.[[field.name]]Changed(value));
98
+ }
99
+ },
100
+ ),
101
+ const SizedBox(height: 8),
102
+ {% endif %}
103
+ {% endif %}
104
+ {% endfor %}
105
+ CButton.elevated(
106
+ radius: AppRadius.s,
107
+ onPressed: state.isSubmitting ? null : () {
108
+ context.read<[[bloc_name]]>().add(const [[pascal_name]]FormEvent.submit());
109
+ },
110
+ child: state.isSubmitting
111
+ ? SizedBox(
112
+ width: 20,
113
+ height: 20,
114
+ child: CCircularProgressIndicator.primary(),
115
+ )
116
+ : CText.label('Submit'),
117
+ ),
118
+ if (state.isSubmitting) ...<Widget>[
119
+ const SizedBox(height: 8),
120
+ CLinearProgressIndicator.primary(),
121
+ ],
122
+ ],
123
+ ),
124
+ ),
125
+ ),
126
+ );
127
+ },
128
+ );
129
+ }
130
+ }