@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,518 @@
1
+ # State Management Patterns — Riverpod 2 with `riverpod_generator`
2
+
3
+ This reference describes the canonical Riverpod patterns for the spec. Include this
4
+ content in Section 10 of the generated specification and in any module SPEC.md that
5
+ defines providers.
6
+
7
+ ---
8
+
9
+ ## State Separation Principle
10
+
11
+ The golden rule for Flutter app state management:
12
+
13
+ | State Type | Tool | Examples |
14
+ |---|---|---|
15
+ | **Server state** (data from API) | `AsyncNotifier` / `FutureProvider` | Task list, profile, catalogue |
16
+ | **Cached server state** (offline) | Hive box + Riverpod provider | Last-seen tasks, profile snapshot |
17
+ | **UI state** (per-screen, transient) | `Notifier` / `useState` (flutter_hooks) | Selected filter, active tab |
18
+ | **Global UI state** | `Notifier` persisted to Hive | Theme mode, locale, sidebar |
19
+ | **Auth state** | `AsyncNotifier`; tokens in `flutter_secure_storage` | JWT, user info, roles |
20
+ | **Form state** | `Form` + controllers (+ optional `Notifier` for wizards) | Field values, validation errors |
21
+
22
+ **Do NOT cache API responses in plain `Notifier`/`StateProvider`.** Use `AsyncNotifier`
23
+ so that loading/error/data states are first-class. For offline persistence, write the
24
+ fetched data to Hive *inside* the provider's `build()` after a successful fetch, and
25
+ seed `build()` with the cached value when offline.
26
+
27
+ ---
28
+
29
+ ## Generated Providers — `build.yaml`
30
+
31
+ ```yaml
32
+ # build.yaml at the project root
33
+ targets:
34
+ $default:
35
+ builders:
36
+ riverpod_generator:
37
+ options:
38
+ # Provide a project-wide naming convention if desired
39
+ provider_family_name: family
40
+ ```
41
+
42
+ Every provider file imports the generator part:
43
+
44
+ ```dart
45
+ import 'package:riverpod_annotation/riverpod_annotation.dart';
46
+
47
+ part 'task_providers.g.dart';
48
+ ```
49
+
50
+ Run codegen with:
51
+ ```bash
52
+ dart run build_runner watch --delete-conflicting-outputs
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Repository Provider Pattern
58
+
59
+ ```dart
60
+ // lib/features/task/data/task_repository.dart
61
+ import 'package:dio/dio.dart';
62
+ import 'package:riverpod_annotation/riverpod_annotation.dart';
63
+
64
+ import '../../../core/api/api_failure.dart';
65
+ import '../../../core/api/dio_provider.dart';
66
+ import '../../../core/api/paginated_response.dart';
67
+ import '../domain/task.dart';
68
+
69
+ part 'task_repository.g.dart';
70
+
71
+ class TaskRepository {
72
+ TaskRepository(this._dio);
73
+ final Dio _dio;
74
+
75
+ Future<PaginatedResponse<Task>> list(TaskListParams params) async {
76
+ try {
77
+ final response = await _dio.get<Map<String, dynamic>>(
78
+ '/tasks',
79
+ queryParameters: params.toJson(),
80
+ );
81
+ return PaginatedResponse.fromJson(
82
+ response.data!,
83
+ (item) => Task.fromJson(item as Map<String, dynamic>),
84
+ );
85
+ } on DioException catch (e) {
86
+ throw ApiFailure.fromDioException(e);
87
+ }
88
+ }
89
+
90
+ Future<Task> getById(String id) async {
91
+ try {
92
+ final response = await _dio.get<Map<String, dynamic>>('/tasks/$id');
93
+ return Task.fromJson(response.data!);
94
+ } on DioException catch (e) {
95
+ throw ApiFailure.fromDioException(e);
96
+ }
97
+ }
98
+
99
+ Future<Task> create(TaskCreate payload) async {
100
+ try {
101
+ final response = await _dio.post<Map<String, dynamic>>(
102
+ '/tasks',
103
+ data: payload.toJson(),
104
+ );
105
+ return Task.fromJson(response.data!);
106
+ } on DioException catch (e) {
107
+ throw ApiFailure.fromDioException(e);
108
+ }
109
+ }
110
+
111
+ Future<Task> update(String id, TaskUpdate payload) async {
112
+ try {
113
+ final response = await _dio.put<Map<String, dynamic>>(
114
+ '/tasks/$id',
115
+ data: payload.toJson(),
116
+ );
117
+ return Task.fromJson(response.data!);
118
+ } on DioException catch (e) {
119
+ throw ApiFailure.fromDioException(e);
120
+ }
121
+ }
122
+
123
+ Future<void> delete(String id) async {
124
+ try {
125
+ await _dio.delete<void>('/tasks/$id');
126
+ } on DioException catch (e) {
127
+ throw ApiFailure.fromDioException(e);
128
+ }
129
+ }
130
+ }
131
+
132
+ @Riverpod(keepAlive: true)
133
+ TaskRepository taskRepository(TaskRepositoryRef ref) {
134
+ return TaskRepository(ref.watch(dioProvider));
135
+ }
136
+ ```
137
+
138
+ ---
139
+
140
+ ## AsyncNotifier (List) Pattern
141
+
142
+ ```dart
143
+ // lib/features/task/application/task_providers.dart
144
+ import 'package:flutter/foundation.dart';
145
+ import 'package:riverpod_annotation/riverpod_annotation.dart';
146
+
147
+ import '../data/task_repository.dart';
148
+ import '../domain/task.dart';
149
+
150
+ part 'task_providers.g.dart';
151
+
152
+ @immutable
153
+ class TaskListParams {
154
+ const TaskListParams({
155
+ this.page = 0,
156
+ this.size = 20,
157
+ this.status,
158
+ this.search,
159
+ });
160
+
161
+ final int page;
162
+ final int size;
163
+ final TaskStatus? status;
164
+ final String? search;
165
+
166
+ Map<String, dynamic> toJson() => {
167
+ 'page': page,
168
+ 'size': size,
169
+ if (status != null) 'status': status!.name.toUpperCase(),
170
+ if (search != null && search!.isNotEmpty) 'search': search,
171
+ };
172
+
173
+ TaskListParams copyWith({int? page, int? size, TaskStatus? status, String? search}) {
174
+ return TaskListParams(
175
+ page: page ?? this.page,
176
+ size: size ?? this.size,
177
+ status: status ?? this.status,
178
+ search: search ?? this.search,
179
+ );
180
+ }
181
+
182
+ @override
183
+ bool operator ==(Object other) =>
184
+ other is TaskListParams &&
185
+ other.page == page &&
186
+ other.size == size &&
187
+ other.status == status &&
188
+ other.search == search;
189
+
190
+ @override
191
+ int get hashCode => Object.hash(page, size, status, search);
192
+ }
193
+
194
+ @riverpod
195
+ class TaskList extends _$TaskList {
196
+ @override
197
+ Future<PaginatedResponse<Task>> build(TaskListParams params) async {
198
+ final repo = ref.watch(taskRepositoryProvider);
199
+ return repo.list(params);
200
+ }
201
+
202
+ Future<void> refresh() async {
203
+ ref.invalidateSelf();
204
+ await future;
205
+ }
206
+ }
207
+
208
+ @riverpod
209
+ Future<Task> taskById(TaskByIdRef ref, String id) async {
210
+ final repo = ref.watch(taskRepositoryProvider);
211
+ return repo.getById(id);
212
+ }
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Mutation Notifier Pattern
218
+
219
+ ```dart
220
+ @riverpod
221
+ class TaskFormController extends _$TaskFormController {
222
+ @override
223
+ AsyncValue<void> build() => const AsyncData(null);
224
+
225
+ Future<Task> create(TaskCreate payload) async {
226
+ state = const AsyncLoading();
227
+ try {
228
+ final repo = ref.read(taskRepositoryProvider);
229
+ final created = await repo.create(payload);
230
+ ref.invalidate(taskListProvider);
231
+ state = const AsyncData(null);
232
+ return created;
233
+ } catch (error, stack) {
234
+ state = AsyncError(error, stack);
235
+ rethrow;
236
+ }
237
+ }
238
+
239
+ Future<Task> update(String id, TaskUpdate payload) async {
240
+ state = const AsyncLoading();
241
+ try {
242
+ final repo = ref.read(taskRepositoryProvider);
243
+ final updated = await repo.update(id, payload);
244
+ ref.invalidate(taskListProvider);
245
+ ref.invalidate(taskByIdProvider(id));
246
+ state = const AsyncData(null);
247
+ return updated;
248
+ } catch (error, stack) {
249
+ state = AsyncError(error, stack);
250
+ rethrow;
251
+ }
252
+ }
253
+
254
+ Future<void> delete(String id) async {
255
+ state = const AsyncLoading();
256
+ try {
257
+ final repo = ref.read(taskRepositoryProvider);
258
+ await repo.delete(id);
259
+ ref.invalidate(taskListProvider);
260
+ state = const AsyncData(null);
261
+ } catch (error, stack) {
262
+ state = AsyncError(error, stack);
263
+ rethrow;
264
+ }
265
+ }
266
+ }
267
+ ```
268
+
269
+ ---
270
+
271
+ ## Consuming Providers in Widgets
272
+
273
+ ```dart
274
+ import 'package:flutter/material.dart';
275
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
276
+
277
+ import '../application/task_providers.dart';
278
+ import '../../../shared/widgets/empty_state.dart';
279
+ import '../../../shared/widgets/error_state.dart';
280
+ import '../../../shared/widgets/loading_indicator.dart';
281
+
282
+ class TaskListScreen extends ConsumerStatefulWidget {
283
+ const TaskListScreen({super.key});
284
+
285
+ @override
286
+ ConsumerState<TaskListScreen> createState() => _TaskListScreenState();
287
+ }
288
+
289
+ class _TaskListScreenState extends ConsumerState<TaskListScreen> {
290
+ TaskListParams _params = const TaskListParams();
291
+
292
+ @override
293
+ Widget build(BuildContext context) {
294
+ final taskListAsync = ref.watch(taskListProvider(_params));
295
+
296
+ return Scaffold(
297
+ appBar: AppBar(title: const Text('Tasks')),
298
+ body: taskListAsync.when(
299
+ loading: () => const LoadingIndicator(),
300
+ error: (error, _) => ErrorState(
301
+ error: error,
302
+ onRetry: () => ref.invalidate(taskListProvider(_params)),
303
+ ),
304
+ data: (page) {
305
+ if (page.content.isEmpty) {
306
+ return const EmptyState(message: 'No tasks found.');
307
+ }
308
+ return ListView.builder(
309
+ itemCount: page.content.length,
310
+ itemBuilder: (context, index) {
311
+ final task = page.content[index];
312
+ return ListTile(
313
+ title: Text(task.title),
314
+ subtitle: Text(task.status.name),
315
+ onTap: () => context.push('/task/${task.id}'),
316
+ );
317
+ },
318
+ );
319
+ },
320
+ ),
321
+ );
322
+ }
323
+ }
324
+ ```
325
+
326
+ ---
327
+
328
+ ## Offline-First Pattern (Hive cache + remote)
329
+
330
+ ```dart
331
+ @riverpod
332
+ class TaskList extends _$TaskList {
333
+ @override
334
+ Future<List<Task>> build(TaskListParams params) async {
335
+ final box = ref.watch(taskCacheBoxProvider);
336
+ final repo = ref.watch(taskRepositoryProvider);
337
+
338
+ // Seed with cached data if present (sync)
339
+ final cached = box.values.toList();
340
+
341
+ try {
342
+ final remote = await repo.list(params);
343
+ await box.clear();
344
+ await box.addAll(remote.content);
345
+ return remote.content;
346
+ } catch (e) {
347
+ // Surface fresh data when possible, but fall back to cache
348
+ if (cached.isNotEmpty) {
349
+ return cached;
350
+ }
351
+ rethrow;
352
+ }
353
+ }
354
+ }
355
+ ```
356
+
357
+ ---
358
+
359
+ ## Family Providers for Detail / Parametrised Queries
360
+
361
+ ```dart
362
+ @riverpod
363
+ Future<Task> taskById(TaskByIdRef ref, String id) async {
364
+ final repo = ref.watch(taskRepositoryProvider);
365
+ return repo.getById(id);
366
+ }
367
+
368
+ // Usage in widget:
369
+ // final taskAsync = ref.watch(taskByIdProvider('abc-123'));
370
+ ```
371
+
372
+ ---
373
+
374
+ ## Listening for Side Effects (`ref.listen`)
375
+
376
+ Use `ref.listen` to react to provider state changes without rebuilding the widget —
377
+ useful for navigation, SnackBars, and dialogs:
378
+
379
+ ```dart
380
+ @override
381
+ Widget build(BuildContext context, WidgetRef ref) {
382
+ ref.listen<AsyncValue<void>>(taskFormControllerProvider, (previous, next) {
383
+ next.whenOrNull(
384
+ error: (error, _) {
385
+ ScaffoldMessenger.of(context).showSnackBar(
386
+ SnackBar(content: Text(error.toString())),
387
+ );
388
+ },
389
+ );
390
+ });
391
+ // ...
392
+ }
393
+ ```
394
+
395
+ ---
396
+
397
+ ## Multi-Step Form / Wizard Pattern
398
+
399
+ ```dart
400
+ @freezed
401
+ class TaskWizardState with _$TaskWizardState {
402
+ const factory TaskWizardState({
403
+ @Default(0) int step,
404
+ @Default(null) TaskCreateStep1? step1,
405
+ @Default(null) TaskCreateStep2? step2,
406
+ }) = _TaskWizardState;
407
+ }
408
+
409
+ @riverpod
410
+ class TaskWizard extends _$TaskWizard {
411
+ @override
412
+ TaskWizardState build() => const TaskWizardState();
413
+
414
+ void saveStep1(TaskCreateStep1 data) =>
415
+ state = state.copyWith(step1: data, step: 1);
416
+
417
+ void saveStep2(TaskCreateStep2 data) =>
418
+ state = state.copyWith(step2: data, step: 2);
419
+
420
+ void back() => state = state.copyWith(step: state.step - 1);
421
+
422
+ void reset() => state = const TaskWizardState();
423
+ }
424
+ ```
425
+
426
+ ---
427
+
428
+ ## Cache Invalidation Strategy
429
+
430
+ Follow this decision tree when invalidating Riverpod state after mutations:
431
+
432
+ | Mutation | Invalidation Target |
433
+ |---|---|
434
+ | Create item | `ref.invalidate(<entity>ListProvider)` |
435
+ | Update item | `ref.invalidate(<entity>ListProvider)` + `ref.invalidate(<entity>ByIdProvider(id))` |
436
+ | Delete item | `ref.invalidate(<entity>ListProvider)` |
437
+ | Bulk action (delete multiple) | `ref.invalidate(<entity>ListProvider)` and individual `<entity>ByIdProvider(id)` |
438
+ | Status toggle | `ref.invalidate(<entity>ListProvider)` + `ref.invalidate(<entity>ByIdProvider(id))` |
439
+
440
+ Prefer `ref.invalidate(...)` over manually mutating provider state — invalidation lets
441
+ the next `watch` rebuild fresh from source while preserving the loading UI.
442
+
443
+ ---
444
+
445
+ ## Auth State Provider
446
+
447
+ ```dart
448
+ @freezed
449
+ sealed class AuthState with _$AuthState {
450
+ const factory AuthState.unauthenticated() = _Unauthenticated;
451
+ const factory AuthState.authenticated({
452
+ required AuthUser user,
453
+ required String accessToken,
454
+ }) = _Authenticated;
455
+ }
456
+
457
+ @Riverpod(keepAlive: true)
458
+ class Auth extends _$Auth {
459
+ @override
460
+ FutureOr<AuthState> build() async {
461
+ final repo = ref.watch(authRepositoryProvider);
462
+ final restored = await repo.restoreSession();
463
+ return restored ?? const AuthState.unauthenticated();
464
+ }
465
+
466
+ Future<void> login(String email, String password) async {
467
+ state = const AsyncLoading();
468
+ state = await AsyncValue.guard(() async {
469
+ final repo = ref.read(authRepositoryProvider);
470
+ final session = await repo.login(email, password);
471
+ return AuthState.authenticated(
472
+ user: session.user,
473
+ accessToken: session.accessToken,
474
+ );
475
+ });
476
+ }
477
+
478
+ Future<void> logout() async {
479
+ final repo = ref.read(authRepositoryProvider);
480
+ await repo.logout();
481
+ state = const AsyncData(AuthState.unauthenticated());
482
+ }
483
+ }
484
+ ```
485
+
486
+ ---
487
+
488
+ ## Testing Providers in Isolation
489
+
490
+ ```dart
491
+ // test/features/task/task_list_test.dart
492
+ import 'package:flutter_test/flutter_test.dart';
493
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
494
+ import 'package:mocktail/mocktail.dart';
495
+
496
+ import 'package:mobile_app/features/task/data/task_repository.dart';
497
+ import 'package:mobile_app/features/task/application/task_providers.dart';
498
+
499
+ class _MockTaskRepository extends Mock implements TaskRepository {}
500
+
501
+ void main() {
502
+ test('taskList returns paginated tasks', () async {
503
+ final repo = _MockTaskRepository();
504
+ when(() => repo.list(any())).thenAnswer((_) async => PaginatedResponse(
505
+ content: [const Task(id: '1', title: 'A', status: TaskStatus.draft, /*...*/)],
506
+ page: 0, size: 20, totalElements: 1, totalPages: 1,
507
+ ));
508
+
509
+ final container = ProviderContainer(overrides: [
510
+ taskRepositoryProvider.overrideWithValue(repo),
511
+ ]);
512
+ addTearDown(container.dispose);
513
+
514
+ final page = await container.read(taskListProvider(const TaskListParams()).future);
515
+ expect(page.content, hasLength(1));
516
+ });
517
+ }
518
+ ```