maxsim-flutter 1.0.0

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 (281) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/LICENSE +21 -0
  3. package/README.md +445 -0
  4. package/claude-plugin/.claude-plugin/plugin.json +46 -0
  5. package/claude-plugin/agents/flutter-setup-agent.md +54 -0
  6. package/claude-plugin/commands/flutter-add.md +32 -0
  7. package/claude-plugin/commands/flutter-create.md +28 -0
  8. package/claude-plugin/commands/flutter-migrate.md +28 -0
  9. package/claude-plugin/skills/flutter-scaffolding/SKILL.md +109 -0
  10. package/dist/claude-setup/agent-writer.d.ts +21 -0
  11. package/dist/claude-setup/agent-writer.d.ts.map +1 -0
  12. package/dist/claude-setup/agent-writer.js +365 -0
  13. package/dist/claude-setup/agent-writer.js.map +1 -0
  14. package/dist/claude-setup/claude-md-generator.d.ts +7 -0
  15. package/dist/claude-setup/claude-md-generator.d.ts.map +1 -0
  16. package/dist/claude-setup/claude-md-generator.js +428 -0
  17. package/dist/claude-setup/claude-md-generator.js.map +1 -0
  18. package/dist/claude-setup/commands-writer.d.ts +7 -0
  19. package/dist/claude-setup/commands-writer.d.ts.map +1 -0
  20. package/dist/claude-setup/commands-writer.js +303 -0
  21. package/dist/claude-setup/commands-writer.js.map +1 -0
  22. package/dist/claude-setup/hooks-writer.d.ts +3 -0
  23. package/dist/claude-setup/hooks-writer.d.ts.map +1 -0
  24. package/dist/claude-setup/hooks-writer.js +34 -0
  25. package/dist/claude-setup/hooks-writer.js.map +1 -0
  26. package/dist/claude-setup/index.d.ts +10 -0
  27. package/dist/claude-setup/index.d.ts.map +1 -0
  28. package/dist/claude-setup/index.js +9 -0
  29. package/dist/claude-setup/index.js.map +1 -0
  30. package/dist/claude-setup/mcp-config-writer.d.ts +3 -0
  31. package/dist/claude-setup/mcp-config-writer.d.ts.map +1 -0
  32. package/dist/claude-setup/mcp-config-writer.js +42 -0
  33. package/dist/claude-setup/mcp-config-writer.js.map +1 -0
  34. package/dist/claude-setup/plugin-assembler.d.ts +2 -0
  35. package/dist/claude-setup/plugin-assembler.d.ts.map +1 -0
  36. package/dist/claude-setup/plugin-assembler.js +3 -0
  37. package/dist/claude-setup/plugin-assembler.js.map +1 -0
  38. package/dist/claude-setup/prd-generator.d.ts +10 -0
  39. package/dist/claude-setup/prd-generator.d.ts.map +1 -0
  40. package/dist/claude-setup/prd-generator.js +295 -0
  41. package/dist/claude-setup/prd-generator.js.map +1 -0
  42. package/dist/claude-setup/setup-orchestrator.d.ts +11 -0
  43. package/dist/claude-setup/setup-orchestrator.d.ts.map +1 -0
  44. package/dist/claude-setup/setup-orchestrator.js +46 -0
  45. package/dist/claude-setup/setup-orchestrator.js.map +1 -0
  46. package/dist/claude-setup/skill-writer.d.ts +3 -0
  47. package/dist/claude-setup/skill-writer.d.ts.map +1 -0
  48. package/dist/claude-setup/skill-writer.js +256 -0
  49. package/dist/claude-setup/skill-writer.js.map +1 -0
  50. package/dist/cli/commands/add.d.ts +16 -0
  51. package/dist/cli/commands/add.d.ts.map +1 -0
  52. package/dist/cli/commands/add.js +414 -0
  53. package/dist/cli/commands/add.js.map +1 -0
  54. package/dist/cli/commands/create.d.ts +3 -0
  55. package/dist/cli/commands/create.d.ts.map +1 -0
  56. package/dist/cli/commands/create.js +161 -0
  57. package/dist/cli/commands/create.js.map +1 -0
  58. package/dist/cli/commands/list.d.ts +12 -0
  59. package/dist/cli/commands/list.d.ts.map +1 -0
  60. package/dist/cli/commands/list.js +148 -0
  61. package/dist/cli/commands/list.js.map +1 -0
  62. package/dist/cli/commands/migrate.d.ts +3 -0
  63. package/dist/cli/commands/migrate.d.ts.map +1 -0
  64. package/dist/cli/commands/migrate.js +332 -0
  65. package/dist/cli/commands/migrate.js.map +1 -0
  66. package/dist/cli/index.d.ts +3 -0
  67. package/dist/cli/index.d.ts.map +1 -0
  68. package/dist/cli/index.js +34 -0
  69. package/dist/cli/index.js.map +1 -0
  70. package/dist/cli/ui/prompts.d.ts +9 -0
  71. package/dist/cli/ui/prompts.d.ts.map +1 -0
  72. package/dist/cli/ui/prompts.js +74 -0
  73. package/dist/cli/ui/prompts.js.map +1 -0
  74. package/dist/cli/ui/spinner.d.ts +2 -0
  75. package/dist/cli/ui/spinner.d.ts.map +1 -0
  76. package/dist/cli/ui/spinner.js +5 -0
  77. package/dist/cli/ui/spinner.js.map +1 -0
  78. package/dist/cli/version-check.d.ts +7 -0
  79. package/dist/cli/version-check.d.ts.map +1 -0
  80. package/dist/cli/version-check.js +92 -0
  81. package/dist/cli/version-check.js.map +1 -0
  82. package/dist/core/config/defaults.d.ts +3 -0
  83. package/dist/core/config/defaults.d.ts.map +1 -0
  84. package/dist/core/config/defaults.js +21 -0
  85. package/dist/core/config/defaults.js.map +1 -0
  86. package/dist/core/config/loader.d.ts +4 -0
  87. package/dist/core/config/loader.d.ts.map +1 -0
  88. package/dist/core/config/loader.js +38 -0
  89. package/dist/core/config/loader.js.map +1 -0
  90. package/dist/core/config/schema.d.ts +406 -0
  91. package/dist/core/config/schema.d.ts.map +1 -0
  92. package/dist/core/config/schema.js +119 -0
  93. package/dist/core/config/schema.js.map +1 -0
  94. package/dist/core/context.d.ts +57 -0
  95. package/dist/core/context.d.ts.map +1 -0
  96. package/dist/core/context.js +99 -0
  97. package/dist/core/context.js.map +1 -0
  98. package/dist/core/detector.d.ts +28 -0
  99. package/dist/core/detector.d.ts.map +1 -0
  100. package/dist/core/detector.js +200 -0
  101. package/dist/core/detector.js.map +1 -0
  102. package/dist/core/validator.d.ts +7 -0
  103. package/dist/core/validator.d.ts.map +1 -0
  104. package/dist/core/validator.js +35 -0
  105. package/dist/core/validator.js.map +1 -0
  106. package/dist/index.d.ts +21 -0
  107. package/dist/index.d.ts.map +1 -0
  108. package/dist/index.js +14 -0
  109. package/dist/index.js.map +1 -0
  110. package/dist/modules/composer.d.ts +50 -0
  111. package/dist/modules/composer.d.ts.map +1 -0
  112. package/dist/modules/composer.js +122 -0
  113. package/dist/modules/composer.js.map +1 -0
  114. package/dist/modules/definitions/analytics/module.d.ts +7 -0
  115. package/dist/modules/definitions/analytics/module.d.ts.map +1 -0
  116. package/dist/modules/definitions/analytics/module.js +28 -0
  117. package/dist/modules/definitions/analytics/module.js.map +1 -0
  118. package/dist/modules/definitions/api/module.d.ts +7 -0
  119. package/dist/modules/definitions/api/module.d.ts.map +1 -0
  120. package/dist/modules/definitions/api/module.js +41 -0
  121. package/dist/modules/definitions/api/module.js.map +1 -0
  122. package/dist/modules/definitions/auth/module.d.ts +7 -0
  123. package/dist/modules/definitions/auth/module.d.ts.map +1 -0
  124. package/dist/modules/definitions/auth/module.js +53 -0
  125. package/dist/modules/definitions/auth/module.js.map +1 -0
  126. package/dist/modules/definitions/cicd/module.d.ts +7 -0
  127. package/dist/modules/definitions/cicd/module.d.ts.map +1 -0
  128. package/dist/modules/definitions/cicd/module.js +34 -0
  129. package/dist/modules/definitions/cicd/module.js.map +1 -0
  130. package/dist/modules/definitions/core/module.d.ts +7 -0
  131. package/dist/modules/definitions/core/module.d.ts.map +1 -0
  132. package/dist/modules/definitions/core/module.js +31 -0
  133. package/dist/modules/definitions/core/module.js.map +1 -0
  134. package/dist/modules/definitions/database/module.d.ts +8 -0
  135. package/dist/modules/definitions/database/module.d.ts.map +1 -0
  136. package/dist/modules/definitions/database/module.js +47 -0
  137. package/dist/modules/definitions/database/module.js.map +1 -0
  138. package/dist/modules/definitions/deep-linking/module.d.ts +7 -0
  139. package/dist/modules/definitions/deep-linking/module.d.ts.map +1 -0
  140. package/dist/modules/definitions/deep-linking/module.js +48 -0
  141. package/dist/modules/definitions/deep-linking/module.js.map +1 -0
  142. package/dist/modules/definitions/i18n/module.d.ts +7 -0
  143. package/dist/modules/definitions/i18n/module.d.ts.map +1 -0
  144. package/dist/modules/definitions/i18n/module.js +37 -0
  145. package/dist/modules/definitions/i18n/module.js.map +1 -0
  146. package/dist/modules/definitions/push/module.d.ts +7 -0
  147. package/dist/modules/definitions/push/module.d.ts.map +1 -0
  148. package/dist/modules/definitions/push/module.js +40 -0
  149. package/dist/modules/definitions/push/module.js.map +1 -0
  150. package/dist/modules/definitions/theme/module.d.ts +7 -0
  151. package/dist/modules/definitions/theme/module.d.ts.map +1 -0
  152. package/dist/modules/definitions/theme/module.js +42 -0
  153. package/dist/modules/definitions/theme/module.js.map +1 -0
  154. package/dist/modules/registry.d.ts +50 -0
  155. package/dist/modules/registry.d.ts.map +1 -0
  156. package/dist/modules/registry.js +104 -0
  157. package/dist/modules/registry.js.map +1 -0
  158. package/dist/modules/resolver.d.ts +42 -0
  159. package/dist/modules/resolver.d.ts.map +1 -0
  160. package/dist/modules/resolver.js +140 -0
  161. package/dist/modules/resolver.js.map +1 -0
  162. package/dist/ralph/prd-generator.d.ts +2 -0
  163. package/dist/ralph/prd-generator.d.ts.map +1 -0
  164. package/dist/ralph/prd-generator.js +3 -0
  165. package/dist/ralph/prd-generator.js.map +1 -0
  166. package/dist/ralph/ralph-converter.d.ts +2 -0
  167. package/dist/ralph/ralph-converter.d.ts.map +1 -0
  168. package/dist/ralph/ralph-converter.js +3 -0
  169. package/dist/ralph/ralph-converter.js.map +1 -0
  170. package/dist/ralph/story-sizer.d.ts +2 -0
  171. package/dist/ralph/story-sizer.d.ts.map +1 -0
  172. package/dist/ralph/story-sizer.js +3 -0
  173. package/dist/ralph/story-sizer.js.map +1 -0
  174. package/dist/scaffold/engine.d.ts +40 -0
  175. package/dist/scaffold/engine.d.ts.map +1 -0
  176. package/dist/scaffold/engine.js +233 -0
  177. package/dist/scaffold/engine.js.map +1 -0
  178. package/dist/scaffold/file-writer.d.ts +22 -0
  179. package/dist/scaffold/file-writer.d.ts.map +1 -0
  180. package/dist/scaffold/file-writer.js +68 -0
  181. package/dist/scaffold/file-writer.js.map +1 -0
  182. package/dist/scaffold/post-processors/build-runner.d.ts +2 -0
  183. package/dist/scaffold/post-processors/build-runner.d.ts.map +1 -0
  184. package/dist/scaffold/post-processors/build-runner.js +5 -0
  185. package/dist/scaffold/post-processors/build-runner.js.map +1 -0
  186. package/dist/scaffold/post-processors/dart-format.d.ts +2 -0
  187. package/dist/scaffold/post-processors/dart-format.d.ts.map +1 -0
  188. package/dist/scaffold/post-processors/dart-format.js +5 -0
  189. package/dist/scaffold/post-processors/dart-format.js.map +1 -0
  190. package/dist/scaffold/post-processors/flutter-pub-get.d.ts +2 -0
  191. package/dist/scaffold/post-processors/flutter-pub-get.d.ts.map +1 -0
  192. package/dist/scaffold/post-processors/flutter-pub-get.js +5 -0
  193. package/dist/scaffold/post-processors/flutter-pub-get.js.map +1 -0
  194. package/dist/scaffold/renderer.d.ts +20 -0
  195. package/dist/scaffold/renderer.d.ts.map +1 -0
  196. package/dist/scaffold/renderer.js +75 -0
  197. package/dist/scaffold/renderer.js.map +1 -0
  198. package/dist/scaffold/template-helpers.d.ts +25 -0
  199. package/dist/scaffold/template-helpers.d.ts.map +1 -0
  200. package/dist/scaffold/template-helpers.js +115 -0
  201. package/dist/scaffold/template-helpers.js.map +1 -0
  202. package/dist/types/config.d.ts +4 -0
  203. package/dist/types/config.d.ts.map +1 -0
  204. package/dist/types/config.js +2 -0
  205. package/dist/types/config.js.map +1 -0
  206. package/dist/types/module.d.ts +85 -0
  207. package/dist/types/module.d.ts.map +1 -0
  208. package/dist/types/module.js +2 -0
  209. package/dist/types/module.js.map +1 -0
  210. package/dist/types/project.d.ts +13 -0
  211. package/dist/types/project.d.ts.map +1 -0
  212. package/dist/types/project.js +2 -0
  213. package/dist/types/project.js.map +1 -0
  214. package/package.json +80 -0
  215. package/templates/core/analysis_options.yaml.hbs +21 -0
  216. package/templates/core/lib/app.dart.hbs +20 -0
  217. package/templates/core/lib/core/providers/app_providers.dart.hbs +3 -0
  218. package/templates/core/lib/core/router/app_router.dart.hbs +19 -0
  219. package/templates/core/lib/core/theme/app_theme.dart.hbs +15 -0
  220. package/templates/core/lib/features/home/data/models/.gitkeep +0 -0
  221. package/templates/core/lib/features/home/data/repositories/.gitkeep +0 -0
  222. package/templates/core/lib/features/home/domain/entities/.gitkeep +0 -0
  223. package/templates/core/lib/features/home/domain/repositories/.gitkeep +0 -0
  224. package/templates/core/lib/features/home/presentation/pages/home_page.dart.hbs +18 -0
  225. package/templates/core/lib/features/home/presentation/widgets/.gitkeep +0 -0
  226. package/templates/core/lib/main.dart.hbs +11 -0
  227. package/templates/core/pubspec.yaml.hbs +30 -0
  228. package/templates/core/test/widget_test.dart.hbs +16 -0
  229. package/templates/modules/analytics/lib/features/analytics/data/observers/analytics_route_observer.dart.hbs +53 -0
  230. package/templates/modules/analytics/lib/features/analytics/data/services/firebase_analytics_service.dart.hbs +39 -0
  231. package/templates/modules/analytics/lib/features/analytics/domain/services/analytics_service.dart.hbs +25 -0
  232. package/templates/modules/analytics/lib/features/analytics/presentation/providers/analytics_provider.dart.hbs +17 -0
  233. package/templates/modules/analytics/pubspec.partial.yaml +2 -0
  234. package/templates/modules/api/lib/features/api/data/datasources/api_client.dart.hbs +171 -0
  235. package/templates/modules/api/lib/features/api/data/interceptors/auth_interceptor.dart.hbs +25 -0
  236. package/templates/modules/api/lib/features/api/data/interceptors/retry_interceptor.dart.hbs +42 -0
  237. package/templates/modules/api/lib/features/api/data/repositories/api_repository_impl.dart.hbs +21 -0
  238. package/templates/modules/api/lib/features/api/domain/exceptions/api_exception.dart.hbs +31 -0
  239. package/templates/modules/api/lib/features/api/domain/repositories/api_repository.dart.hbs +8 -0
  240. package/templates/modules/api/lib/features/api/presentation/providers/api_provider.dart.hbs +17 -0
  241. package/templates/modules/api/pubspec.partial.yaml +7 -0
  242. package/templates/modules/auth/lib/features/auth/data/datasources/auth_remote_datasource.dart.hbs +181 -0
  243. package/templates/modules/auth/lib/features/auth/data/models/auth_user_model.dart.hbs +27 -0
  244. package/templates/modules/auth/lib/features/auth/data/repositories/auth_repository_impl.dart.hbs +49 -0
  245. package/templates/modules/auth/lib/features/auth/domain/entities/user_entity.dart.hbs +18 -0
  246. package/templates/modules/auth/lib/features/auth/domain/repositories/auth_repository.dart.hbs +22 -0
  247. package/templates/modules/auth/lib/features/auth/domain/usecases/register_usecase.dart.hbs +20 -0
  248. package/templates/modules/auth/lib/features/auth/domain/usecases/sign_in_usecase.dart.hbs +15 -0
  249. package/templates/modules/auth/lib/features/auth/domain/usecases/sign_out_usecase.dart.hbs +11 -0
  250. package/templates/modules/auth/lib/features/auth/presentation/pages/login_page.dart.hbs +118 -0
  251. package/templates/modules/auth/lib/features/auth/presentation/pages/register_page.dart.hbs +131 -0
  252. package/templates/modules/auth/lib/features/auth/presentation/providers/auth_provider.dart.hbs +31 -0
  253. package/templates/modules/auth/pubspec.partial.yaml +13 -0
  254. package/templates/modules/cicd/.github/workflows/ci.yml.hbs +45 -0
  255. package/templates/modules/cicd/.gitlab-ci.yml.hbs +53 -0
  256. package/templates/modules/cicd/bitbucket-pipelines.yml.hbs +39 -0
  257. package/templates/modules/cicd/pubspec.partial.yaml +1 -0
  258. package/templates/modules/database/lib/features/database/data/datasources/database_datasource.dart.hbs +115 -0
  259. package/templates/modules/database/lib/features/database/data/repositories/database_repository_impl.dart.hbs +73 -0
  260. package/templates/modules/database/lib/features/database/domain/repositories/database_repository.dart.hbs +10 -0
  261. package/templates/modules/database/lib/features/database/presentation/providers/database_provider.dart.hbs +43 -0
  262. package/templates/modules/database/pubspec.partial.yaml +27 -0
  263. package/templates/modules/deep-linking/lib/features/deep_linking/data/datasources/deep_link_datasource.dart.hbs +18 -0
  264. package/templates/modules/deep-linking/lib/features/deep_linking/data/repositories/deep_link_repository_impl.dart.hbs +14 -0
  265. package/templates/modules/deep-linking/lib/features/deep_linking/domain/repositories/deep_link_repository.dart.hbs +7 -0
  266. package/templates/modules/deep-linking/lib/features/deep_linking/presentation/providers/deep_link_provider.dart.hbs +57 -0
  267. package/templates/modules/deep-linking/pubspec.partial.yaml +2 -0
  268. package/templates/modules/i18n/l10n.yaml.hbs +5 -0
  269. package/templates/modules/i18n/lib/core/l10n/l10n_config.dart.hbs +29 -0
  270. package/templates/modules/i18n/lib/core/l10n/l10n_provider.dart.hbs +20 -0
  271. package/templates/modules/i18n/lib/l10n/app_de.arb.hbs +27 -0
  272. package/templates/modules/i18n/lib/l10n/app_en.arb.hbs +27 -0
  273. package/templates/modules/i18n/pubspec.partial.yaml +7 -0
  274. package/templates/modules/push/lib/features/push/data/datasources/push_datasource.dart.hbs +124 -0
  275. package/templates/modules/push/lib/features/push/data/repositories/push_repository_impl.dart.hbs +28 -0
  276. package/templates/modules/push/lib/features/push/domain/repositories/push_repository.dart.hbs +31 -0
  277. package/templates/modules/push/lib/features/push/presentation/providers/push_provider.dart.hbs +34 -0
  278. package/templates/modules/push/pubspec.partial.yaml +8 -0
  279. package/templates/modules/theme/lib/core/theme/app_theme.dart.hbs +74 -0
  280. package/templates/modules/theme/lib/core/theme/theme_provider.dart.hbs +46 -0
  281. package/templates/modules/theme/pubspec.partial.yaml +2 -0
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "maxsim-flutter",
3
+ "version": "1.0.0",
4
+ "description": "AI-powered Flutter app scaffolding with Clean Architecture, Riverpod, and autonomous development via Ralph",
5
+ "type": "module",
6
+ "bin": {
7
+ "maxsim-flutter": "./dist/cli/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "files": [
12
+ "dist/",
13
+ "templates/",
14
+ "claude-plugin/",
15
+ "README.md",
16
+ "CHANGELOG.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc -p tsconfig.build.json",
21
+ "dev": "tsc -p tsconfig.build.json --watch",
22
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest --passWithNoTests",
23
+ "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch",
24
+ "lint": "eslint src/",
25
+ "lint:fix": "eslint src/ --fix",
26
+ "format": "prettier --write \"src/**/*.ts\"",
27
+ "typecheck": "tsc --noEmit",
28
+ "validate:pack": "node scripts/validate-pack.mjs",
29
+ "prepublishOnly": "npm run typecheck && npm run lint && npm test && npm run build",
30
+ "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
31
+ },
32
+ "dependencies": {
33
+ "@clack/prompts": "^0.8.2",
34
+ "chalk": "^5.3.0",
35
+ "commander": "^12.1.0",
36
+ "execa": "^9.5.2",
37
+ "fs-extra": "^11.2.0",
38
+ "handlebars": "^4.7.8",
39
+ "js-yaml": "^4.1.0",
40
+ "ora": "^8.1.1",
41
+ "zod": "^3.23.8"
42
+ },
43
+ "devDependencies": {
44
+ "@types/fs-extra": "^11.0.4",
45
+ "@types/jest": "^29.5.14",
46
+ "@types/js-yaml": "^4.0.9",
47
+ "@types/node": "^22.10.5",
48
+ "@typescript-eslint/eslint-plugin": "^8.18.2",
49
+ "@typescript-eslint/parser": "^8.18.2",
50
+ "conventional-changelog-cli": "^5.0.0",
51
+ "eslint": "^9.17.0",
52
+ "jest": "^29.7.0",
53
+ "prettier": "^3.4.2",
54
+ "ts-jest": "^29.2.5",
55
+ "ts-node": "^10.9.2",
56
+ "typescript": "^5.7.2",
57
+ "typescript-eslint": "^8.56.0"
58
+ },
59
+ "engines": {
60
+ "node": ">=18.0.0"
61
+ },
62
+ "keywords": [
63
+ "flutter",
64
+ "scaffold",
65
+ "cli",
66
+ "clean-architecture",
67
+ "riverpod",
68
+ "claude",
69
+ "ai",
70
+ "ralph"
71
+ ],
72
+ "license": "MIT",
73
+ "repository": {
74
+ "type": "git",
75
+ "url": "git+https://github.com/maystudios/maxsim-flutter.git"
76
+ },
77
+ "publishConfig": {
78
+ "access": "public"
79
+ }
80
+ }
@@ -0,0 +1,21 @@
1
+ include: package:flutter_lints/flutter.yaml
2
+
3
+ analyzer:
4
+ language:
5
+ strict-casts: true
6
+ strict-inference: true
7
+ strict-raw-types: true
8
+ errors:
9
+ missing_required_param: error
10
+ missing_return: error
11
+
12
+ linter:
13
+ rules:
14
+ - always_declare_return_types
15
+ - avoid_print
16
+ - prefer_const_constructors
17
+ - prefer_const_declarations
18
+ - prefer_final_fields
19
+ - prefer_single_quotes
20
+ - sort_child_properties_last
21
+ - use_super_parameters
@@ -0,0 +1,20 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
3
+ import 'core/router/app_router.dart';
4
+
5
+ class App extends ConsumerWidget {
6
+ const App({super.key});
7
+
8
+ @override
9
+ Widget build(BuildContext context, WidgetRef ref) {
10
+ final router = ref.watch(routerProvider);
11
+ return MaterialApp.router(
12
+ title: '{{project.name}}',
13
+ theme: ThemeData(
14
+ colorSchemeSeed: Colors.blue,
15
+ useMaterial3: true,
16
+ ),
17
+ routerConfig: router,
18
+ );
19
+ }
20
+ }
@@ -0,0 +1,3 @@
1
+ // Barrel file for shared application providers.
2
+ // Export providers here to make them accessible across the app.
3
+ export '../router/app_router.dart';
@@ -0,0 +1,19 @@
1
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
2
+ import 'package:go_router/go_router.dart';
3
+ import 'package:riverpod_annotation/riverpod_annotation.dart';
4
+ import '../../features/home/presentation/pages/home_page.dart';
5
+
6
+ part 'app_router.g.dart';
7
+
8
+ @riverpod
9
+ GoRouter router(Ref ref) {
10
+ return GoRouter(
11
+ initialLocation: '/',
12
+ routes: [
13
+ GoRoute(
14
+ path: '/',
15
+ builder: (context, state) => const HomePage(),
16
+ ),
17
+ ],
18
+ );
19
+ }
@@ -0,0 +1,15 @@
1
+ import 'package:flutter/material.dart';
2
+
3
+ abstract final class AppTheme {
4
+ static ThemeData get light => ThemeData(
5
+ colorSchemeSeed: Colors.blue,
6
+ useMaterial3: true,
7
+ brightness: Brightness.light,
8
+ );
9
+
10
+ static ThemeData get dark => ThemeData(
11
+ colorSchemeSeed: Colors.blue,
12
+ useMaterial3: true,
13
+ brightness: Brightness.dark,
14
+ );
15
+ }
@@ -0,0 +1,18 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
3
+
4
+ class HomePage extends ConsumerWidget {
5
+ const HomePage({super.key});
6
+
7
+ @override
8
+ Widget build(BuildContext context, WidgetRef ref) {
9
+ return Scaffold(
10
+ appBar: AppBar(
11
+ title: const Text('{{project.name}}'),
12
+ ),
13
+ body: const Center(
14
+ child: Text('Welcome to {{project.name}}!'),
15
+ ),
16
+ );
17
+ }
18
+ }
@@ -0,0 +1,11 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
3
+ import 'app.dart';
4
+
5
+ void main() {
6
+ runApp(
7
+ const ProviderScope(
8
+ child: App(),
9
+ ),
10
+ );
11
+ }
@@ -0,0 +1,30 @@
1
+ name: {{project.name}}
2
+ description: {{project.description}}
3
+ publish_to: 'none'
4
+ version: 1.0.0+1
5
+
6
+ environment:
7
+ sdk: ^3.5.0
8
+ flutter: ^3.24.0
9
+
10
+ dependencies:
11
+ flutter:
12
+ sdk: flutter
13
+ flutter_riverpod: ^2.6.1
14
+ riverpod_annotation: ^2.6.1
15
+ go_router: ^14.6.2
16
+ freezed_annotation: ^2.4.4
17
+ json_annotation: ^4.9.0
18
+
19
+ dev_dependencies:
20
+ flutter_test:
21
+ sdk: flutter
22
+ flutter_lints: ^5.0.0
23
+ build_runner: ^2.4.13
24
+ riverpod_generator: ^2.6.3
25
+ go_router_builder: ^2.7.1
26
+ freezed: ^2.5.7
27
+ json_serializable: ^6.8.0
28
+
29
+ flutter:
30
+ uses-material-design: true
@@ -0,0 +1,16 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
3
+ import 'package:flutter_test/flutter_test.dart';
4
+ import 'package:{{project.name}}/app.dart';
5
+
6
+ void main() {
7
+ testWidgets('App renders without errors', (WidgetTester tester) async {
8
+ await tester.pumpWidget(
9
+ const ProviderScope(
10
+ child: App(),
11
+ ),
12
+ );
13
+
14
+ expect(find.byType(MaterialApp), findsOneWidget);
15
+ });
16
+ }
@@ -0,0 +1,53 @@
1
+ import 'package:flutter/widgets.dart';
2
+ import 'package:go_router/go_router.dart';
3
+ import '../../domain/services/analytics_service.dart';
4
+
5
+ /// A [NavigatorObserver] that automatically logs screen views to analytics
6
+ /// whenever the user navigates to a new route via go_router.
7
+ class AnalyticsRouteObserver extends NavigatorObserver {
8
+ final AnalyticsService _analytics;
9
+
10
+ AnalyticsRouteObserver(this._analytics);
11
+
12
+ String? _extractRouteName(Route<dynamic>? route) {
13
+ if (route == null) return null;
14
+ final settings = route.settings;
15
+ return settings.name;
16
+ }
17
+
18
+ @override
19
+ void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
20
+ super.didPush(route, previousRoute);
21
+ final name = _extractRouteName(route);
22
+ if (name != null) {
23
+ _analytics.logScreenView(
24
+ screenName: name,
25
+ screenClass: route.runtimeType.toString(),
26
+ );
27
+ }
28
+ }
29
+
30
+ @override
31
+ void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {
32
+ super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
33
+ final name = _extractRouteName(newRoute);
34
+ if (name != null) {
35
+ _analytics.logScreenView(
36
+ screenName: name,
37
+ screenClass: newRoute?.runtimeType.toString(),
38
+ );
39
+ }
40
+ }
41
+
42
+ @override
43
+ void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
44
+ super.didPop(route, previousRoute);
45
+ final name = _extractRouteName(previousRoute);
46
+ if (name != null) {
47
+ _analytics.logScreenView(
48
+ screenName: name,
49
+ screenClass: previousRoute?.runtimeType.toString(),
50
+ );
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,39 @@
1
+ import 'package:firebase_analytics/firebase_analytics.dart';
2
+ import '../../domain/services/analytics_service.dart';
3
+
4
+ class FirebaseAnalyticsService implements AnalyticsService {
5
+ final FirebaseAnalytics _analytics;
6
+
7
+ FirebaseAnalyticsService({FirebaseAnalytics? analytics})
8
+ : _analytics = analytics ?? FirebaseAnalytics.instance;
9
+
10
+ @override
11
+ Future<void> logScreenView({
12
+ required String screenName,
13
+ String? screenClass,
14
+ }) =>
15
+ _analytics.logScreenView(
16
+ screenName: screenName,
17
+ screenClass: screenClass,
18
+ );
19
+
20
+ @override
21
+ Future<void> logEvent({
22
+ required String name,
23
+ Map<String, Object>? parameters,
24
+ }) =>
25
+ _analytics.logEvent(name: name, parameters: parameters);
26
+
27
+ @override
28
+ Future<void> setUserProperty({
29
+ required String name,
30
+ required String? value,
31
+ }) =>
32
+ _analytics.setUserProperty(name: name, value: value);
33
+
34
+ @override
35
+ Future<void> setUserId(String? id) => _analytics.setUserId(id: id);
36
+
37
+ @override
38
+ Future<void> resetAnalyticsData() => _analytics.resetAnalyticsData();
39
+ }
@@ -0,0 +1,25 @@
1
+ abstract class AnalyticsService {
2
+ /// Log a screen view event.
3
+ Future<void> logScreenView({
4
+ required String screenName,
5
+ String? screenClass,
6
+ });
7
+
8
+ /// Log a custom event with optional parameters.
9
+ Future<void> logEvent({
10
+ required String name,
11
+ Map<String, Object>? parameters,
12
+ });
13
+
14
+ /// Set a user property.
15
+ Future<void> setUserProperty({
16
+ required String name,
17
+ required String? value,
18
+ });
19
+
20
+ /// Set the current user ID (e.g. after login).
21
+ Future<void> setUserId(String? id);
22
+
23
+ /// Reset analytics data (e.g. after logout).
24
+ Future<void> resetAnalyticsData();
25
+ }
@@ -0,0 +1,17 @@
1
+ import 'package:flutter_riverpod/flutter_riverpod.dart';
2
+ import 'package:riverpod_annotation/riverpod_annotation.dart';
3
+ import '../../data/services/firebase_analytics_service.dart';
4
+ import '../../data/observers/analytics_route_observer.dart';
5
+ import '../../domain/services/analytics_service.dart';
6
+
7
+ part 'analytics_provider.g.dart';
8
+
9
+ @riverpod
10
+ AnalyticsService analyticsService(Ref ref) {
11
+ return FirebaseAnalyticsService();
12
+ }
13
+
14
+ @riverpod
15
+ AnalyticsRouteObserver analyticsRouteObserver(Ref ref) {
16
+ return AnalyticsRouteObserver(ref.watch(analyticsServiceProvider));
17
+ }
@@ -0,0 +1,2 @@
1
+ dependencies:
2
+ firebase_analytics: ^11.3.6
@@ -0,0 +1,171 @@
1
+ import 'package:dio/dio.dart';
2
+ import '../../domain/exceptions/api_exception.dart';
3
+
4
+ /// Central API client built on Dio.
5
+ ///
6
+ /// Provides a pre-configured [Dio] instance with:
7
+ /// - Base URL from project configuration
8
+ /// - Request/response logging interceptor
9
+ /// - Error-mapping interceptor that converts [DioException] to [ApiException]
10
+ /// - Configurable request timeout
11
+ class ApiClient {
12
+ final Dio dio;
13
+
14
+ ApiClient({Dio? dio}) : dio = dio ?? _createDio();
15
+
16
+ static Dio _createDio() {
17
+ final dio = Dio(
18
+ BaseOptions(
19
+ baseUrl: const String.fromEnvironment(
20
+ 'API_BASE_URL',
21
+ defaultValue: '{{#if modules.api.baseUrl}}{{{modules.api.baseUrl}}}{{else}}https://api.example.com{{/if}}',
22
+ ),
23
+ connectTimeout: const Duration(seconds: 15),
24
+ receiveTimeout: const Duration(seconds: 15),
25
+ sendTimeout: const Duration(seconds: 15),
26
+ headers: {
27
+ 'Content-Type': 'application/json',
28
+ 'Accept': 'application/json',
29
+ },
30
+ ),
31
+ );
32
+
33
+ dio.interceptors.addAll([
34
+ _LoggingInterceptor(),
35
+ _ErrorInterceptor(),
36
+ ]);
37
+
38
+ return dio;
39
+ }
40
+
41
+ /// Perform a GET request.
42
+ Future<Response<T>> get<T>(
43
+ String path, {
44
+ Map<String, dynamic>? queryParameters,
45
+ Options? options,
46
+ }) {
47
+ return dio.get<T>(path, queryParameters: queryParameters, options: options);
48
+ }
49
+
50
+ /// Perform a POST request.
51
+ Future<Response<T>> post<T>(
52
+ String path, {
53
+ dynamic data,
54
+ Map<String, dynamic>? queryParameters,
55
+ Options? options,
56
+ }) {
57
+ return dio.post<T>(path, data: data, queryParameters: queryParameters, options: options);
58
+ }
59
+
60
+ /// Perform a PUT request.
61
+ Future<Response<T>> put<T>(
62
+ String path, {
63
+ dynamic data,
64
+ Map<String, dynamic>? queryParameters,
65
+ Options? options,
66
+ }) {
67
+ return dio.put<T>(path, data: data, queryParameters: queryParameters, options: options);
68
+ }
69
+
70
+ /// Perform a PATCH request.
71
+ Future<Response<T>> patch<T>(
72
+ String path, {
73
+ dynamic data,
74
+ Map<String, dynamic>? queryParameters,
75
+ Options? options,
76
+ }) {
77
+ return dio.patch<T>(path, data: data, queryParameters: queryParameters, options: options);
78
+ }
79
+
80
+ /// Perform a DELETE request.
81
+ Future<Response<T>> delete<T>(
82
+ String path, {
83
+ dynamic data,
84
+ Map<String, dynamic>? queryParameters,
85
+ Options? options,
86
+ }) {
87
+ return dio.delete<T>(path, data: data, queryParameters: queryParameters, options: options);
88
+ }
89
+ }
90
+
91
+ /// Interceptor that logs request and response details in debug mode.
92
+ class _LoggingInterceptor extends Interceptor {
93
+ @override
94
+ void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
95
+ assert(() {
96
+ // ignore: avoid_print
97
+ print('[API] ${options.method} ${options.uri}');
98
+ return true;
99
+ }());
100
+ handler.next(options);
101
+ }
102
+
103
+ @override
104
+ void onResponse(Response response, ResponseInterceptorHandler handler) {
105
+ assert(() {
106
+ // ignore: avoid_print
107
+ print('[API] ${response.statusCode} ${response.requestOptions.uri}');
108
+ return true;
109
+ }());
110
+ handler.next(response);
111
+ }
112
+
113
+ @override
114
+ void onError(DioException err, ErrorInterceptorHandler handler) {
115
+ assert(() {
116
+ // ignore: avoid_print
117
+ print('[API] ERROR ${err.type}: ${err.message}');
118
+ return true;
119
+ }());
120
+ handler.next(err);
121
+ }
122
+ }
123
+
124
+ /// Interceptor that maps [DioException] to typed [ApiException].
125
+ class _ErrorInterceptor extends Interceptor {
126
+ @override
127
+ void onError(DioException err, ErrorInterceptorHandler handler) {
128
+ final apiException = _mapDioException(err);
129
+ handler.reject(
130
+ DioException(
131
+ requestOptions: err.requestOptions,
132
+ response: err.response,
133
+ type: err.type,
134
+ error: apiException,
135
+ ),
136
+ );
137
+ }
138
+
139
+ ApiException _mapDioException(DioException err) {
140
+ switch (err.type) {
141
+ case DioExceptionType.connectionTimeout:
142
+ case DioExceptionType.sendTimeout:
143
+ case DioExceptionType.receiveTimeout:
144
+ return ApiException(
145
+ message: 'Request timed out. Please try again.',
146
+ );
147
+ case DioExceptionType.badResponse:
148
+ return ApiException(
149
+ statusCode: err.response?.statusCode,
150
+ message: _extractMessage(err.response),
151
+ data: err.response?.data,
152
+ );
153
+ case DioExceptionType.cancel:
154
+ return const ApiException(message: 'Request was cancelled.');
155
+ case DioExceptionType.connectionError:
156
+ return const ApiException(
157
+ message: 'No internet connection. Please check your network.',
158
+ );
159
+ default:
160
+ return ApiException(message: err.message ?? 'An unexpected error occurred.');
161
+ }
162
+ }
163
+
164
+ String _extractMessage(Response? response) {
165
+ if (response?.data is Map) {
166
+ final data = response!.data as Map;
167
+ return (data['message'] ?? data['error'] ?? 'Request failed').toString();
168
+ }
169
+ return 'Request failed with status ${response?.statusCode}';
170
+ }
171
+ }
@@ -0,0 +1,25 @@
1
+ import 'package:dio/dio.dart';
2
+
3
+ /// Interceptor that attaches authentication tokens to requests.
4
+ ///
5
+ /// Override [getToken] with your token retrieval logic
6
+ /// (e.g., reading from secure storage or a Riverpod provider).
7
+ class AuthInterceptor extends Interceptor {
8
+ /// Override this to return the current auth token.
9
+ Future<String?> getToken() async {
10
+ // TODO: Implement token retrieval from your auth/storage solution.
11
+ return null;
12
+ }
13
+
14
+ @override
15
+ void onRequest(
16
+ RequestOptions options,
17
+ RequestInterceptorHandler handler,
18
+ ) async {
19
+ final token = await getToken();
20
+ if (token != null) {
21
+ options.headers['Authorization'] = 'Bearer $token';
22
+ }
23
+ handler.next(options);
24
+ }
25
+ }
@@ -0,0 +1,42 @@
1
+ import 'package:dio/dio.dart';
2
+
3
+ /// Interceptor that retries failed requests with exponential backoff.
4
+ ///
5
+ /// Retries on connection errors and 5xx server errors up to [maxRetries] times.
6
+ class RetryInterceptor extends Interceptor {
7
+ final Dio dio;
8
+ final int maxRetries;
9
+
10
+ RetryInterceptor({
11
+ required this.dio,
12
+ this.maxRetries = 3,
13
+ });
14
+
15
+ @override
16
+ void onError(DioException err, ErrorInterceptorHandler handler) async {
17
+ if (_shouldRetry(err)) {
18
+ final retryCount = (err.requestOptions.extra['retryCount'] as int?) ?? 0;
19
+ if (retryCount < maxRetries) {
20
+ final delay = Duration(milliseconds: 200 * (retryCount + 1));
21
+ await Future<void>.delayed(delay);
22
+
23
+ err.requestOptions.extra['retryCount'] = retryCount + 1;
24
+ try {
25
+ final response = await dio.fetch(err.requestOptions);
26
+ handler.resolve(response);
27
+ return;
28
+ } on DioException catch (e) {
29
+ handler.reject(e);
30
+ return;
31
+ }
32
+ }
33
+ }
34
+ handler.next(err);
35
+ }
36
+
37
+ bool _shouldRetry(DioException err) {
38
+ return err.type == DioExceptionType.connectionError ||
39
+ err.type == DioExceptionType.connectionTimeout ||
40
+ (err.response?.statusCode != null && err.response!.statusCode! >= 500);
41
+ }
42
+ }
@@ -0,0 +1,21 @@
1
+ import '../../domain/exceptions/api_exception.dart';
2
+ import '../../domain/repositories/api_repository.dart';
3
+ import '../datasources/api_client.dart';
4
+
5
+ class ApiRepositoryImpl implements ApiRepository {
6
+ final ApiClient _client;
7
+
8
+ const ApiRepositoryImpl(this._client);
9
+
10
+ @override
11
+ Future<bool> healthCheck() async {
12
+ try {
13
+ final response = await _client.get<dynamic>('/health');
14
+ return response.statusCode == 200;
15
+ } on ApiException {
16
+ return false;
17
+ } catch (_) {
18
+ return false;
19
+ }
20
+ }
21
+ }