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.
- package/CHANGELOG.md +42 -0
- package/LICENSE +21 -0
- package/README.md +445 -0
- package/claude-plugin/.claude-plugin/plugin.json +46 -0
- package/claude-plugin/agents/flutter-setup-agent.md +54 -0
- package/claude-plugin/commands/flutter-add.md +32 -0
- package/claude-plugin/commands/flutter-create.md +28 -0
- package/claude-plugin/commands/flutter-migrate.md +28 -0
- package/claude-plugin/skills/flutter-scaffolding/SKILL.md +109 -0
- package/dist/claude-setup/agent-writer.d.ts +21 -0
- package/dist/claude-setup/agent-writer.d.ts.map +1 -0
- package/dist/claude-setup/agent-writer.js +365 -0
- package/dist/claude-setup/agent-writer.js.map +1 -0
- package/dist/claude-setup/claude-md-generator.d.ts +7 -0
- package/dist/claude-setup/claude-md-generator.d.ts.map +1 -0
- package/dist/claude-setup/claude-md-generator.js +428 -0
- package/dist/claude-setup/claude-md-generator.js.map +1 -0
- package/dist/claude-setup/commands-writer.d.ts +7 -0
- package/dist/claude-setup/commands-writer.d.ts.map +1 -0
- package/dist/claude-setup/commands-writer.js +303 -0
- package/dist/claude-setup/commands-writer.js.map +1 -0
- package/dist/claude-setup/hooks-writer.d.ts +3 -0
- package/dist/claude-setup/hooks-writer.d.ts.map +1 -0
- package/dist/claude-setup/hooks-writer.js +34 -0
- package/dist/claude-setup/hooks-writer.js.map +1 -0
- package/dist/claude-setup/index.d.ts +10 -0
- package/dist/claude-setup/index.d.ts.map +1 -0
- package/dist/claude-setup/index.js +9 -0
- package/dist/claude-setup/index.js.map +1 -0
- package/dist/claude-setup/mcp-config-writer.d.ts +3 -0
- package/dist/claude-setup/mcp-config-writer.d.ts.map +1 -0
- package/dist/claude-setup/mcp-config-writer.js +42 -0
- package/dist/claude-setup/mcp-config-writer.js.map +1 -0
- package/dist/claude-setup/plugin-assembler.d.ts +2 -0
- package/dist/claude-setup/plugin-assembler.d.ts.map +1 -0
- package/dist/claude-setup/plugin-assembler.js +3 -0
- package/dist/claude-setup/plugin-assembler.js.map +1 -0
- package/dist/claude-setup/prd-generator.d.ts +10 -0
- package/dist/claude-setup/prd-generator.d.ts.map +1 -0
- package/dist/claude-setup/prd-generator.js +295 -0
- package/dist/claude-setup/prd-generator.js.map +1 -0
- package/dist/claude-setup/setup-orchestrator.d.ts +11 -0
- package/dist/claude-setup/setup-orchestrator.d.ts.map +1 -0
- package/dist/claude-setup/setup-orchestrator.js +46 -0
- package/dist/claude-setup/setup-orchestrator.js.map +1 -0
- package/dist/claude-setup/skill-writer.d.ts +3 -0
- package/dist/claude-setup/skill-writer.d.ts.map +1 -0
- package/dist/claude-setup/skill-writer.js +256 -0
- package/dist/claude-setup/skill-writer.js.map +1 -0
- package/dist/cli/commands/add.d.ts +16 -0
- package/dist/cli/commands/add.d.ts.map +1 -0
- package/dist/cli/commands/add.js +414 -0
- package/dist/cli/commands/add.js.map +1 -0
- package/dist/cli/commands/create.d.ts +3 -0
- package/dist/cli/commands/create.d.ts.map +1 -0
- package/dist/cli/commands/create.js +161 -0
- package/dist/cli/commands/create.js.map +1 -0
- package/dist/cli/commands/list.d.ts +12 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +148 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/migrate.d.ts +3 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/dist/cli/commands/migrate.js +332 -0
- package/dist/cli/commands/migrate.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +34 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/ui/prompts.d.ts +9 -0
- package/dist/cli/ui/prompts.d.ts.map +1 -0
- package/dist/cli/ui/prompts.js +74 -0
- package/dist/cli/ui/prompts.js.map +1 -0
- package/dist/cli/ui/spinner.d.ts +2 -0
- package/dist/cli/ui/spinner.d.ts.map +1 -0
- package/dist/cli/ui/spinner.js +5 -0
- package/dist/cli/ui/spinner.js.map +1 -0
- package/dist/cli/version-check.d.ts +7 -0
- package/dist/cli/version-check.d.ts.map +1 -0
- package/dist/cli/version-check.js +92 -0
- package/dist/cli/version-check.js.map +1 -0
- package/dist/core/config/defaults.d.ts +3 -0
- package/dist/core/config/defaults.d.ts.map +1 -0
- package/dist/core/config/defaults.js +21 -0
- package/dist/core/config/defaults.js.map +1 -0
- package/dist/core/config/loader.d.ts +4 -0
- package/dist/core/config/loader.d.ts.map +1 -0
- package/dist/core/config/loader.js +38 -0
- package/dist/core/config/loader.js.map +1 -0
- package/dist/core/config/schema.d.ts +406 -0
- package/dist/core/config/schema.d.ts.map +1 -0
- package/dist/core/config/schema.js +119 -0
- package/dist/core/config/schema.js.map +1 -0
- package/dist/core/context.d.ts +57 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +99 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/detector.d.ts +28 -0
- package/dist/core/detector.d.ts.map +1 -0
- package/dist/core/detector.js +200 -0
- package/dist/core/detector.js.map +1 -0
- package/dist/core/validator.d.ts +7 -0
- package/dist/core/validator.d.ts.map +1 -0
- package/dist/core/validator.js +35 -0
- package/dist/core/validator.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/composer.d.ts +50 -0
- package/dist/modules/composer.d.ts.map +1 -0
- package/dist/modules/composer.js +122 -0
- package/dist/modules/composer.js.map +1 -0
- package/dist/modules/definitions/analytics/module.d.ts +7 -0
- package/dist/modules/definitions/analytics/module.d.ts.map +1 -0
- package/dist/modules/definitions/analytics/module.js +28 -0
- package/dist/modules/definitions/analytics/module.js.map +1 -0
- package/dist/modules/definitions/api/module.d.ts +7 -0
- package/dist/modules/definitions/api/module.d.ts.map +1 -0
- package/dist/modules/definitions/api/module.js +41 -0
- package/dist/modules/definitions/api/module.js.map +1 -0
- package/dist/modules/definitions/auth/module.d.ts +7 -0
- package/dist/modules/definitions/auth/module.d.ts.map +1 -0
- package/dist/modules/definitions/auth/module.js +53 -0
- package/dist/modules/definitions/auth/module.js.map +1 -0
- package/dist/modules/definitions/cicd/module.d.ts +7 -0
- package/dist/modules/definitions/cicd/module.d.ts.map +1 -0
- package/dist/modules/definitions/cicd/module.js +34 -0
- package/dist/modules/definitions/cicd/module.js.map +1 -0
- package/dist/modules/definitions/core/module.d.ts +7 -0
- package/dist/modules/definitions/core/module.d.ts.map +1 -0
- package/dist/modules/definitions/core/module.js +31 -0
- package/dist/modules/definitions/core/module.js.map +1 -0
- package/dist/modules/definitions/database/module.d.ts +8 -0
- package/dist/modules/definitions/database/module.d.ts.map +1 -0
- package/dist/modules/definitions/database/module.js +47 -0
- package/dist/modules/definitions/database/module.js.map +1 -0
- package/dist/modules/definitions/deep-linking/module.d.ts +7 -0
- package/dist/modules/definitions/deep-linking/module.d.ts.map +1 -0
- package/dist/modules/definitions/deep-linking/module.js +48 -0
- package/dist/modules/definitions/deep-linking/module.js.map +1 -0
- package/dist/modules/definitions/i18n/module.d.ts +7 -0
- package/dist/modules/definitions/i18n/module.d.ts.map +1 -0
- package/dist/modules/definitions/i18n/module.js +37 -0
- package/dist/modules/definitions/i18n/module.js.map +1 -0
- package/dist/modules/definitions/push/module.d.ts +7 -0
- package/dist/modules/definitions/push/module.d.ts.map +1 -0
- package/dist/modules/definitions/push/module.js +40 -0
- package/dist/modules/definitions/push/module.js.map +1 -0
- package/dist/modules/definitions/theme/module.d.ts +7 -0
- package/dist/modules/definitions/theme/module.d.ts.map +1 -0
- package/dist/modules/definitions/theme/module.js +42 -0
- package/dist/modules/definitions/theme/module.js.map +1 -0
- package/dist/modules/registry.d.ts +50 -0
- package/dist/modules/registry.d.ts.map +1 -0
- package/dist/modules/registry.js +104 -0
- package/dist/modules/registry.js.map +1 -0
- package/dist/modules/resolver.d.ts +42 -0
- package/dist/modules/resolver.d.ts.map +1 -0
- package/dist/modules/resolver.js +140 -0
- package/dist/modules/resolver.js.map +1 -0
- package/dist/ralph/prd-generator.d.ts +2 -0
- package/dist/ralph/prd-generator.d.ts.map +1 -0
- package/dist/ralph/prd-generator.js +3 -0
- package/dist/ralph/prd-generator.js.map +1 -0
- package/dist/ralph/ralph-converter.d.ts +2 -0
- package/dist/ralph/ralph-converter.d.ts.map +1 -0
- package/dist/ralph/ralph-converter.js +3 -0
- package/dist/ralph/ralph-converter.js.map +1 -0
- package/dist/ralph/story-sizer.d.ts +2 -0
- package/dist/ralph/story-sizer.d.ts.map +1 -0
- package/dist/ralph/story-sizer.js +3 -0
- package/dist/ralph/story-sizer.js.map +1 -0
- package/dist/scaffold/engine.d.ts +40 -0
- package/dist/scaffold/engine.d.ts.map +1 -0
- package/dist/scaffold/engine.js +233 -0
- package/dist/scaffold/engine.js.map +1 -0
- package/dist/scaffold/file-writer.d.ts +22 -0
- package/dist/scaffold/file-writer.d.ts.map +1 -0
- package/dist/scaffold/file-writer.js +68 -0
- package/dist/scaffold/file-writer.js.map +1 -0
- package/dist/scaffold/post-processors/build-runner.d.ts +2 -0
- package/dist/scaffold/post-processors/build-runner.d.ts.map +1 -0
- package/dist/scaffold/post-processors/build-runner.js +5 -0
- package/dist/scaffold/post-processors/build-runner.js.map +1 -0
- package/dist/scaffold/post-processors/dart-format.d.ts +2 -0
- package/dist/scaffold/post-processors/dart-format.d.ts.map +1 -0
- package/dist/scaffold/post-processors/dart-format.js +5 -0
- package/dist/scaffold/post-processors/dart-format.js.map +1 -0
- package/dist/scaffold/post-processors/flutter-pub-get.d.ts +2 -0
- package/dist/scaffold/post-processors/flutter-pub-get.d.ts.map +1 -0
- package/dist/scaffold/post-processors/flutter-pub-get.js +5 -0
- package/dist/scaffold/post-processors/flutter-pub-get.js.map +1 -0
- package/dist/scaffold/renderer.d.ts +20 -0
- package/dist/scaffold/renderer.d.ts.map +1 -0
- package/dist/scaffold/renderer.js +75 -0
- package/dist/scaffold/renderer.js.map +1 -0
- package/dist/scaffold/template-helpers.d.ts +25 -0
- package/dist/scaffold/template-helpers.d.ts.map +1 -0
- package/dist/scaffold/template-helpers.js +115 -0
- package/dist/scaffold/template-helpers.js.map +1 -0
- package/dist/types/config.d.ts +4 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/module.d.ts +85 -0
- package/dist/types/module.d.ts.map +1 -0
- package/dist/types/module.js +2 -0
- package/dist/types/module.js.map +1 -0
- package/dist/types/project.d.ts +13 -0
- package/dist/types/project.d.ts.map +1 -0
- package/dist/types/project.js +2 -0
- package/dist/types/project.js.map +1 -0
- package/package.json +80 -0
- package/templates/core/analysis_options.yaml.hbs +21 -0
- package/templates/core/lib/app.dart.hbs +20 -0
- package/templates/core/lib/core/providers/app_providers.dart.hbs +3 -0
- package/templates/core/lib/core/router/app_router.dart.hbs +19 -0
- package/templates/core/lib/core/theme/app_theme.dart.hbs +15 -0
- package/templates/core/lib/features/home/data/models/.gitkeep +0 -0
- package/templates/core/lib/features/home/data/repositories/.gitkeep +0 -0
- package/templates/core/lib/features/home/domain/entities/.gitkeep +0 -0
- package/templates/core/lib/features/home/domain/repositories/.gitkeep +0 -0
- package/templates/core/lib/features/home/presentation/pages/home_page.dart.hbs +18 -0
- package/templates/core/lib/features/home/presentation/widgets/.gitkeep +0 -0
- package/templates/core/lib/main.dart.hbs +11 -0
- package/templates/core/pubspec.yaml.hbs +30 -0
- package/templates/core/test/widget_test.dart.hbs +16 -0
- package/templates/modules/analytics/lib/features/analytics/data/observers/analytics_route_observer.dart.hbs +53 -0
- package/templates/modules/analytics/lib/features/analytics/data/services/firebase_analytics_service.dart.hbs +39 -0
- package/templates/modules/analytics/lib/features/analytics/domain/services/analytics_service.dart.hbs +25 -0
- package/templates/modules/analytics/lib/features/analytics/presentation/providers/analytics_provider.dart.hbs +17 -0
- package/templates/modules/analytics/pubspec.partial.yaml +2 -0
- package/templates/modules/api/lib/features/api/data/datasources/api_client.dart.hbs +171 -0
- package/templates/modules/api/lib/features/api/data/interceptors/auth_interceptor.dart.hbs +25 -0
- package/templates/modules/api/lib/features/api/data/interceptors/retry_interceptor.dart.hbs +42 -0
- package/templates/modules/api/lib/features/api/data/repositories/api_repository_impl.dart.hbs +21 -0
- package/templates/modules/api/lib/features/api/domain/exceptions/api_exception.dart.hbs +31 -0
- package/templates/modules/api/lib/features/api/domain/repositories/api_repository.dart.hbs +8 -0
- package/templates/modules/api/lib/features/api/presentation/providers/api_provider.dart.hbs +17 -0
- package/templates/modules/api/pubspec.partial.yaml +7 -0
- package/templates/modules/auth/lib/features/auth/data/datasources/auth_remote_datasource.dart.hbs +181 -0
- package/templates/modules/auth/lib/features/auth/data/models/auth_user_model.dart.hbs +27 -0
- package/templates/modules/auth/lib/features/auth/data/repositories/auth_repository_impl.dart.hbs +49 -0
- package/templates/modules/auth/lib/features/auth/domain/entities/user_entity.dart.hbs +18 -0
- package/templates/modules/auth/lib/features/auth/domain/repositories/auth_repository.dart.hbs +22 -0
- package/templates/modules/auth/lib/features/auth/domain/usecases/register_usecase.dart.hbs +20 -0
- package/templates/modules/auth/lib/features/auth/domain/usecases/sign_in_usecase.dart.hbs +15 -0
- package/templates/modules/auth/lib/features/auth/domain/usecases/sign_out_usecase.dart.hbs +11 -0
- package/templates/modules/auth/lib/features/auth/presentation/pages/login_page.dart.hbs +118 -0
- package/templates/modules/auth/lib/features/auth/presentation/pages/register_page.dart.hbs +131 -0
- package/templates/modules/auth/lib/features/auth/presentation/providers/auth_provider.dart.hbs +31 -0
- package/templates/modules/auth/pubspec.partial.yaml +13 -0
- package/templates/modules/cicd/.github/workflows/ci.yml.hbs +45 -0
- package/templates/modules/cicd/.gitlab-ci.yml.hbs +53 -0
- package/templates/modules/cicd/bitbucket-pipelines.yml.hbs +39 -0
- package/templates/modules/cicd/pubspec.partial.yaml +1 -0
- package/templates/modules/database/lib/features/database/data/datasources/database_datasource.dart.hbs +115 -0
- package/templates/modules/database/lib/features/database/data/repositories/database_repository_impl.dart.hbs +73 -0
- package/templates/modules/database/lib/features/database/domain/repositories/database_repository.dart.hbs +10 -0
- package/templates/modules/database/lib/features/database/presentation/providers/database_provider.dart.hbs +43 -0
- package/templates/modules/database/pubspec.partial.yaml +27 -0
- package/templates/modules/deep-linking/lib/features/deep_linking/data/datasources/deep_link_datasource.dart.hbs +18 -0
- package/templates/modules/deep-linking/lib/features/deep_linking/data/repositories/deep_link_repository_impl.dart.hbs +14 -0
- package/templates/modules/deep-linking/lib/features/deep_linking/domain/repositories/deep_link_repository.dart.hbs +7 -0
- package/templates/modules/deep-linking/lib/features/deep_linking/presentation/providers/deep_link_provider.dart.hbs +57 -0
- package/templates/modules/deep-linking/pubspec.partial.yaml +2 -0
- package/templates/modules/i18n/l10n.yaml.hbs +5 -0
- package/templates/modules/i18n/lib/core/l10n/l10n_config.dart.hbs +29 -0
- package/templates/modules/i18n/lib/core/l10n/l10n_provider.dart.hbs +20 -0
- package/templates/modules/i18n/lib/l10n/app_de.arb.hbs +27 -0
- package/templates/modules/i18n/lib/l10n/app_en.arb.hbs +27 -0
- package/templates/modules/i18n/pubspec.partial.yaml +7 -0
- package/templates/modules/push/lib/features/push/data/datasources/push_datasource.dart.hbs +124 -0
- package/templates/modules/push/lib/features/push/data/repositories/push_repository_impl.dart.hbs +28 -0
- package/templates/modules/push/lib/features/push/domain/repositories/push_repository.dart.hbs +31 -0
- package/templates/modules/push/lib/features/push/presentation/providers/push_provider.dart.hbs +34 -0
- package/templates/modules/push/pubspec.partial.yaml +8 -0
- package/templates/modules/theme/lib/core/theme/app_theme.dart.hbs +74 -0
- package/templates/modules/theme/lib/core/theme/theme_provider.dart.hbs +46 -0
- 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,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
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -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
|
+
}
|
|
File without changes
|
|
@@ -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,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
|
+
}
|