agent-flutter 0.1.19 → 0.1.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-flutter",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "Portable Flutter skill/rule pack initializer for multiple AI IDEs.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,3 +1,153 @@
1
1
  ---
2
2
  alwaysApply: false
3
3
  ---
4
+ # API Integration Rules & Workflow
5
+
6
+ ## 1. Goal
7
+ Standardize how to integrate backend APIs into this project using the current architecture:
8
+ - Transport: `lib/src/api/api.dart`
9
+ - Endpoint mapping: `lib/src/api/api_url.dart`
10
+ - Flavor config: `lib/src/utils/app_api_config.dart`
11
+ - Persistence/token: `lib/src/utils/app_shared.dart`
12
+ - State/UI: `lib/src/ui/**` with `Bloc + PageState`
13
+
14
+ The rule is designed to prevent common violations: hardcoded URLs, mixed model layers, missing flavor handling, and inconsistent error behavior.
15
+
16
+ ## 1.1 Skill References (Required)
17
+ When executing this rule, apply these skills together:
18
+ - Architecture: [flutter-standard-lib-src-architecture](../skills/flutter-standard-lib-src-architecture/SKILL.md)
19
+ - Dependency flow: [flutter-standard-lib-src-architecture-dependency-rules](../skills/flutter-standard-lib-src-architecture-dependency-rules/SKILL.md)
20
+ - State management: [flutter-bloc-state-management](../skills/flutter-bloc-state-management/SKILL.md)
21
+ - DI: [flutter-dependency-injection-injectable](../skills/flutter-dependency-injection-injectable/SKILL.md)
22
+ - Error handling: [flutter-error-handling](../skills/flutter-error-handling/SKILL.md)
23
+ - Localization: [getx-localization-standard](../skills/getx-localization-standard/SKILL.md)
24
+ - Model reuse: [dart-model-reuse](../skills/dart-model-reuse/SKILL.md)
25
+
26
+ ## 2. Definition of Ready (P1 - Required Before Coding)
27
+ API integration must not start until all items are clear:
28
+ - Feature scope (function flow) is explicit.
29
+ - API contract is explicit: method, path, request body/query, response schema, error schema.
30
+ - Database-side behavior is understood from backend contract (field semantics, nullable rules, pagination/sort semantics).
31
+ - Target flavor is explicit for testing (`staging` or `prod`).
32
+
33
+ If any item is missing, stop implementation and request contract clarification first.
34
+
35
+ ## 3. Project Conventions (Current Repo)
36
+
37
+ ### A. Endpoint Source of Truth
38
+ - Base URL must come from `AppApiConfig.baseApiUrl`.
39
+ - Endpoint paths must be defined in `api_url.dart` interfaces (`IAuthApiUrl`, future groups).
40
+ - Do not hardcode endpoint strings inside bloc/page/widget.
41
+
42
+ ### B. Transport Layer
43
+ - Reuse `Api.request()` in `lib/src/api/api.dart`.
44
+ - Use `useIDToken: true` by default for authenticated APIs.
45
+ - Use `useIDToken: false` only for anonymous APIs (login/register/...).
46
+ - Do not duplicate Dio setup in feature code.
47
+
48
+ ### C. Model Layer Separation
49
+ - Request models: `lib/src/core/model/request/`
50
+ - Response models: `lib/src/core/model/response/`
51
+ - UI/domain-only models: `lib/src/core/model/`
52
+ - Never pass raw `Map<String, dynamic>` across UI layers for non-trivial APIs.
53
+ - Reuse-first: check existing models before creating new ones.
54
+
55
+ ### D. Repository Boundary
56
+ - All remote calls must be wrapped by repository classes in `lib/src/core/repository/`.
57
+ - Bloc/Controller must call repository methods, not `Api.request()` directly.
58
+ - Repository owns request/response mapping and token persistence side effects when needed.
59
+ - New repository methods should prefer `Result<T, E>` style returns for predictable error flow.
60
+
61
+ ### E. State/Error Handling
62
+ - Use `PageState` (`initial/loading/failure/success`) for request lifecycle.
63
+ - User-facing messages must be localized via `LocaleKey.*.tr` (no hardcoded text in bloc).
64
+ - Network/server errors should be normalized in repository before bubbling to UI state.
65
+ - Token invalid flow should be handled consistently with existing dialog flow (`showDialogErrorToken`).
66
+ - Do not throw raw exceptions into UI layer for new integrations; map to typed error/result first.
67
+
68
+ ## 4. Standard Workflow for New API Integration
69
+
70
+ ### Step 1: Confirm Contract + Flavor
71
+ - Confirm endpoint contract and sample payloads.
72
+ - Confirm test flavor command:
73
+ - `fvm flutter run --flavor staging --dart-define=FLAVOR=staging`
74
+ - `fvm flutter run --flavor prod --dart-define=FLAVOR=prod`
75
+
76
+ ### Step 2: Add Endpoint Mapping
77
+ - Extend `api_url.dart` by feature group interface.
78
+ - Keep path builders centralized (no duplicated string fragments).
79
+ - Follow structure constraints from [flutter-standard-lib-src-architecture](../skills/flutter-standard-lib-src-architecture/SKILL.md).
80
+
81
+ ### Step 3: Create Models
82
+ - Add request/response models in correct folders.
83
+ - Prefer typed fields and safe nullability.
84
+ - Add parser methods (`fromJson/toJson`) consistently.
85
+ - Apply reuse/evolution checklist from [dart-model-reuse](../skills/dart-model-reuse/SKILL.md).
86
+
87
+ ### Step 4: Implement Repository Method
88
+ - Repository extends/reuses `Api`.
89
+ - Use endpoint from `apiUrl.<group>`.
90
+ - Map response to typed model.
91
+ - Handle auth token persistence/refresh responsibilities in repository only.
92
+ - Follow mapping strategy from [flutter-error-handling](../skills/flutter-error-handling/SKILL.md).
93
+
94
+ ### Step 5: Wire DI
95
+ - Register repository through binding/module with `Get` injection.
96
+ - UI layer resolves repository through bloc/controller, not directly in widgets.
97
+ - Follow module boundaries from [flutter-dependency-injection-injectable](../skills/flutter-dependency-injection-injectable/SKILL.md).
98
+
99
+ ### Step 6: Integrate into Bloc
100
+ - Add event(s): trigger API use case.
101
+ - Emit `PageState.loading` before call.
102
+ - On success: emit `PageState.success` with typed data.
103
+ - On failure: emit `PageState.failure` with localized error key/message.
104
+ - Keep event/state modeling aligned with [flutter-bloc-state-management](../skills/flutter-bloc-state-management/SKILL.md).
105
+
106
+ ### Step 7: UI Consumption
107
+ - UI reads bloc state and renders loading/error/success.
108
+ - No API parsing logic in widget tree.
109
+ - All strings shown from API errors must pass localization policy in [getx-localization-standard](../skills/getx-localization-standard/SKILL.md).
110
+
111
+ ## 5. Minimal Reference Pattern
112
+
113
+ ```dart
114
+ class FeatureRepository extends Api {
115
+ final IFeatureApiUrl _url;
116
+ FeatureRepository(this._url);
117
+
118
+ Future<FeatureResponse> fetchFeature(FeatureRequest req) async {
119
+ final res = await request<Map<String, dynamic>>(
120
+ _url.fetchFeature,
121
+ Method.post,
122
+ body: req.toJson(),
123
+ useIDToken: true,
124
+ );
125
+ return FeatureResponse.fromJson(res.data ?? <String, dynamic>{});
126
+ }
127
+ }
128
+ ```
129
+
130
+ ## 6. Anti-Patterns (Do Not)
131
+ - Do not call `Dio()` directly inside bloc/controller.
132
+ - Do not put endpoint string literals in UI layer.
133
+ - Do not mix request/response classes in one generic model file.
134
+ - Do not hardcode user-facing error strings.
135
+ - Do not bypass flavor config by using fixed base URL.
136
+
137
+ ## 7. Verification Checklist Before PR
138
+ - [ ] Endpoint added in `api_url.dart` and consumed from repository only.
139
+ - [ ] Request/response models are separated and typed.
140
+ - [ ] Bloc uses `PageState` lifecycle correctly.
141
+ - [ ] All displayed text/error is localized via `LocaleKey`.
142
+ - [ ] Flavor run verified at least on staging command.
143
+ - [ ] `fvm flutter analyze` passes.
144
+ - [ ] Existing behavior (auth/token/toast/dialog flow) is not regressed.
145
+
146
+ ## 8. Prompt Template for AI
147
+ Use this when asking AI to implement integration:
148
+
149
+ > Integrate API for `<feature>` using project rule `integration-api.md`.
150
+ > Contract: `<method/path/request/response/errors>`.
151
+ > Use `api_url.dart` + repository layer + typed models + bloc `PageState`.
152
+ > No hardcoded endpoint/message. Localize all user text.
153
+ > Validate with `analyze` and staging flavor run command.
@@ -237,6 +237,94 @@ ensure_line_in_file() {
237
237
  fi
238
238
  }
239
239
 
240
+ configure_android_flavors() {
241
+ local android_app_id="$ORG_ID.$APP_PACKAGE_NAME"
242
+ local gradle_kts_file="android/app/build.gradle.kts"
243
+
244
+ if [[ ! -f "$gradle_kts_file" ]]; then
245
+ echo "Warning: Android Gradle Kotlin file not found, skip flavor config: $gradle_kts_file"
246
+ return 0
247
+ fi
248
+
249
+ cat >"$gradle_kts_file" <<EOF
250
+ import java.io.File
251
+
252
+ fun loadEnv(name: String): Map<String, String> {
253
+ val envFile = rootProject.file("../" + name)
254
+ val env = mutableMapOf<String, String>()
255
+
256
+ if (envFile.exists()) {
257
+ envFile.forEachLine { line ->
258
+ if (line.isNotBlank() && !line.startsWith("#") && line.contains("=")) {
259
+ val parts = line.split("=", limit = 2)
260
+ env[parts[0].trim()] = parts[1].trim()
261
+ }
262
+ }
263
+ }
264
+ return env
265
+ }
266
+
267
+ val stagingEnv = loadEnv(".env.staging")
268
+ val prodEnv = loadEnv(".env.prod")
269
+
270
+ plugins {
271
+ id("com.android.application")
272
+ id("kotlin-android")
273
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
274
+ id("dev.flutter.flutter-gradle-plugin")
275
+ }
276
+
277
+ android {
278
+ namespace = "$android_app_id"
279
+ compileSdk = flutter.compileSdkVersion
280
+ ndkVersion = flutter.ndkVersion
281
+
282
+ compileOptions {
283
+ sourceCompatibility = JavaVersion.VERSION_11
284
+ targetCompatibility = JavaVersion.VERSION_11
285
+ }
286
+
287
+ kotlinOptions {
288
+ jvmTarget = JavaVersion.VERSION_11.toString()
289
+ }
290
+
291
+ defaultConfig {
292
+ applicationId = "$android_app_id"
293
+ minSdk = flutter.minSdkVersion
294
+ targetSdk = flutter.targetSdkVersion
295
+ versionCode = flutter.versionCode
296
+ versionName = flutter.versionName
297
+ }
298
+
299
+ buildTypes {
300
+ release {
301
+ signingConfig = signingConfigs.getByName("debug")
302
+ }
303
+ }
304
+
305
+ flavorDimensions += "app"
306
+ productFlavors {
307
+ create("prod") {
308
+ dimension = "app"
309
+ resValue("string", "google_maps_api_key", prodEnv["GOOGLE_MAP_API_KEY"] ?: "")
310
+ }
311
+ create("staging") {
312
+ dimension = "app"
313
+ applicationIdSuffix = ".staging"
314
+ versionNameSuffix = "-staging"
315
+ resValue("string", "google_maps_api_key", stagingEnv["GOOGLE_MAP_API_KEY"] ?: "")
316
+ }
317
+ }
318
+ }
319
+
320
+ flutter {
321
+ source = "../.."
322
+ }
323
+ EOF
324
+
325
+ echo "Configured Android product flavors (prod/staging)."
326
+ }
327
+
240
328
  PROJECT_NAME="$(prompt_required "$PROJECT_NAME" "Project name: ")"
241
329
  if [[ -z "$PROJECT_NAME" ]]; then
242
330
  echo "Error: project name is required." >&2
@@ -290,6 +378,8 @@ if ! "${create_args[@]}"; then
290
378
  exit 1
291
379
  fi
292
380
 
381
+ configure_android_flavors
382
+
293
383
  mkdir -p .vscode
294
384
  cat >.vscode/settings.json <<'JSON'
295
385
  {