@rashidee/co2 1.2.5

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 (190) hide show
  1. package/dist/index.js +25449 -0
  2. package/drizzle/0000_remarkable_charles_xavier.sql +210 -0
  3. package/drizzle/0001_wise_wolf_cub.sql +13 -0
  4. package/drizzle/0002_secret_spyke.sql +16 -0
  5. package/drizzle/0003_workspace_config_singleton.sql +16 -0
  6. package/drizzle/0004_terminal_claim_v120.sql +11 -0
  7. package/drizzle/0005_brainstorm_v124.sql +10 -0
  8. package/drizzle/0006_project_v140.sql +6 -0
  9. package/drizzle/0007_prd_wizard_v124.sql +14 -0
  10. package/drizzle/0008_workflow_artifact_v126.sql +24 -0
  11. package/drizzle/0009_project_import_v150.sql +7 -0
  12. package/drizzle/0010_quality_v151.sql +3 -0
  13. package/drizzle/meta/0000_snapshot.json +1335 -0
  14. package/drizzle/meta/0001_snapshot.json +1416 -0
  15. package/drizzle/meta/0002_snapshot.json +1510 -0
  16. package/drizzle/meta/_journal.json +83 -0
  17. package/package.json +41 -0
  18. package/plugin/.claude-plugin/marketplace.json +36 -0
  19. package/plugin/.claude-plugin/plugin.json +22 -0
  20. package/plugin/README.md +625 -0
  21. package/plugin/SKILLS.md +350 -0
  22. package/plugin/co2.svg +5 -0
  23. package/plugin/commands/brainstorm-loop.md +129 -0
  24. package/plugin/skills/conductor-defect/SKILL.md +898 -0
  25. package/plugin/skills/conductor-feature-develop/SKILL.md +1383 -0
  26. package/plugin/skills/conductor-feature-develop/references/implementation-domain-template.md +131 -0
  27. package/plugin/skills/conductor-feature-develop/references/implementation-master-template.md +89 -0
  28. package/plugin/skills/conductor-feature-develop/references/playwright-setup.md +224 -0
  29. package/plugin/skills/conductor-feature-prepare/SKILL.md +604 -0
  30. package/plugin/skills/depgen-k8s/SKILL.md +468 -0
  31. package/plugin/skills/depgen-k8s/references/dockerfile-laravel.md +197 -0
  32. package/plugin/skills/depgen-k8s/references/dockerfile-nodejs.md +163 -0
  33. package/plugin/skills/depgen-k8s/references/dockerfile-spring-boot.md +174 -0
  34. package/plugin/skills/depgen-k8s/references/k8s-patterns.md +869 -0
  35. package/plugin/skills/mockgen-shadcn/SKILL.md +1067 -0
  36. package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +1467 -0
  37. package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +498 -0
  38. package/plugin/skills/mockgen-shadcn/references/mockup-index-template.md +216 -0
  39. package/plugin/skills/mockgen-tailwind/SKILL.md +904 -0
  40. package/plugin/skills/mockgen-tailwind/references/admin-layout-template.md +720 -0
  41. package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +498 -0
  42. package/plugin/skills/mockgen-tailwind/references/mockup-index-template.md +190 -0
  43. package/plugin/skills/modelgen-nosql/SKILL.md +617 -0
  44. package/plugin/skills/modelgen-nosql/references/model-extraction-methodology.md +497 -0
  45. package/plugin/skills/modelgen-nosql/references/nosql-design-guide.md +258 -0
  46. package/plugin/skills/modelgen-relational/SKILL.md +707 -0
  47. package/plugin/skills/modelgen-relational/references/model-extraction-methodology.md +499 -0
  48. package/plugin/skills/specgen-flutter-riverpod/SKILL.md +898 -0
  49. package/plugin/skills/specgen-flutter-riverpod/references/component-patterns.md +971 -0
  50. package/plugin/skills/specgen-flutter-riverpod/references/notification-patterns.md +457 -0
  51. package/plugin/skills/specgen-flutter-riverpod/references/routing-patterns.md +362 -0
  52. package/plugin/skills/specgen-flutter-riverpod/references/security-patterns.md +709 -0
  53. package/plugin/skills/specgen-flutter-riverpod/references/spec-template.md +1566 -0
  54. package/plugin/skills/specgen-flutter-riverpod/references/state-patterns.md +518 -0
  55. package/plugin/skills/specgen-flutter-riverpod/references/storage-patterns.md +370 -0
  56. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/SKILL.md +984 -0
  57. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/batch-patterns.md +359 -0
  58. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/messaging-patterns.md +468 -0
  59. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/modulith-patterns.md +461 -0
  60. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/reporting-patterns.md +803 -0
  61. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/security-patterns.md +501 -0
  62. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/spec-template.md +1268 -0
  63. package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/web-patterns.md +301 -0
  64. package/plugin/skills/specgen-node-cli-web/SKILL.md +561 -0
  65. package/plugin/skills/specgen-node-cli-web/references/auth-patterns.md +293 -0
  66. package/plugin/skills/specgen-node-cli-web/references/cli-packaging-patterns.md +250 -0
  67. package/plugin/skills/specgen-node-cli-web/references/server-patterns.md +247 -0
  68. package/plugin/skills/specgen-node-cli-web/references/spec-template.md +401 -0
  69. package/plugin/skills/specgen-node-cli-web/references/testing-patterns.md +174 -0
  70. package/plugin/skills/specgen-react-mui/SKILL.md +789 -0
  71. package/plugin/skills/specgen-react-mui/references/component-patterns.md +1077 -0
  72. package/plugin/skills/specgen-react-mui/references/reporting-patterns.md +1998 -0
  73. package/plugin/skills/specgen-react-mui/references/routing-patterns.md +461 -0
  74. package/plugin/skills/specgen-react-mui/references/security-patterns.md +599 -0
  75. package/plugin/skills/specgen-react-mui/references/spec-template.md +1030 -0
  76. package/plugin/skills/specgen-react-mui/references/state-patterns.md +327 -0
  77. package/plugin/skills/specgen-react-tailwind/SKILL.md +859 -0
  78. package/plugin/skills/specgen-react-tailwind/references/component-patterns.md +1212 -0
  79. package/plugin/skills/specgen-react-tailwind/references/reporting-patterns.md +1529 -0
  80. package/plugin/skills/specgen-react-tailwind/references/routing-patterns.md +669 -0
  81. package/plugin/skills/specgen-react-tailwind/references/security-patterns.md +656 -0
  82. package/plugin/skills/specgen-react-tailwind/references/spec-template.md +1237 -0
  83. package/plugin/skills/specgen-react-tailwind/references/state-patterns.md +327 -0
  84. package/plugin/skills/specgen-sdk-java/SKILL.md +933 -0
  85. package/plugin/skills/specgen-sdk-java/references/api-extraction-patterns.md +247 -0
  86. package/plugin/skills/specgen-sdk-java/references/http-client-patterns.md +495 -0
  87. package/plugin/skills/specgen-sdk-java/references/packaging-patterns.md +366 -0
  88. package/plugin/skills/specgen-sdk-java/references/spec-template.md +956 -0
  89. package/plugin/skills/specgen-spring-jpa-jtehtmx/SKILL.md +1284 -0
  90. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/batch-patterns.md +1224 -0
  91. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/jasper-patterns.md +1638 -0
  92. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/messaging-patterns.md +512 -0
  93. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/modulith-patterns.md +354 -0
  94. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/security-patterns.md +551 -0
  95. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/spec-template.md +2502 -0
  96. package/plugin/skills/specgen-spring-jpa-jtehtmx/references/web-patterns.md +503 -0
  97. package/plugin/skills/specgen-spring-jpa-restapi/SKILL.md +990 -0
  98. package/plugin/skills/specgen-spring-jpa-restapi/references/batch-patterns.md +1224 -0
  99. package/plugin/skills/specgen-spring-jpa-restapi/references/messaging-patterns.md +512 -0
  100. package/plugin/skills/specgen-spring-jpa-restapi/references/modulith-patterns.md +279 -0
  101. package/plugin/skills/specgen-spring-jpa-restapi/references/reporting-patterns.md +1536 -0
  102. package/plugin/skills/specgen-spring-jpa-restapi/references/restapi-patterns.md +722 -0
  103. package/plugin/skills/specgen-spring-jpa-restapi/references/security-patterns.md +351 -0
  104. package/plugin/skills/specgen-spring-jpa-restapi/references/spec-template.md +553 -0
  105. package/plugin/skills/specgen-ts-cli/SKILL.md +861 -0
  106. package/plugin/skills/specgen-ts-cli/references/async-patterns.md +1101 -0
  107. package/plugin/skills/specgen-ts-cli/references/command-patterns.md +383 -0
  108. package/plugin/skills/specgen-ts-cli/references/config-patterns.md +430 -0
  109. package/plugin/skills/specgen-ts-cli/references/database-patterns.md +750 -0
  110. package/plugin/skills/specgen-ts-cli/references/packaging-patterns.md +426 -0
  111. package/plugin/skills/specgen-ts-cli/references/spec-template.md +1391 -0
  112. package/plugin/skills/specgen-ts-cli/references/testing-patterns.md +909 -0
  113. package/plugin/skills/testgen-functional/SKILL.md +1381 -0
  114. package/plugin/skills/tracegen-matrix/SKILL.md +243 -0
  115. package/plugin/skills/util-preparek8senv/SKILL.md +422 -0
  116. package/plugin/skills/util-projectinit/SKILL.md +307 -0
  117. package/plugin/skills/util-projectinit/reference/brainstorm-loop.md +129 -0
  118. package/plugin/skills/util-projectinit/reference/brainstorm-protocol.md +144 -0
  119. package/plugin/skills/util-projectinit/reference/brainstorm-setup-guide.md +153 -0
  120. package/plugin/skills/util-projectinit/reference/brainstorm-template-appdev.md +87 -0
  121. package/plugin/skills/util-projectinit/reference/co2-context-generation-guide.md +761 -0
  122. package/plugin/skills/util-projectsync/SKILL.md +501 -0
  123. package/plugin/skills/util-updprd/SKILL.md +324 -0
  124. package/plugin/skills/util-usanalyzer/SKILL.md +307 -0
  125. package/plugin/skills/util-ustagger/SKILL.md +154 -0
  126. package/static/assets/abnfDiagram-VRR7QNED-DvPuwyCV.js +1 -0
  127. package/static/assets/arc-DRKw7sjs.js +1 -0
  128. package/static/assets/architectureDiagram-ZJ3FMSHR-CaNcxUbC.js +36 -0
  129. package/static/assets/blockDiagram-677ZJIJ3-Demd4AOK.js +132 -0
  130. package/static/assets/c4Diagram-LMCZKHZV-PVKUfc2x.js +10 -0
  131. package/static/assets/channel-C2nVbuwv.js +1 -0
  132. package/static/assets/chunk-2Q5K7J3B-CExf8ww_.js +1 -0
  133. package/static/assets/chunk-32BRIVSS-CFPaoONF.js +1 -0
  134. package/static/assets/chunk-5VM5RSS4-DE2esM67.js +15 -0
  135. package/static/assets/chunk-EX3LRPZG-SON_YdDG.js +231 -0
  136. package/static/assets/chunk-JWPE2WC7-Qxah8LNr.js +1 -0
  137. package/static/assets/chunk-MOJQB5TN-BcWGQV3j.js +88 -0
  138. package/static/assets/chunk-RYQCIY6F-DkDj-OR5.js +1 -0
  139. package/static/assets/chunk-V7JOEXUC-BlI1Mrah.js +206 -0
  140. package/static/assets/chunk-VR4S4FIN-Agf__duj.js +1 -0
  141. package/static/assets/chunk-XXDRQBXY-B5CfAh85.js +1 -0
  142. package/static/assets/classDiagram-OUVF2IWQ-DXJIljsR.js +1 -0
  143. package/static/assets/classDiagram-v2-EOCWNBFH-DXJIljsR.js +1 -0
  144. package/static/assets/cose-bilkent-JH36ORCC-DO0cQkcy.js +1 -0
  145. package/static/assets/cynefin-VYW2F7L2-CfOnsnXc.js +178 -0
  146. package/static/assets/cynefinDiagram-TSTJHNR4-BJ8tULAv.js +62 -0
  147. package/static/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
  148. package/static/assets/dagre-VKFMJZFB-DfjwzHqP.js +4 -0
  149. package/static/assets/defaultLocale-DX6XiGOO.js +1 -0
  150. package/static/assets/diagram-FQU43EPY-CndSOIIM.js +3 -0
  151. package/static/assets/diagram-G47NLZAW-Czf5hN0Z.js +24 -0
  152. package/static/assets/diagram-NH7WQ7WH-COAyojGh.js +24 -0
  153. package/static/assets/diagram-OA4YK3LP-H8PoiN2m.js +30 -0
  154. package/static/assets/diagram-WEI45ONY-DzcaVzjp.js +41 -0
  155. package/static/assets/ebnfDiagram-CCIWWBDH-DPazGYLJ.js +1 -0
  156. package/static/assets/erDiagram-Q63AITRT-Bo-fubgF.js +85 -0
  157. package/static/assets/flowDiagram-23GEKE2U-CSvxUDwh.js +156 -0
  158. package/static/assets/ganttDiagram-NO4QXBWP-DnBveWX6.js +292 -0
  159. package/static/assets/gitGraphDiagram-IHSO6WYX-B3rVL8c-.js +106 -0
  160. package/static/assets/graph-C9eacEi8.js +1 -0
  161. package/static/assets/index-LZSQkLE2.css +41 -0
  162. package/static/assets/index-y7XDSgMG.js +495 -0
  163. package/static/assets/infoDiagram-FWYZ7A6U-CvWV9yy2.js +2 -0
  164. package/static/assets/init-Gi6I4Gst.js +1 -0
  165. package/static/assets/ishikawaDiagram-FXEZZL3T-DF28a1N6.js +70 -0
  166. package/static/assets/journeyDiagram-5HDEW3XC-CB6CugYk.js +139 -0
  167. package/static/assets/kanban-definition-HUTT4EX6-ResSLF6d.js +89 -0
  168. package/static/assets/katex-C5jXJg4s.js +257 -0
  169. package/static/assets/layout-DEXfKzaS.js +1 -0
  170. package/static/assets/linear-CLO--ryk.js +1 -0
  171. package/static/assets/map-Czzmt4hB.js +1 -0
  172. package/static/assets/mindmap-definition-LN4V7U3C-BvW3EbxX.js +96 -0
  173. package/static/assets/ordinal-Cboi1Yqb.js +1 -0
  174. package/static/assets/pegDiagram-2B236MQR-BHCgqrrM.js +1 -0
  175. package/static/assets/pieDiagram-ENE6RG2P-DVmCQ5FA.js +39 -0
  176. package/static/assets/quadrantDiagram-ABIIQ3AL-B3e5KR52.js +7 -0
  177. package/static/assets/railroadDiagram-RFXS5EU6-Csv5ysLc.js +1 -0
  178. package/static/assets/requirementDiagram-TGXJPOKE-DOMVzvnB.js +84 -0
  179. package/static/assets/sankeyDiagram-HTMAVEWB-B1dcTOvy.js +40 -0
  180. package/static/assets/sequenceDiagram-DBY2YBRQ-BY414yeO.js +162 -0
  181. package/static/assets/sizeCapture-X5ZJPWSS-CuJfrfBN.js +1 -0
  182. package/static/assets/stateDiagram-2N3HPSRC-FX4rJkzV.js +1 -0
  183. package/static/assets/stateDiagram-v2-6OUMAXLB-BVufbO_H.js +1 -0
  184. package/static/assets/swimlanes-5IMT3BWC-Sfy58mmv.js +2 -0
  185. package/static/assets/swimlanesDiagram-G3AALYLV-wDCDw08H.js +8 -0
  186. package/static/assets/timeline-definition-FHXFAJF6-CmV7by8p.js +120 -0
  187. package/static/assets/vennDiagram-L72KCM5P-FqIyFZFt.js +34 -0
  188. package/static/assets/wardleyDiagram-EHGQE667-BZ06O45i.js +78 -0
  189. package/static/assets/xychartDiagram-FW5EYKEG-BOkffR06.js +7 -0
  190. package/static/index.html +24 -0
@@ -0,0 +1,1566 @@
1
+ # Specification Template — Flutter Mobile App with Riverpod
2
+
3
+ This is the authoritative template for the generated specification. The specification is
4
+ split into **two types of files**:
5
+
6
+ 1. **`SPECIFICATION.md`** (root) — Table of Contents, shared infrastructure, and all
7
+ application-level sections. Generated once per application.
8
+ 2. **`<module>/SPEC.md`** (per-module) — Self-contained module blueprint.
9
+ Generated once per module from PRD.md.
10
+
11
+ ```
12
+ spec/
13
+ ├── SPECIFICATION.md
14
+ ├── task/
15
+ │ └── SPEC.md
16
+ ├── order/
17
+ │ └── SPEC.md
18
+ └── ...
19
+ ```
20
+
21
+ Placeholders use `{{VARIABLE}}` syntax and must be replaced with actual values gathered
22
+ from context files.
23
+
24
+ Sections marked **[CONDITIONAL]** should only be included when the corresponding
25
+ integration is selected.
26
+
27
+ **CRITICAL: Sample code requirement.** Every widget/class in the spec MUST include a
28
+ complete, continuous Dart code sample in a fenced code block. The code must be
29
+ self-explanatory and directly usable as a reference for a coding agent. Do not describe
30
+ classes with bullet points alone — always accompany descriptions with full Dart code.
31
+
32
+ ---
33
+
34
+ # Part A: Root SPECIFICATION.md
35
+
36
+ ---
37
+
38
+ ## Table of Contents
39
+
40
+ Generate a TOC with clickable Markdown anchor links to every H2 and H3 section.
41
+ Additionally, include a **Modules** section in the TOC that links to each module's
42
+ `SPEC.md` file:
43
+
44
+ ```markdown
45
+ ## Table of Contents
46
+
47
+ ### Shared Infrastructure
48
+ - [1. Project Overview](#1-project-overview)
49
+ - [2. Package Configuration](#2-package-configuration)
50
+ - ...all shared sections...
51
+
52
+ ### Modules
53
+ - [Task](task/SPEC.md)
54
+ - [Order](order/SPEC.md)
55
+ - [Catalogue](catalogue/SPEC.md)
56
+ - ...(one link per module)...
57
+ ```
58
+
59
+ Only include conditional sections that apply based on determined selections.
60
+
61
+ ---
62
+
63
+ ## Section 1: Project Overview
64
+
65
+ ```
66
+ # {{APPLICATION_NAME}} — Technical Specification
67
+
68
+ ## 1. Project Overview
69
+
70
+ **Application Name**: {{APPLICATION_NAME}}
71
+ **Project Slug**: {{PROJECT_SLUG}}
72
+ **App Display Name**: {{APP_TITLE}}
73
+ **Application ID**: {{APPLICATION_ID}}
74
+ **Framework**: Flutter Mobile (Android + iOS)
75
+ **Description**: {{APP_DESCRIPTION}}
76
+ **Versions Covered**: v1.0.0 — v{{LATEST_VERSION}}
77
+
78
+ ### Target Platforms
79
+ - **Android**: minSdk {{MIN_ANDROID_SDK}} (Android 6.0+), targetSdk 34
80
+ - **iOS**: deployment target {{MIN_IOS_VERSION}}+
81
+
82
+ ### Optional Components (Auto-Determined)
83
+ **Backend API Base URL**: {{API_BASE_URL}}
84
+ **Authentication**: {{AUTH_TYPE}}
85
+ **WebSocket**: {{WEBSOCKET}}
86
+ **i18n**: {{I18N}}
87
+ **Analytics**: {{ANALYTICS}}
88
+ **Crashlytics**: {{CRASHLYTICS}}
89
+ **ImagePicker**: {{IMAGE_PICKER}}
90
+ **FilePicker**: {{FILE_PICKER}}
91
+ **Sharing**: {{SHARING}}
92
+ **Permissions**: {{PERMISSIONS}}
93
+
94
+ ### Technology Stack
95
+ (Render the core stack version table from SKILL.md, plus selected optional integration versions)
96
+
97
+ ### User Roles
98
+ (List each user role extracted from mockup role folders, with role constant and access scope.)
99
+
100
+ | Role | Description | Constant | Accessible Modules |
101
+ |------|-------------|----------|-------------------|
102
+ | Worker | Field worker | `WORKER` | Task, Job, Profile |
103
+ | Admin | Full system | `ADMIN` | All modules |
104
+
105
+ ### Modules
106
+ (List each module from PRD.md, with type and link to its SPEC.md.)
107
+
108
+ | Module | Type | Stories | Versions | Spec |
109
+ |--------|------|---------|----------|------|
110
+ | Task | Business | 5 | 1.0.0, 1.0.4 | [SPEC](task/SPEC.md) |
111
+ | Profile | System | 3 | 1.0.0 | [SPEC](profile/SPEC.md) |
112
+
113
+ ### Input Sources
114
+ - CLAUDE.md: {{path}}
115
+ - PRD.md: {{path}}
116
+ - Module Model: {{path to MODEL.md}}
117
+ - Mockup Screens: {{path to MOCKUP.html}}
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Section 2: Package Configuration
123
+
124
+ ### 2.1 pubspec.yaml
125
+
126
+ Generate a complete `pubspec.yaml` inside a code block. Include all core and
127
+ conditional dependencies based on the determined selections.
128
+
129
+ ```yaml
130
+ name: {{PROJECT_SLUG}}
131
+ description: {{APP_DESCRIPTION}}
132
+ publish_to: 'none'
133
+ version: {{APP_VERSION}}+1
134
+
135
+ environment:
136
+ sdk: '>=3.5.0 <4.0.0'
137
+ flutter: '>=3.24.0'
138
+
139
+ dependencies:
140
+ flutter:
141
+ sdk: flutter
142
+
143
+ # State management
144
+ flutter_riverpod: ^2.5.1
145
+ hooks_riverpod: ^2.5.1
146
+ flutter_hooks: ^0.20.5
147
+ riverpod_annotation: ^2.3.5
148
+
149
+ # Local storage
150
+ hive: ^2.2.3
151
+ hive_flutter: ^1.1.0
152
+ flutter_secure_storage: ^9.2.2
153
+ path_provider: ^2.1.4
154
+
155
+ # Networking
156
+ dio: ^5.7.0
157
+ dio_smart_retry: ^6.0.0
158
+ pretty_dio_logger: ^1.4.0
159
+
160
+ # Navigation
161
+ go_router: ^14.2.7
162
+
163
+ # Code generation models
164
+ freezed_annotation: ^2.4.4
165
+ json_annotation: ^4.9.0
166
+
167
+ # Firebase + notifications
168
+ firebase_core: ^3.6.0
169
+ firebase_messaging: ^15.1.3
170
+ flutter_local_notifications: ^17.2.3
171
+
172
+ # Media / assets
173
+ cached_network_image: ^3.4.1
174
+ flutter_svg: ^2.0.10+1
175
+
176
+ # UI extras
177
+ pull_to_refresh: ^2.0.0
178
+ font_awesome_flutter: ^10.7.0
179
+ material_design_icons_flutter: ^7.0.7296
180
+
181
+ # Utilities
182
+ intl: ^0.19.0
183
+ url_launcher: ^6.3.1
184
+ flutter_dotenv: ^5.2.1
185
+ package_info_plus: ^8.0.2
186
+
187
+ # [If Auth = Keycloak]
188
+ flutter_appauth: ^8.0.0+1
189
+ openid_client: ^0.4.9
190
+
191
+ # [If WebSocket = yes]
192
+ web_socket_channel: ^3.0.1
193
+
194
+ # [If Analytics = yes]
195
+ firebase_analytics: ^11.3.3
196
+
197
+ # [If Crashlytics = yes]
198
+ firebase_crashlytics: ^4.1.3
199
+
200
+ # [If ImagePicker = yes]
201
+ image_picker: ^1.1.2
202
+ permission_handler: ^11.3.1
203
+
204
+ # [If FilePicker = yes]
205
+ file_picker: ^8.1.2
206
+
207
+ # [If Sharing = yes]
208
+ share_plus: ^10.0.2
209
+
210
+ dev_dependencies:
211
+ flutter_test:
212
+ sdk: flutter
213
+ flutter_lints: ^4.0.0
214
+ build_runner: ^2.4.13
215
+ freezed: ^2.5.7
216
+ json_serializable: ^6.8.0
217
+ riverpod_generator: ^2.4.3
218
+ hive_generator: ^2.0.1
219
+ flutter_native_splash: ^2.4.1
220
+ mocktail: ^1.0.4
221
+
222
+ flutter:
223
+ uses-material-design: true
224
+ assets:
225
+ - .env.development
226
+ - .env.production
227
+ - assets/images/
228
+ - assets/icons/
229
+
230
+ fonts:
231
+ - family: {{PRIMARY_FONT_FAMILY}}
232
+ fonts:
233
+ - asset: assets/fonts/{{PRIMARY_FONT_FAMILY}}-Regular.ttf
234
+ - asset: assets/fonts/{{PRIMARY_FONT_FAMILY}}-Medium.ttf
235
+ weight: 500
236
+ - asset: assets/fonts/{{PRIMARY_FONT_FAMILY}}-Bold.ttf
237
+ weight: 700
238
+ ```
239
+
240
+ ### 2.2 analysis_options.yaml
241
+
242
+ ```yaml
243
+ include: package:flutter_lints/flutter.yaml
244
+
245
+ analyzer:
246
+ exclude:
247
+ - "**/*.g.dart"
248
+ - "**/*.freezed.dart"
249
+ - "**/generated_plugin_registrant.dart"
250
+ - "lib/generated/**"
251
+ language:
252
+ strict-casts: true
253
+ strict-inference: true
254
+ strict-raw-types: true
255
+
256
+ linter:
257
+ rules:
258
+ - prefer_const_constructors
259
+ - prefer_const_literals_to_create_immutables
260
+ - prefer_final_locals
261
+ - prefer_final_in_for_each
262
+ - avoid_print
263
+ - use_super_parameters
264
+ - require_trailing_commas
265
+ - sort_pub_dependencies
266
+ ```
267
+
268
+ ### 2.3 build_runner Scripts
269
+
270
+ ```bash
271
+ # One-shot code generation
272
+ dart run build_runner build --delete-conflicting-outputs
273
+
274
+ # Watch mode (rebuilds on save)
275
+ dart run build_runner watch --delete-conflicting-outputs
276
+
277
+ # Generate native splash screens (after editing pubspec config)
278
+ dart run flutter_native_splash:create
279
+ ```
280
+
281
+ ### 2.4 Environment Files
282
+
283
+ ```env
284
+ # .env.development
285
+ API_BASE_URL=http://10.0.2.2:{{BACKEND_PORT}}/api/v1
286
+ API_TIMEOUT_MS=30000
287
+ API_RETRY_ATTEMPTS=3
288
+
289
+ # [If Auth = Keycloak]
290
+ KEYCLOAK_ISSUER=http://10.0.2.2:8180/realms/{{KEYCLOAK_REALM}}
291
+ KEYCLOAK_CLIENT_ID={{KEYCLOAK_CLIENT_ID}}
292
+ KEYCLOAK_REDIRECT_URI={{APPLICATION_ID}}:/oauth2redirect
293
+ KEYCLOAK_POST_LOGOUT_REDIRECT_URI={{APPLICATION_ID}}:/oauth2redirect
294
+
295
+ # [If WebSocket = yes]
296
+ WS_URL=ws://10.0.2.2:{{BACKEND_PORT}}/ws
297
+ ```
298
+
299
+ ```env
300
+ # .env.production
301
+ API_BASE_URL=https://api.example.com/api/v1
302
+ API_TIMEOUT_MS=30000
303
+ API_RETRY_ATTEMPTS=3
304
+
305
+ # [If Auth = Keycloak]
306
+ KEYCLOAK_ISSUER=https://sso.example.com/realms/{{KEYCLOAK_REALM}}
307
+ KEYCLOAK_CLIENT_ID={{KEYCLOAK_CLIENT_ID}}
308
+ KEYCLOAK_REDIRECT_URI={{APPLICATION_ID}}:/oauth2redirect
309
+ KEYCLOAK_POST_LOGOUT_REDIRECT_URI={{APPLICATION_ID}}:/oauth2redirect
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Section 3: Application Configuration
315
+
316
+ ### 3.1 .gitignore (Flutter)
317
+
318
+ ```gitignore
319
+ # Miscellaneous
320
+ *.class
321
+ *.log
322
+ *.pyc
323
+ *.swp
324
+ .DS_Store
325
+ .atom/
326
+ .buildlog/
327
+ .history
328
+ .svn/
329
+ migrate_working_dir/
330
+
331
+ # IntelliJ / VSCode
332
+ .idea/
333
+ .vscode/
334
+ *.iml
335
+ *.ipr
336
+ *.iws
337
+
338
+ # Flutter / Dart / Pub-related
339
+ **/doc/api/
340
+ **/ios/Flutter/.last_build_id
341
+ .dart_tool/
342
+ .flutter-plugins
343
+ .flutter-plugins-dependencies
344
+ .packages
345
+ .pub-cache/
346
+ .pub/
347
+ /build/
348
+ /coverage/
349
+ pubspec.lock
350
+
351
+ # Symbolication
352
+ app.*.symbols
353
+ app.*.map.json
354
+
355
+ # Obfuscation
356
+ app.*.symbols
357
+
358
+ # Android
359
+ **/android/**/gradle-wrapper.jar
360
+ **/android/.gradle
361
+ **/android/captures/
362
+ **/android/gradlew
363
+ **/android/gradlew.bat
364
+ **/android/local.properties
365
+ **/android/**/GeneratedPluginRegistrant.java
366
+ **/android/key.properties
367
+ **/android/app/release/
368
+ **/android/app/google-services.json
369
+
370
+ # iOS
371
+ **/ios/**/*.mode1v3
372
+ **/ios/**/*.mode2v3
373
+ **/ios/**/*.moved-aside
374
+ **/ios/**/*.pbxuser
375
+ **/ios/**/*.perspectivev3
376
+ **/ios/**/Pods/
377
+ **/ios/**/.symlinks/
378
+ **/ios/**/profile
379
+ **/ios/**/xcuserdata
380
+ **/ios/.generated/
381
+ **/ios/Flutter/App.framework
382
+ **/ios/Flutter/Flutter.framework
383
+ **/ios/Flutter/Flutter.podspec
384
+ **/ios/Flutter/Generated.xcconfig
385
+ **/ios/Flutter/ephemeral
386
+ **/ios/Flutter/app.flx
387
+ **/ios/Flutter/app.zip
388
+ **/ios/Flutter/flutter_assets/
389
+ **/ios/Flutter/flutter_export_environment.sh
390
+ **/ios/ServiceDefinitions.json
391
+ **/ios/Runner/GeneratedPluginRegistrant.*
392
+ **/ios/Runner/GoogleService-Info.plist
393
+
394
+ # Environment (never commit secrets)
395
+ .env
396
+ .env.local
397
+ .env.development
398
+ .env.production
399
+ .env.*.local
400
+ ```
401
+
402
+ ### 3.2 main.dart
403
+
404
+ ```dart
405
+ import 'package:firebase_core/firebase_core.dart';
406
+ import 'package:flutter/foundation.dart';
407
+ import 'package:flutter/material.dart';
408
+ import 'package:flutter_dotenv/flutter_dotenv.dart';
409
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
410
+ import 'package:hive_flutter/hive_flutter.dart';
411
+
412
+ import 'firebase_options.dart';
413
+ import 'app.dart';
414
+ import 'core/local_storage/hive_setup.dart';
415
+ import 'core/notification/notification_service.dart';
416
+
417
+ @pragma('vm:entry-point')
418
+ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
419
+ await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
420
+ }
421
+
422
+ Future<void> main() async {
423
+ WidgetsFlutterBinding.ensureInitialized();
424
+
425
+ // Load environment-specific .env
426
+ final env = kReleaseMode ? '.env.production' : '.env.development';
427
+ await dotenv.load(fileName: env);
428
+
429
+ // Initialize Firebase
430
+ await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
431
+ FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
432
+
433
+ // Initialize Hive + register adapters
434
+ await HiveSetup.init();
435
+
436
+ // Initialize local notifications
437
+ await NotificationService.instance.init();
438
+
439
+ runApp(const ProviderScope(child: MyApp()));
440
+ }
441
+ ```
442
+
443
+ ### 3.3 Android Configuration Notes
444
+
445
+ ```text
446
+ android/app/build.gradle:
447
+ - applicationId "{{APPLICATION_ID}}"
448
+ - minSdkVersion {{MIN_ANDROID_SDK}}
449
+ - targetSdkVersion 34
450
+ - compileSdkVersion 34
451
+
452
+ android/app/src/main/AndroidManifest.xml:
453
+ - <uses-permission android:name="android.permission.INTERNET" />
454
+ - <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- Android 13+ -->
455
+ - [If ImagePicker = yes] <uses-permission android:name="android.permission.CAMERA" />
456
+ - Deep link intent filter for Keycloak callback:
457
+ <intent-filter android:autoVerify="true">
458
+ <action android:name="android.intent.action.VIEW"/>
459
+ <category android:name="android.intent.category.DEFAULT"/>
460
+ <category android:name="android.intent.category.BROWSABLE"/>
461
+ <data android:scheme="{{APPLICATION_ID}}"/>
462
+ </intent-filter>
463
+ - Default notification channel meta-data (FCM):
464
+ <meta-data
465
+ android:name="com.google.firebase.messaging.default_notification_channel_id"
466
+ android:value="default" />
467
+
468
+ android/app/google-services.json:
469
+ - Add file (gitignored — pulled from Firebase project)
470
+ - Add `apply plugin: 'com.google.gms.google-services'` at the bottom of build.gradle
471
+ - Add classpath in android/build.gradle: 'com.google.gms:google-services:4.4.2'
472
+ ```
473
+
474
+ ### 3.4 iOS Configuration Notes
475
+
476
+ ```text
477
+ ios/Runner/Info.plist:
478
+ - CFBundleURLTypes for Keycloak callback (URL scheme = {{APPLICATION_ID}})
479
+ - [If ImagePicker = yes] NSCameraUsageDescription, NSPhotoLibraryUsageDescription
480
+ - [If Permissions = yes] NSLocationWhenInUseUsageDescription (if location used)
481
+ - UIBackgroundModes: remote-notification (for FCM background delivery)
482
+
483
+ ios/Runner/AppDelegate.swift:
484
+ - Register for remote notifications and pass FCM token to Flutter
485
+ - Set `application.registerForRemoteNotifications()` and the
486
+ `UNUserNotificationCenter.current().delegate = self`
487
+
488
+ ios/Runner/GoogleService-Info.plist:
489
+ - Add file (gitignored — pulled from Firebase project)
490
+ - Drag into Runner target in Xcode
491
+
492
+ ios/Podfile:
493
+ - platform :ios, '{{MIN_IOS_VERSION}}'
494
+ - post_install hook adds GCC_PREPROCESSOR_DEFINITIONS for flutter_local_notifications
495
+ ```
496
+
497
+ ### 3.5 flutter_native_splash Configuration (pubspec.yaml)
498
+
499
+ ```yaml
500
+ flutter_native_splash:
501
+ color: "{{PAGE_BACKGROUND_HEX}}"
502
+ image: assets/images/splash.png
503
+ color_dark: "#121212"
504
+ image_dark: assets/images/splash_dark.png
505
+ android_12:
506
+ image: assets/images/splash_android12.png
507
+ icon_background_color: "{{PRIMARY_COLOR_HEX}}"
508
+ ios: true
509
+ android: true
510
+ web: false
511
+ ```
512
+
513
+ Run: `dart run flutter_native_splash:create`
514
+
515
+ ---
516
+
517
+ ## Section 4: Directory Structure
518
+
519
+ Generate the full `lib/` directory tree. Use actual module names from PRD.md and MODEL.md.
520
+ Follow the feature-based structure from `references/routing-patterns.md`.
521
+
522
+ ```
523
+ lib/
524
+ ├── main.dart
525
+ ├── app.dart # MaterialApp.router + theme + locale
526
+ ├── firebase_options.dart # Generated by `flutterfire configure`
527
+ ├── router/
528
+ │ └── app_router.dart # go_router configuration
529
+ ├── core/
530
+ │ ├── api/
531
+ │ │ ├── dio_provider.dart # Dio instance + interceptors
532
+ │ │ ├── api_failure.dart # Sealed-class error type
533
+ │ │ └── auth_interceptor.dart # [If Auth != none] Bearer token injector
534
+ │ ├── auth/
535
+ │ │ ├── auth_repository.dart # signIn/signOut/refresh
536
+ │ │ ├── auth_provider.dart # Riverpod auth state
537
+ │ │ ├── auth_state.dart # Sealed-class auth state (freezed)
538
+ │ │ └── roles.dart # Role constants
539
+ │ ├── local_storage/
540
+ │ │ ├── hive_setup.dart # Hive.initFlutter + adapter registration
541
+ │ │ ├── secure_storage_provider.dart # flutter_secure_storage wrapper
542
+ │ │ └── box_provider.dart # Typed Box<T> providers
543
+ │ ├── notification/
544
+ │ │ ├── notification_service.dart # FCM + local notifications init
545
+ │ │ ├── notification_handler.dart # Foreground / background / tap handlers
546
+ │ │ └── notification_navigation.dart # Routes deep-linked from notifications
547
+ │ ├── theme/
548
+ │ │ ├── app_theme.dart # lightTheme + darkTheme
549
+ │ │ ├── app_colors.dart # Color tokens from mockup
550
+ │ │ └── app_typography.dart # TextTheme tokens
551
+ │ ├── env/
552
+ │ │ └── env_config.dart # Typed accessor over dotenv
553
+ │ └── utils/
554
+ │ ├── validators.dart # Form field validators
555
+ │ ├── formatters.dart # intl-based formatters
556
+ │ └── url_helper.dart # url_launcher wrappers
557
+ ├── shared/
558
+ │ ├── widgets/
559
+ │ │ ├── app_scaffold.dart
560
+ │ │ ├── loading_indicator.dart
561
+ │ │ ├── empty_state.dart
562
+ │ │ ├── error_state.dart
563
+ │ │ ├── confirm_dialog.dart
564
+ │ │ ├── status_chip.dart
565
+ │ │ ├── search_bar_field.dart
566
+ │ │ ├── pull_to_refresh_list.dart
567
+ │ │ ├── infinite_scroll_list.dart
568
+ │ │ ├── app_network_image.dart # Wraps cached_network_image
569
+ │ │ ├── cached_svg_icon.dart # Wraps flutter_svg
570
+ │ │ └── shell_screen.dart # AppShell with bottom navigation
571
+ │ ├── form/
572
+ │ │ ├── form_text_field.dart
573
+ │ │ ├── form_dropdown_field.dart
574
+ │ │ ├── form_date_picker_field.dart
575
+ │ │ ├── form_switch_field.dart
576
+ │ │ └── form_image_picker_field.dart # [If ImagePicker = yes]
577
+ │ └── navigation/
578
+ │ └── nav_config.dart # Bottom-nav / drawer items
579
+ ├── features/
580
+ │ ├── auth/ # [If Auth != none]
581
+ │ │ ├── presentation/
582
+ │ │ │ ├── login_screen.dart # [If Auth = Local]
583
+ │ │ │ └── auth_callback_screen.dart # [If Auth = Keycloak]
584
+ │ │ └── auth.dart # Barrel
585
+ │ ├── dashboard/
586
+ │ │ ├── presentation/
587
+ │ │ │ └── dashboard_screen.dart
588
+ │ │ └── dashboard.dart
589
+ │ ├── {{module1}}/
590
+ │ │ ├── data/
591
+ │ │ │ ├── {{module1}}_repository.dart
592
+ │ │ │ └── {{module1}}_remote_data_source.dart
593
+ │ │ ├── domain/
594
+ │ │ │ └── {{module1}}.dart # @freezed model + Hive adapter
595
+ │ │ ├── application/
596
+ │ │ │ └── {{module1}}_providers.dart # Riverpod providers (generated)
597
+ │ │ ├── presentation/
598
+ │ │ │ ├── {{module1}}_list_screen.dart
599
+ │ │ │ ├── {{module1}}_detail_screen.dart
600
+ │ │ │ └── {{module1}}_form_screen.dart
601
+ │ │ └── {{module1}}.dart # Barrel
602
+ │ └── {{module2}}/
603
+ │ └── (same structure)
604
+ └── l10n/ # [If i18n = yes]
605
+ ├── app_en.arb
606
+ ├── app_ms.arb
607
+ └── intl_helper.dart
608
+ ```
609
+
610
+ ---
611
+
612
+ ## Section 5: Theme Configuration
613
+
614
+ Generate the complete Material 3 `ThemeData` using design tokens extracted from MOCKUP.html.
615
+ Reference `references/component-patterns.md` for the theme setup pattern.
616
+
617
+ Include:
618
+ - `lib/core/theme/app_colors.dart` — color tokens with actual hex values from mockup
619
+ - `lib/core/theme/app_typography.dart` — TextTheme tokens from mockup
620
+ - `lib/core/theme/app_theme.dart` — `ThemeData lightTheme` + `ThemeData darkTheme` factory,
621
+ component themes (ElevatedButtonTheme, AppBarTheme, CardTheme, InputDecorationTheme)
622
+ - Design token mapping table showing mockup CSS property → ThemeData property
623
+
624
+ **app.dart integration:**
625
+
626
+ ```dart
627
+ import 'package:flutter/material.dart';
628
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
629
+
630
+ import 'core/theme/app_theme.dart';
631
+ import 'router/app_router.dart';
632
+ import 'shared/providers/theme_mode_provider.dart';
633
+
634
+ class MyApp extends ConsumerWidget {
635
+ const MyApp({super.key});
636
+
637
+ @override
638
+ Widget build(BuildContext context, WidgetRef ref) {
639
+ final themeMode = ref.watch(themeModeProvider);
640
+ final router = ref.watch(appRouterProvider);
641
+
642
+ return MaterialApp.router(
643
+ title: '{{APP_TITLE}}',
644
+ debugShowCheckedModeBanner: false,
645
+ theme: AppTheme.light(),
646
+ darkTheme: AppTheme.dark(),
647
+ themeMode: themeMode,
648
+ routerConfig: router,
649
+ // [If i18n = yes]
650
+ // localizationsDelegates: AppLocalizations.localizationsDelegates,
651
+ // supportedLocales: AppLocalizations.supportedLocales,
652
+ );
653
+ }
654
+ }
655
+ ```
656
+
657
+ ---
658
+
659
+ ## Section 6: Authentication Configuration [CONDITIONAL — Include only if Auth != none]
660
+
661
+ **[If Auth = Keycloak]:**
662
+ Refer to `references/security-patterns.md` for the complete content.
663
+ Must cover:
664
+ - `lib/core/auth/auth_repository.dart` — wraps `FlutterAppAuth` for sign-in/refresh
665
+ - `lib/core/auth/auth_state.dart` — sealed-class auth state via freezed
666
+ - `lib/core/auth/auth_provider.dart` — `AuthNotifier extends AsyncNotifier<AuthState>`
667
+ - `lib/core/auth/roles.dart` — role constants
668
+ - `lib/core/api/auth_interceptor.dart` — Dio interceptor that injects Bearer token
669
+ - Token persistence in `flutter_secure_storage`
670
+ - AppAuth URL scheme registration (Android `AndroidManifest.xml` + iOS `Info.plist`)
671
+ - Auth callback handled natively by `flutter_appauth` (no Flutter route needed)
672
+ - Silent token refresh logic on app startup
673
+
674
+ **[If Auth = Local]:**
675
+ Refer to `references/security-patterns.md` (Local JWT section) for the complete content.
676
+ Must cover:
677
+ - `lib/features/auth/presentation/login_screen.dart` — email/password form
678
+ - `lib/core/auth/auth_repository.dart` — POST `/auth/login`, `/auth/refresh`
679
+ - `lib/core/auth/auth_provider.dart` — Riverpod auth state with login/logout actions
680
+ - `lib/core/api/auth_interceptor.dart` — reads token from `flutter_secure_storage`,
681
+ refreshes on 401
682
+ - go_router `redirect` guard that pushes `/login` when unauthenticated
683
+
684
+ ---
685
+
686
+ ## Section 7: Router Configuration
687
+
688
+ Refer to `references/routing-patterns.md` for the complete route tree.
689
+ Must cover:
690
+ - `lib/router/app_router.dart` — complete `GoRouter(...)` with ALL routes from PRD.md
691
+ - `ShellRoute` for the bottom-navigation scaffold
692
+ - `redirect` callback for auth + role enforcement
693
+ - Deep link routes (e.g., `/notifications/:id`) for FCM payload navigation
694
+ - The exact module-based URL paths (e.g., `/task`, NOT `/worker/task`)
695
+ - 404 and unauthorized routes
696
+
697
+ ---
698
+
699
+ ## Section 8: API Client Configuration
700
+
701
+ ```dart
702
+ // lib/core/api/dio_provider.dart
703
+ import 'package:dio/dio.dart';
704
+ import 'package:dio_smart_retry/dio_smart_retry.dart';
705
+ import 'package:flutter/foundation.dart';
706
+ import 'package:flutter_dotenv/flutter_dotenv.dart';
707
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
708
+ import 'package:pretty_dio_logger/pretty_dio_logger.dart';
709
+ import 'package:riverpod_annotation/riverpod_annotation.dart';
710
+
711
+ import 'auth_interceptor.dart';
712
+
713
+ part 'dio_provider.g.dart';
714
+
715
+ @Riverpod(keepAlive: true)
716
+ Dio dio(DioRef ref) {
717
+ final dio = Dio(
718
+ BaseOptions(
719
+ baseUrl: dotenv.env['API_BASE_URL']!,
720
+ connectTimeout: Duration(
721
+ milliseconds: int.parse(dotenv.env['API_TIMEOUT_MS'] ?? '30000'),
722
+ ),
723
+ receiveTimeout: Duration(
724
+ milliseconds: int.parse(dotenv.env['API_TIMEOUT_MS'] ?? '30000'),
725
+ ),
726
+ headers: {'Content-Type': 'application/json'},
727
+ ),
728
+ );
729
+
730
+ // Auth interceptor — see Section 6
731
+ dio.interceptors.add(AuthInterceptor(ref));
732
+
733
+ // Smart retry — exponential backoff on 5xx and network errors,
734
+ // only for idempotent methods (GET, PUT, DELETE)
735
+ dio.interceptors.add(
736
+ RetryInterceptor(
737
+ dio: dio,
738
+ retries: int.parse(dotenv.env['API_RETRY_ATTEMPTS'] ?? '3'),
739
+ retryDelays: const [
740
+ Duration(seconds: 1),
741
+ Duration(seconds: 2),
742
+ Duration(seconds: 4),
743
+ ],
744
+ retryableExtraStatuses: const {502, 503, 504},
745
+ ),
746
+ );
747
+
748
+ // Verbose logging — dev only
749
+ if (kDebugMode) {
750
+ dio.interceptors.add(
751
+ PrettyDioLogger(
752
+ requestHeader: true,
753
+ requestBody: true,
754
+ responseBody: true,
755
+ responseHeader: false,
756
+ compact: false,
757
+ ),
758
+ );
759
+ }
760
+
761
+ return dio;
762
+ }
763
+ ```
764
+
765
+ ```dart
766
+ // lib/core/api/api_failure.dart
767
+ import 'package:dio/dio.dart';
768
+ import 'package:freezed_annotation/freezed_annotation.dart';
769
+
770
+ part 'api_failure.freezed.dart';
771
+
772
+ @freezed
773
+ sealed class ApiFailure with _$ApiFailure {
774
+ const factory ApiFailure.network() = _Network;
775
+ const factory ApiFailure.unauthorized() = _Unauthorized;
776
+ const factory ApiFailure.forbidden() = _Forbidden;
777
+ const factory ApiFailure.notFound() = _NotFound;
778
+ const factory ApiFailure.server({int? status, String? message}) = _Server;
779
+ const factory ApiFailure.unknown(Object error) = _Unknown;
780
+
781
+ factory ApiFailure.fromDioException(DioException error) {
782
+ if (error.type == DioExceptionType.connectionTimeout ||
783
+ error.type == DioExceptionType.receiveTimeout ||
784
+ error.type == DioExceptionType.sendTimeout ||
785
+ error.type == DioExceptionType.connectionError) {
786
+ return const ApiFailure.network();
787
+ }
788
+ final status = error.response?.statusCode ?? 0;
789
+ if (status == 401) return const ApiFailure.unauthorized();
790
+ if (status == 403) return const ApiFailure.forbidden();
791
+ if (status == 404) return const ApiFailure.notFound();
792
+ if (status >= 500) {
793
+ final body = error.response?.data;
794
+ final message = body is Map<String, dynamic> ? body['message'] as String? : null;
795
+ return ApiFailure.server(status: status, message: message);
796
+ }
797
+ return ApiFailure.unknown(error);
798
+ }
799
+ }
800
+ ```
801
+
802
+ ---
803
+
804
+ ## Section 9: Local Storage (Hive)
805
+
806
+ Refer to `references/storage-patterns.md`. Cover:
807
+ - `lib/core/local_storage/hive_setup.dart` — `Hive.initFlutter()`, register adapters,
808
+ open boxes (`settings`, `user_profile`, `<module>_cache`)
809
+ - `lib/core/local_storage/secure_storage_provider.dart` — `FlutterSecureStorage`
810
+ wrapper exposed as Riverpod provider
811
+ - `lib/core/local_storage/box_provider.dart` — typed `Box<T>` providers
812
+ - Encryption pattern using `HiveAesCipher` with key from secure storage
813
+ - Migration strategy on schema changes (clear-and-refetch vs. lazy migration)
814
+
815
+ ---
816
+
817
+ ## Section 10: Riverpod Setup
818
+
819
+ Refer to `references/state-patterns.md`. Cover:
820
+ - `ProviderScope` wrapping `MyApp` in `main.dart`
821
+ - `riverpod_generator` configuration (`build.yaml` snippet)
822
+ - Provider naming conventions (`<Entity>RepositoryProvider`, `<Entity>ListProvider`,
823
+ `<Entity>ByIdProvider(id)`, `<Entity>FormControllerProvider`)
824
+ - `AsyncNotifier` / `AsyncNotifierProvider.family` patterns
825
+ - Side-effect mutation pattern (`Notifier` exposing `create()`, `update()`, `delete()`)
826
+ - Cache invalidation via `ref.invalidate(...)` and `ref.refresh(...)`
827
+ - Listening pattern in widgets via `ref.watch(...)` + `AsyncValue.when(...)`
828
+
829
+ ---
830
+
831
+ ## Section 11: Shared Layouts
832
+
833
+ For each shell, provide:
834
+ 1. The constructor signature
835
+ 2. The complete widget
836
+
837
+ - **AppShellScreen**: `Scaffold` with `body` from `ShellRoute` outlet and `BottomNavigationBar`
838
+ driven by `nav_config.dart` (role-filtered)
839
+ - **AuthShell**: Centered `Scaffold` for login / forgot-password / Keycloak callback
840
+ - **PublicShell**: Minimal scaffold for unauthenticated content (if any)
841
+
842
+ ---
843
+
844
+ ## Section 12: Shared Widgets
845
+
846
+ For each shared widget, provide the constructor signature and complete Dart code.
847
+ Reference `references/component-patterns.md` for implementations.
848
+
849
+ | Widget | Purpose |
850
+ |---|---|
851
+ | `AppScaffold` | Standard AppBar + body + optional FAB |
852
+ | `LoadingIndicator` | Full-screen CircularProgressIndicator |
853
+ | `EmptyState` | Empty list placeholder with icon and message |
854
+ | `ErrorState` | Full-screen error with retry button |
855
+ | `ConfirmDialog` | `showDialog`-based confirmation |
856
+ | `StatusChip` | Colored Chip for enum status values |
857
+ | `SearchBarField` | Debounced TextField for searches |
858
+ | `PullToRefreshList` | `SmartRefresher` + `ListView` wrapper |
859
+ | `InfiniteScrollList` | ScrollController-driven pagination wrapper |
860
+ | `AppNetworkImage` | `CachedNetworkImage` with placeholder + error fallback |
861
+ | `CachedSvgIcon` | `SvgPicture.asset` / `.network` with cache |
862
+
863
+ ---
864
+
865
+ ## Section 13: Navigation Configuration
866
+
867
+ Generate `lib/shared/navigation/nav_config.dart` with the actual navigation items
868
+ from the mockup nav files. For each role:
869
+ - List all nav items that role can see
870
+ - Include the icon (mix of `Icons.*`, `FontAwesomeIcons.*`, `MdiIcons.*` as appropriate)
871
+ - Bottom-nav routes use module-based paths only (e.g., `/task`, `/profile`)
872
+
873
+ ```dart
874
+ // lib/shared/navigation/nav_config.dart
875
+ import 'package:flutter/material.dart';
876
+ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
877
+ import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
878
+
879
+ import '../../core/auth/roles.dart';
880
+
881
+ class NavItem {
882
+ const NavItem({
883
+ required this.label,
884
+ required this.path,
885
+ required this.icon,
886
+ required this.activeIcon,
887
+ this.requiredRoles,
888
+ });
889
+
890
+ final String label;
891
+ final String path;
892
+ final IconData icon;
893
+ final IconData activeIcon;
894
+ /// If null, accessible to all authenticated users
895
+ final List<String>? requiredRoles;
896
+ }
897
+
898
+ const navigationItems = <NavItem>[
899
+ NavItem(
900
+ label: 'Dashboard',
901
+ path: '/dashboard',
902
+ icon: Icons.dashboard_outlined,
903
+ activeIcon: Icons.dashboard,
904
+ ),
905
+ NavItem(
906
+ label: 'Tasks',
907
+ path: '/task',
908
+ icon: MdiIcons.formatListChecks,
909
+ activeIcon: MdiIcons.formatListChecks,
910
+ requiredRoles: [Roles.worker, Roles.admin],
911
+ ),
912
+ NavItem(
913
+ label: 'Profile',
914
+ path: '/profile',
915
+ icon: FontAwesomeIcons.user,
916
+ activeIcon: FontAwesomeIcons.userLarge,
917
+ ),
918
+ ];
919
+ ```
920
+
921
+ ---
922
+
923
+ ## Section 14: Form Infrastructure
924
+
925
+ Reference `references/component-patterns.md` for the complete reusable form-field widgets.
926
+ Describe:
927
+ - `Form` + `GlobalKey<FormState>` pattern
928
+ - All `Form*Field` widgets with full constructor signatures
929
+ - Reset pattern for edit modes (re-initialize controllers in `initState` / `didUpdateWidget`)
930
+ - Loading state on submit button (replace text with `CircularProgressIndicator`)
931
+
932
+ ```dart
933
+ // lib/core/utils/validators.dart
934
+ class Validators {
935
+ Validators._();
936
+
937
+ static String? required(String? value, {String label = 'This field'}) {
938
+ if (value == null || value.trim().isEmpty) return '$label is required';
939
+ return null;
940
+ }
941
+
942
+ static String? minLength(String? value, int min, {String label = 'This field'}) {
943
+ if (value == null) return null;
944
+ if (value.length < min) return '$label must be at least $min characters';
945
+ return null;
946
+ }
947
+
948
+ static String? maxLength(String? value, int max, {String label = 'This field'}) {
949
+ if (value == null) return null;
950
+ if (value.length > max) return '$label must be at most $max characters';
951
+ return null;
952
+ }
953
+
954
+ static String? email(String? value) {
955
+ if (value == null || value.isEmpty) return null;
956
+ final pattern = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$');
957
+ if (!pattern.hasMatch(value)) return 'Enter a valid email address';
958
+ return null;
959
+ }
960
+
961
+ static String? url(String? value) {
962
+ if (value == null || value.isEmpty) return null;
963
+ final parsed = Uri.tryParse(value);
964
+ if (parsed == null || !(parsed.isScheme('http') || parsed.isScheme('https'))) {
965
+ return 'Enter a valid URL';
966
+ }
967
+ return null;
968
+ }
969
+
970
+ static FormFieldValidator<String> compose(List<FormFieldValidator<String>> validators) {
971
+ return (value) {
972
+ for (final v in validators) {
973
+ final result = v(value);
974
+ if (result != null) return result;
975
+ }
976
+ return null;
977
+ };
978
+ }
979
+ }
980
+ ```
981
+
982
+ ---
983
+
984
+ ## Section 15: Error Handling Strategy
985
+
986
+ Describe:
987
+ - `FlutterError.onError` + `PlatformDispatcher.instance.onError` in `main.dart`
988
+ - Optional Crashlytics forwarding: `FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;`
989
+ - Dio response interceptor mapping `DioException` → `ApiFailure`
990
+ - Riverpod `AsyncValue.when(loading: ..., error: ..., data: ...)` pattern in screens
991
+ - SnackBar via `ScaffoldMessenger` for transient feedback
992
+
993
+ ```dart
994
+ // lib/shared/widgets/error_state.dart
995
+ import 'package:flutter/material.dart';
996
+
997
+ import '../../core/api/api_failure.dart';
998
+
999
+ class ErrorState extends StatelessWidget {
1000
+ const ErrorState({super.key, required this.error, this.onRetry});
1001
+
1002
+ final Object error;
1003
+ final VoidCallback? onRetry;
1004
+
1005
+ String _messageFor(Object error) {
1006
+ if (error is ApiFailure) {
1007
+ return switch (error) {
1008
+ ApiFailure(:final _) when error is _Network => 'No internet connection. Please try again.',
1009
+ ApiFailure() when error is _Unauthorized => 'Your session has expired. Please sign in again.',
1010
+ ApiFailure() when error is _Forbidden => 'You do not have permission to perform this action.',
1011
+ ApiFailure() when error is _NotFound => 'The requested resource was not found.',
1012
+ ApiFailure() when error is _Server => 'A server error occurred. Please try again later.',
1013
+ _ => 'An unexpected error occurred.',
1014
+ };
1015
+ }
1016
+ return error.toString();
1017
+ }
1018
+
1019
+ @override
1020
+ Widget build(BuildContext context) {
1021
+ return Center(
1022
+ child: Padding(
1023
+ padding: const EdgeInsets.all(24),
1024
+ child: Column(
1025
+ mainAxisSize: MainAxisSize.min,
1026
+ children: [
1027
+ Icon(
1028
+ Icons.error_outline,
1029
+ size: 56,
1030
+ color: Theme.of(context).colorScheme.error,
1031
+ ),
1032
+ const SizedBox(height: 12),
1033
+ Text(
1034
+ _messageFor(error),
1035
+ style: Theme.of(context).textTheme.bodyLarge,
1036
+ textAlign: TextAlign.center,
1037
+ ),
1038
+ if (onRetry != null) ...[
1039
+ const SizedBox(height: 16),
1040
+ FilledButton.icon(
1041
+ onPressed: onRetry,
1042
+ icon: const Icon(Icons.refresh),
1043
+ label: const Text('Retry'),
1044
+ ),
1045
+ ],
1046
+ ],
1047
+ ),
1048
+ ),
1049
+ );
1050
+ }
1051
+ }
1052
+ ```
1053
+
1054
+ ---
1055
+
1056
+ ## Section 16: Notification System
1057
+
1058
+ Refer to `references/notification-patterns.md` for the full FCM + flutter_local_notifications
1059
+ architecture. Cover:
1060
+ - `NotificationService.init()` lifecycle
1061
+ - Permission request flow (iOS + Android 13+)
1062
+ - Foreground / background / terminated handlers
1063
+ - Local notification display for foreground FCM messages
1064
+ - Deep link from notification tap → `router.go('/...')`
1065
+ - FCM token registration with backend on first launch + refresh
1066
+ - Topic subscription (if PRD describes topic-based broadcasts)
1067
+ - Scheduled local notifications (`zonedSchedule`) for in-app reminders
1068
+
1069
+ ---
1070
+
1071
+ ## Section 17: Theming & Dark Mode
1072
+
1073
+ ```dart
1074
+ // lib/shared/providers/theme_mode_provider.dart
1075
+ import 'package:flutter/material.dart';
1076
+ import 'package:hive_flutter/hive_flutter.dart';
1077
+ import 'package:riverpod_annotation/riverpod_annotation.dart';
1078
+
1079
+ part 'theme_mode_provider.g.dart';
1080
+
1081
+ const _settingsBox = 'settings';
1082
+ const _themeModeKey = 'themeMode';
1083
+
1084
+ @Riverpod(keepAlive: true)
1085
+ class ThemeModeController extends _$ThemeModeController {
1086
+ late Box _box;
1087
+
1088
+ @override
1089
+ ThemeMode build() {
1090
+ _box = Hive.box(_settingsBox);
1091
+ final stored = _box.get(_themeModeKey) as String?;
1092
+ return switch (stored) {
1093
+ 'dark' => ThemeMode.dark,
1094
+ 'light' => ThemeMode.light,
1095
+ _ => ThemeMode.system,
1096
+ };
1097
+ }
1098
+
1099
+ Future<void> toggle() async {
1100
+ final next = switch (state) {
1101
+ ThemeMode.light => ThemeMode.dark,
1102
+ ThemeMode.dark => ThemeMode.light,
1103
+ ThemeMode.system => ThemeMode.dark,
1104
+ };
1105
+ await _box.put(_themeModeKey, next.name);
1106
+ state = next;
1107
+ }
1108
+ }
1109
+ ```
1110
+
1111
+ ---
1112
+
1113
+ ## Section 18: Testing Strategy
1114
+
1115
+ Overview only — detailed per-feature test patterns in each module's SPEC.md.
1116
+
1117
+ - **Unit tests**: Pure Dart logic (validators, formatters, repository mappers)
1118
+ - **Provider tests**: Use `ProviderContainer` with overrides (mock repositories)
1119
+ - **Widget tests**: `flutter_test` + `ProviderScope` overrides
1120
+ - **Integration tests**: `integration_test` package for end-to-end flows
1121
+ - **Mocking**: `mocktail` (preferred — no codegen) for repository / Dio mocks
1122
+
1123
+ ```yaml
1124
+ # pubspec.yaml — add under dev_dependencies if not present
1125
+ integration_test:
1126
+ sdk: flutter
1127
+ ```
1128
+
1129
+ ---
1130
+
1131
+ ## Section 19: Build & Distribution
1132
+
1133
+ ### Android Release Build
1134
+
1135
+ ```bash
1136
+ # Generate keystore (one-time)
1137
+ keytool -genkey -v -keystore upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
1138
+
1139
+ # Configure ~/.gradle/gradle.properties:
1140
+ # RELEASE_STORE_FILE=/path/to/upload-keystore.jks
1141
+ # RELEASE_STORE_PASSWORD=...
1142
+ # RELEASE_KEY_ALIAS=upload
1143
+ # RELEASE_KEY_PASSWORD=...
1144
+
1145
+ flutter build appbundle --release # Google Play
1146
+ flutter build apk --release --split-per-abi # Sideload
1147
+ ```
1148
+
1149
+ ### iOS Release Build
1150
+
1151
+ ```bash
1152
+ flutter build ipa --release
1153
+ # Or open ios/Runner.xcworkspace in Xcode and Archive
1154
+ ```
1155
+
1156
+ ### Native Splash & Icons
1157
+
1158
+ ```bash
1159
+ dart run flutter_native_splash:create
1160
+ # Optional launcher icons (add flutter_launcher_icons dev dependency)
1161
+ dart run flutter_launcher_icons
1162
+ ```
1163
+
1164
+ ### ProGuard Rules (Android Release)
1165
+
1166
+ ```text
1167
+ # android/app/proguard-rules.pro
1168
+
1169
+ # Firebase
1170
+ -keep class com.google.firebase.** { *; }
1171
+ -keep class com.google.android.gms.** { *; }
1172
+
1173
+ # AppAuth (Keycloak)
1174
+ -keep class net.openid.appauth.** { *; }
1175
+
1176
+ # flutter_local_notifications
1177
+ -keep class com.dexterous.** { *; }
1178
+
1179
+ # Keep Dio generic types
1180
+ -keepattributes Signature
1181
+ ```
1182
+
1183
+ ---
1184
+
1185
+ ## Section 20: Internationalisation [CONDITIONAL — Include only if i18n = yes]
1186
+
1187
+ ```yaml
1188
+ # pubspec.yaml
1189
+ flutter:
1190
+ generate: true
1191
+ ```
1192
+
1193
+ ```yaml
1194
+ # l10n.yaml (project root)
1195
+ arb-dir: lib/l10n
1196
+ template-arb-file: app_en.arb
1197
+ output-localization-file: app_localizations.dart
1198
+ synthetic-package: false
1199
+ output-dir: lib/generated
1200
+ ```
1201
+
1202
+ ```json
1203
+ // lib/l10n/app_en.arb
1204
+ {
1205
+ "@@locale": "en",
1206
+ "appTitle": "{{APP_TITLE}}",
1207
+ "actionRetry": "Retry",
1208
+ "errorGeneric": "An unexpected error occurred."
1209
+ }
1210
+ ```
1211
+
1212
+ Wire `MaterialApp.router` localization delegates (already shown in Section 5) and add
1213
+ a locale-switch provider persisted in Hive `settings` box.
1214
+
1215
+ ---
1216
+
1217
+ ## Section 21: WebSocket Integration [CONDITIONAL — Include only if WebSocket = yes]
1218
+
1219
+ ```dart
1220
+ // lib/core/realtime/socket_provider.dart
1221
+ import 'dart:async';
1222
+
1223
+ import 'package:flutter_dotenv/flutter_dotenv.dart';
1224
+ import 'package:riverpod_annotation/riverpod_annotation.dart';
1225
+ import 'package:web_socket_channel/web_socket_channel.dart';
1226
+
1227
+ part 'socket_provider.g.dart';
1228
+
1229
+ @Riverpod(keepAlive: true)
1230
+ Stream<dynamic> socketEvents(SocketEventsRef ref) async* {
1231
+ final url = dotenv.env['WS_URL']!;
1232
+ while (true) {
1233
+ final channel = WebSocketChannel.connect(Uri.parse(url));
1234
+ try {
1235
+ ref.onDispose(channel.sink.close);
1236
+ await for (final event in channel.stream) {
1237
+ yield event;
1238
+ }
1239
+ } catch (_) {
1240
+ // Backoff before reconnect
1241
+ await Future<void>.delayed(const Duration(seconds: 3));
1242
+ }
1243
+ }
1244
+ }
1245
+ ```
1246
+
1247
+ ---
1248
+
1249
+ ## Section 22: Firebase Initialization
1250
+
1251
+ ```bash
1252
+ # One-time CLI flow during scaffolding
1253
+ dart pub global activate flutterfire_cli
1254
+ flutterfire configure --project={{FIREBASE_PROJECT_ID}}
1255
+ ```
1256
+
1257
+ This produces `lib/firebase_options.dart` and downloads `google-services.json` /
1258
+ `GoogleService-Info.plist` automatically. Both native files must be gitignored.
1259
+
1260
+ ```dart
1261
+ // main.dart excerpt (already shown above)
1262
+ await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
1263
+ FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
1264
+ ```
1265
+
1266
+ ---
1267
+
1268
+ # Part B: Per-Module SPEC.md Template
1269
+
1270
+ Each module from PRD.md and MODEL.md gets its own `<module-name>/SPEC.md` file.
1271
+
1272
+ ---
1273
+
1274
+ ## Module SPEC.md Structure
1275
+
1276
+ ```markdown
1277
+ # {{ModuleName}} — Module Specification
1278
+
1279
+ > Part of [{{APPLICATION_NAME}} Technical Specification](../SPECIFICATION.md)
1280
+
1281
+ ## Overview
1282
+
1283
+ **Module:** {{ModuleName}}
1284
+ **Feature Path:** `lib/features/{{moduleName}}/`
1285
+ **Type:** {{System Module | Business Module}}
1286
+ **Backend Endpoint Base:** `/{{api-path}}` (inferred from model/API context)
1287
+ **Cache Box:** `{{moduleName}}_cache` (if module is cached offline)
1288
+ ```
1289
+
1290
+ ---
1291
+
1292
+ ### Traceability
1293
+
1294
+ ```markdown
1295
+ ## Traceability
1296
+
1297
+ ### User Stories
1298
+ | ID | Version | Description |
1299
+ |---|---|---|
1300
+ | USA000030 | v1.0.0 | Create/update task |
1301
+ | USA000033 | v1.0.0 | View list of tasks |
1302
+
1303
+ ### Non-Functional Requirements
1304
+ | ID | Version | Description |
1305
+ |---|---|---|
1306
+ | NFRA00021 | v1.0.0 | Task list paginated with 20 items per page |
1307
+ | NFRA00024 | v1.0.0 | Task title max 100 characters |
1308
+
1309
+ ### Constraints
1310
+ | ID | Version | Description |
1311
+ |---|---|---|
1312
+ | CONSA0012 | v1.0.0 | Status values: DRAFT, IN_PROGRESS, COMPLETED |
1313
+
1314
+ ### Removed / Replaced
1315
+ | ID | Removed In | Replaced By | Reason |
1316
+ |---|---|---|---|
1317
+ | CONSA0012 (READY status) | v1.0.4 | — | Auto-computed status; READY removed |
1318
+ _or "None." if no removals_
1319
+
1320
+ ### Data Sources
1321
+ | Artifact | Reference |
1322
+ |---|---|
1323
+ | DB Table | `task` |
1324
+ | Mockup Screen | `worker/task/task-list.html` |
1325
+ ```
1326
+
1327
+ ---
1328
+
1329
+ ### Freezed Model
1330
+
1331
+ ```markdown
1332
+ ## Freezed Model
1333
+
1334
+ File: `lib/features/{{moduleName}}/domain/{{moduleName}}.dart`
1335
+
1336
+ (Complete @freezed data class — field-for-field from MODEL.md, with fromJson/toJson and
1337
+ optional Hive adapter annotations if module is cached offline)
1338
+ ```
1339
+
1340
+ All enum values must match PRD.md constraints exactly. Include the generic
1341
+ `PaginatedResponse<T>` type in `lib/core/api/` (shared), not per-module.
1342
+
1343
+ ```dart
1344
+ // Example
1345
+ import 'package:freezed_annotation/freezed_annotation.dart';
1346
+ import 'package:hive/hive.dart';
1347
+
1348
+ part 'task.freezed.dart';
1349
+ part 'task.g.dart';
1350
+
1351
+ enum TaskStatus {
1352
+ @JsonValue('DRAFT') draft,
1353
+ @JsonValue('IN_PROGRESS') inProgress,
1354
+ @JsonValue('COMPLETED') completed,
1355
+ }
1356
+
1357
+ @freezed
1358
+ @HiveType(typeId: 1)
1359
+ class Task with _$Task {
1360
+ @HiveField(0) const factory Task({
1361
+ @HiveField(0) required String id,
1362
+ @HiveField(1) required String title,
1363
+ @HiveField(2) String? description,
1364
+ @HiveField(3) required TaskStatus status,
1365
+ @HiveField(4) required DateTime dueDate,
1366
+ @HiveField(5) required DateTime createdAt,
1367
+ @HiveField(6) required DateTime updatedAt,
1368
+ }) = _Task;
1369
+
1370
+ factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
1371
+ }
1372
+ ```
1373
+
1374
+ ---
1375
+
1376
+ ### Validation
1377
+
1378
+ ```markdown
1379
+ ## Validation
1380
+
1381
+ File: `lib/features/{{moduleName}}/presentation/{{moduleName}}_form_validators.dart`
1382
+
1383
+ (Validator closures derived from PRD.md NFRs and Constraints)
1384
+ ```
1385
+
1386
+ Rules must be explicitly derived from tagged NFR IDs:
1387
+ - Character limits → `Validators.maxLength`
1388
+ - URL format → `Validators.url`
1389
+ - Required fields → `Validators.required`
1390
+ - Cross-field rules → composed in the `onSubmit` callback after `formKey.currentState!.validate()`
1391
+
1392
+ ---
1393
+
1394
+ ### Repository
1395
+
1396
+ ```markdown
1397
+ ## Repository
1398
+
1399
+ File: `lib/features/{{moduleName}}/data/{{moduleName}}_repository.dart`
1400
+
1401
+ (Complete repository class with all methods matching user stories)
1402
+ ```
1403
+
1404
+ - `Future<PaginatedResponse<{{Entity}}>> list({{ListParams}} params)` — list/search user stories
1405
+ - `Future<{{Entity}}> getById(String id)` — view detail user stories
1406
+ - `Future<{{Entity}}> create({{EntityCreate}} payload, {File? imageFile})` — create
1407
+ - `Future<{{Entity}}> update(String id, {{EntityUpdate}} payload, {File? imageFile})` — update
1408
+ - `Future<void> delete(String id)` — delete
1409
+ - `String imageUrl(String id)` — if the module has image fields
1410
+
1411
+ ---
1412
+
1413
+ ### Riverpod Providers
1414
+
1415
+ ```markdown
1416
+ ## Riverpod Providers
1417
+
1418
+ File: `lib/features/{{moduleName}}/application/{{moduleName}}_providers.dart`
1419
+
1420
+ (Complete generated providers using @riverpod annotation)
1421
+ ```
1422
+
1423
+ - `<entity>RepositoryProvider` — exposes the repository instance
1424
+ - `<entity>ListProvider(<params>)` — `AsyncNotifierProvider.family` for paginated list
1425
+ - `<entity>ByIdProvider(String id)` — `FutureProvider.family` for single item
1426
+ - `<entity>FormControllerProvider` — `Notifier` exposing `create`/`update`/`delete` with
1427
+ cache invalidation via `ref.invalidate(<entity>ListProvider)`
1428
+
1429
+ ---
1430
+
1431
+ ### Screens
1432
+
1433
+ ```markdown
1434
+ ## Screens
1435
+
1436
+ Files:
1437
+ - `lib/features/{{moduleName}}/presentation/{{moduleName}}_list_screen.dart`
1438
+ - `lib/features/{{moduleName}}/presentation/{{moduleName}}_detail_screen.dart`
1439
+ - `lib/features/{{moduleName}}/presentation/{{moduleName}}_form_screen.dart`
1440
+
1441
+ (Complete screen widget implementations matching the mockup screens)
1442
+ ```
1443
+
1444
+ **CRITICAL — screen widget requirements:**
1445
+ 1. Extend `ConsumerWidget` or `HookConsumerWidget`
1446
+ 2. Use `ref.watch(<entity>ListProvider(params))` and unwrap with `AsyncValue.when(...)`
1447
+ 3. Use `context.go(...)` / `context.push(...)` for navigation (go_router)
1448
+ 4. List screen: wrap with `SmartRefresher` (pull_to_refresh) and call `ref.invalidate(...)`
1449
+ in `onRefresh`
1450
+ 5. Form screen: distinguish create vs edit via the `:id` path parameter
1451
+ 6. Use `ConfirmDialog.show(context, ...)` for destructive actions
1452
+ 7. Use `ScaffoldMessenger` for transient success/error toasts
1453
+
1454
+ Form screen pattern for create/edit:
1455
+ ```dart
1456
+ class TaskFormScreen extends HookConsumerWidget {
1457
+ const TaskFormScreen({super.key, this.taskId});
1458
+ final String? taskId;
1459
+ bool get isEditMode => taskId != null;
1460
+
1461
+ @override
1462
+ Widget build(BuildContext context, WidgetRef ref) {
1463
+ // If isEditMode, watch taskByIdProvider(taskId!) and prefill form
1464
+ // Use formKey + TextEditingControllers for fields
1465
+ // ...
1466
+ }
1467
+ }
1468
+ ```
1469
+
1470
+ ---
1471
+
1472
+ ### Form Widget
1473
+
1474
+ ```markdown
1475
+ ## Form Widget
1476
+
1477
+ File: `lib/features/{{moduleName}}/presentation/widgets/{{moduleName}}_form.dart`
1478
+
1479
+ (Complete form widget using Form + GlobalKey<FormState> + reusable field widgets)
1480
+ ```
1481
+
1482
+ ---
1483
+
1484
+ ### Route Definitions
1485
+
1486
+ ```markdown
1487
+ ## Route Definitions
1488
+
1489
+ Add to `lib/router/app_router.dart`:
1490
+
1491
+ (Complete GoRoute entries for this module — with redirect guards matching the mockup
1492
+ role folder access control)
1493
+ ```
1494
+
1495
+ Example:
1496
+ ```dart
1497
+ GoRoute(
1498
+ path: '/task',
1499
+ name: 'task-list',
1500
+ redirect: (context, state) => _requireRoles(ref, [Roles.worker, Roles.admin]),
1501
+ builder: (context, state) => const TaskListScreen(),
1502
+ routes: [
1503
+ GoRoute(
1504
+ path: 'create',
1505
+ name: 'task-create',
1506
+ builder: (context, state) => const TaskFormScreen(),
1507
+ ),
1508
+ GoRoute(
1509
+ path: ':id',
1510
+ name: 'task-detail',
1511
+ builder: (context, state) => TaskDetailScreen(taskId: state.pathParameters['id']!),
1512
+ routes: [
1513
+ GoRoute(
1514
+ path: 'edit',
1515
+ name: 'task-edit',
1516
+ builder: (context, state) => TaskFormScreen(taskId: state.pathParameters['id']),
1517
+ ),
1518
+ ],
1519
+ ),
1520
+ ],
1521
+ ),
1522
+ ```
1523
+
1524
+ ---
1525
+
1526
+ ### Navigation Items
1527
+
1528
+ ```markdown
1529
+ ## Navigation Items
1530
+
1531
+ Add to `lib/shared/navigation/nav_config.dart`:
1532
+
1533
+ (NavItem entries for this module, per role)
1534
+ ```
1535
+
1536
+ ---
1537
+
1538
+ ### Status Chip Map (if module has status enum)
1539
+
1540
+ ```markdown
1541
+ ## Status Configuration
1542
+
1543
+ ```dart
1544
+ // Status display map for use with <StatusChip />
1545
+ const taskStatusDisplay = {
1546
+ TaskStatus.draft: (label: 'Draft', color: Colors.grey),
1547
+ TaskStatus.inProgress: (label: 'In Progress', color: Colors.blue),
1548
+ TaskStatus.completed: (label: 'Completed', color: Colors.green),
1549
+ };
1550
+ ```
1551
+
1552
+ ---
1553
+
1554
+ ### CRITICAL Rules for Module SPEC.md
1555
+
1556
+ 1. **Use real field names** from MODEL.md — not `fieldOne`/`fieldTwo`
1557
+ 2. **Use real table/collection names** from MODEL.md
1558
+ 3. **Map every user story** to a specific repository method or provider
1559
+ 4. **Map every NFR constraint** to a validator rule or widget behaviour
1560
+ 5. **Use module-based URLs** — `/task`, NOT `/worker/task`
1561
+ 6. **Role guards** come from mockup role folder structure → `redirect` callback on routes
1562
+ 7. **Version-tag every ID** in traceability tables
1563
+ 8. **Use null-safe Dart** — no `dynamic` except at the JSON boundary
1564
+ 9. **All code samples must be complete** — no `// ...` gaps
1565
+ 10. **Form screen handles both create and edit** via the `:id` path parameter unless
1566
+ the mockup has clearly separate screens