flutterflow-mcp 0.3.3 → 0.3.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.
package/README.md CHANGED
@@ -181,6 +181,7 @@ These are limitations in FlutterFlow's own API. They affect any tool that connec
181
181
  - **Editing platform config files requires extra care** — FlutterFlow groups all platform config files together internally. If the AI pushes a change to just one (e.g., Android Manifest), FlutterFlow may delete the others (e.g., ProGuard rules, Gradle config). The MCP handles this by always including all existing files in the same push, but it's something to be aware of.
182
182
  - **Disabling conditional actions works differently** — In the FlutterFlow editor, you can toggle a conditional action on/off with a switch. The API handles this slightly differently, so the AI uses a workaround that achieves the same result.
183
183
  - **Validation isn't perfect** — The validation step catches most errors, but occasionally something that passes validation can still fail when pushed. This is rare, but it's why reviewing changes before pushing is important.
184
+ - **Project list may be incomplete** — The API may not return all projects you have access to, especially shared or team projects. If a project is missing, copy its ID directly from the FlutterFlow editor (click the project name in the top-left corner) and paste it into your AI prompt.
184
185
 
185
186
  ### General Limitations
186
187
 
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  export function registerListProjectsTool(server, client) {
3
- server.tool("list_projects", "List all FlutterFlow projects for the authenticated user", {
3
+ server.tool("list_projects", "List FlutterFlow projects for the authenticated user. NOTE: This may not return all projects you have access to (shared/team projects can be missing). If a project is missing, copy its ID directly from the FlutterFlow editor (click the project name in the top-left corner).", {
4
4
  project_type: z
5
5
  .string()
6
6
  .optional()
@@ -13,6 +13,10 @@ export function registerListProjectsTool(server, client) {
13
13
  type: "text",
14
14
  text: JSON.stringify(result, null, 2),
15
15
  },
16
+ {
17
+ type: "text",
18
+ text: "\n---\n**Tip:** This list may not include all projects you have access to (shared/team projects can be missing). If you don't see a project here, copy its ID directly from the FlutterFlow editor: click the project name (top-left corner) and copy the project ID.",
19
+ },
16
20
  ],
17
21
  };
18
22
  });
@@ -84,6 +84,13 @@ export const TOPIC_MAP = {
84
84
  parametervalues: "03-components.md",
85
85
  callback: "03-components.md",
86
86
  executecallbackaction: "03-components.md",
87
+ // Internationalization
88
+ translation: "01-project-files.md",
89
+ translations: "01-project-files.md",
90
+ i18n: "01-project-files.md",
91
+ localization: "01-project-files.md",
92
+ translatabletext: "01-project-files.md",
93
+ languages: "01-project-files.md",
87
94
  // Universal patterns
88
95
  inputvalue: "README.md",
89
96
  mostrecentinputvalue: "README.md",
@@ -36,7 +36,7 @@ Every FlutterFlow project is a collection of YAML files accessed via file keys.
36
36
  | `enums/id-XXX` | Enum definition with named values |
37
37
  | `collections/id-XXX` | Firestore collection schema: fields, types, subcollections |
38
38
  | `agent/id-XXX` | AI agent configuration |
39
- | `custom-file/id-<TYPE>` | Custom file configs for native platform files. Known types: `MAIN` (main.dart startup actions), `ANDROID_MANIFEST` (XML injection hooks for AndroidManifest.xml), `PROGUARD` (ProGuard rule injection for proguard-rules.pro), `BUILD_GRADLE` (Gradle plugin/dependency/repository injection for build.gradle). Only appear after enabled in FF editor. Sub-file `custom-file/id-MAIN/custom-file-code.dart` contains generated Dart source. |
39
+ | `custom-file/id-<TYPE>` | Custom file configs for native platform files. Known types: `MAIN` (main.dart startup actions), `ANDROID_MANIFEST` (XML injection hooks for AndroidManifest.xml), `INFO_PLIST` (plist property injection for Info.plist), `ENTITLEMENTS` (iOS capability entitlements for Runner.entitlements), `APP_DELEGATE` (Swift code injection for AppDelegate.swift), `PROGUARD` (ProGuard rule injection for proguard-rules.pro), `BUILD_GRADLE` (Gradle plugin/dependency/repository injection for build.gradle). Only appear after enabled in FF editor. Sub-file `custom-file/id-<TYPE>/custom-file-code.dart` contains generated source. |
40
40
  | `environment-settings` | Per-environment configuration values (API URLs, keys) |
41
41
  | `dependencies` | FlutterFlow library package dependencies |
42
42
  | `custom-code-dependencies` | Dart/Flutter pub dependencies for custom code |
@@ -444,6 +444,69 @@ persistLanguageSelection: true # Remember user's language choice
444
444
 
445
445
  ---
446
446
 
447
+ ## languages/translation/id-{key} (Translation Files)
448
+
449
+ **Purpose:** Individual translation entries for user-defined translatable strings. Each file maps a unique key to translated text across all supported languages.
450
+
451
+ **File key pattern:** `languages/translation/id-<key>` (e.g., `languages/translation/id-ttk654j0`)
452
+
453
+ **Schema:**
454
+ ```yaml
455
+ translationIdentifier:
456
+ key: ttk654j0 # Unique translation key (8-char alphanumeric)
457
+ translations:
458
+ - language:
459
+ language: en # ISO 639-1 code (must match languages.yaml)
460
+ text: Continue # Translated text for this language
461
+ - language:
462
+ language: es
463
+ text: Continuar
464
+ isFixed: false # false = user-editable, true = system/preset string
465
+ ```
466
+
467
+ **Key fields:**
468
+
469
+ | Field | Type | Notes |
470
+ |---|---|---|
471
+ | `translationIdentifier.key` | string | Unique key matching the file key suffix. Referenced by widgets and parameter passes. |
472
+ | `translations` | list | One entry per language. Must include all languages from `languages.yaml`. |
473
+ | `translations[].language.language` | string | ISO 639-1 code (`en`, `es`, `fr`, etc.) |
474
+ | `translations[].text` | string | Translated text. Empty string `""` = untranslated (falls back to primary language). |
475
+ | `isFixed` | bool | `true` for system preset strings, `false` for user-defined translations. |
476
+
477
+ ### Where translation keys are referenced
478
+
479
+ Translation keys appear in two contexts:
480
+
481
+ **1. On Text widgets** — via `translationIdentifier` in the `text` prop:
482
+ ```yaml
483
+ # Widget-level translation (static, per-widget)
484
+ text:
485
+ translationIdentifier:
486
+ key: ttk654j0
487
+ textValue:
488
+ inputValue: Continue # English default / editor preview
489
+ ```
490
+
491
+ **2. On component parameter passes** — via `translatableText` inside `inputValue`:
492
+ ```yaml
493
+ # Parameter-level translation (per-instance, used when passing
494
+ # translated strings to component parameters)
495
+ paramIdentifier:
496
+ name: title
497
+ key: p1titl
498
+ inputValue:
499
+ translatableText:
500
+ translationIdentifier:
501
+ key: ms01ttl1
502
+ textValue:
503
+ inputValue: Histamine / Low DAO # English default
504
+ ```
505
+
506
+ > **Important:** The `translatableText` wrapper is the correct way to pass translated strings as component parameters. Do NOT use `translationIdentifier` directly on the parameterPass (it will fail validation with "Unknown field name"). Instead, nest it inside `inputValue.translatableText`.
507
+
508
+ ---
509
+
447
510
  ## app-state.yaml
448
511
 
449
512
  **Purpose:** App-level state variables (global state accessible from any page/component).
@@ -1242,6 +1305,363 @@ parameters: # Template variables referenced in ho
1242
1305
 
1243
1306
  ---
1244
1307
 
1308
+ ## custom-file/id-INFO_PLIST
1309
+
1310
+ **Purpose:** Allows injecting custom properties into the generated `Info.plist` for iOS builds, and defining template variables for dynamic values. Like `ANDROID_MANIFEST`, this is an abstraction layer — you define "hooks" (plist property injection) and "parameters" (template variables).
1311
+
1312
+ > **This file only exists when it has content.** If all hooks and parameters are removed (via UI or API), the file disappears from the server (API returns 404). It reappears when a user adds a hook or parameter in the FF editor.
1313
+
1314
+ > **WARNING: Pushing any `custom-file` deletes siblings.** The API treats all `custom-file/id-*` keys as a single collection. Pushing this file alone will delete all other custom files (MAIN, ANDROID_MANIFEST, PROGUARD, BUILD_GRADLE, etc.). **Always include all existing `custom-file` entries in the same push payload.** See [API Limitation #10](../flutterflow-api-limitations.md#10-pushing-one-custom-file-deletes-all-other-custom-file-entries).
1315
+
1316
+ > **Important — this is NOT the Info.plist itself.**
1317
+ > This is FlutterFlow's configuration that controls **property injection into the generated Info.plist** at build time. The file only appears after a user adds at least one property in the FF editor (Settings > Custom Code > Custom Files > Info.plist).
1318
+
1319
+ **Top-level keys:**
1320
+ - `type`
1321
+ - `identifier`
1322
+ - `hooks`
1323
+ - `parameters`
1324
+
1325
+ ### Hook Types
1326
+
1327
+ Only one hook type:
1328
+
1329
+ | Hook Type | Where it injects in Info.plist |
1330
+ |---|---|
1331
+ | `INFO_PLIST_PROPERTY` | Inside the root `<dict>` — use for adding `<key>`/`<value>` pairs (strings, booleans, arrays, etc.) |
1332
+
1333
+ ### Parameters (Template Variables)
1334
+
1335
+ Same pattern as ANDROID_MANIFEST — parameters are template variables that can be referenced in hook `content` using `{{variableName}}` syntax. Values can come from literal input or environment variables.
1336
+
1337
+ ### Schema
1338
+
1339
+ ```yaml
1340
+ type: INFO_PLIST
1341
+ identifier:
1342
+ name: Info.plist
1343
+
1344
+ hooks: # List of plist property injection hooks
1345
+ - type: INFO_PLIST_PROPERTY # Only hook type for Info.plist
1346
+ identifier:
1347
+ name: livetag # Human-readable hook name
1348
+ key: gh2m0b # Unique 6-char alphanumeric key
1349
+ content: "<key>live</key>\n<string>value</string>" # Raw plist XML to inject
1350
+
1351
+ parameters: # Template variables
1352
+ zini8l: # Parameter key
1353
+ parameter:
1354
+ identifier:
1355
+ name: fsdafd # Variable name — use {{fsdafd}} in hook content
1356
+ dataType:
1357
+ scalarType: String # Data type (String, Integer, etc.)
1358
+ value:
1359
+ inputValue:
1360
+ serializedValue: safdsaf # Literal value
1361
+ ```
1362
+
1363
+ **Key fields:**
1364
+
1365
+ | Field | Type | Notes |
1366
+ |---|---|---|
1367
+ | `type` | string | Always `INFO_PLIST` for this file. |
1368
+ | `identifier.name` | string | Always `Info.plist`. |
1369
+ | `hooks` | list | List of plist property injection hooks. Each hook injects a key-value pair into the root `<dict>`. |
1370
+ | `hooks[].type` | enum | Always `INFO_PLIST_PROPERTY`. |
1371
+ | `hooks[].identifier.name` | string | Human-readable hook name. |
1372
+ | `hooks[].identifier.key` | string | Unique 6-char alphanumeric key. |
1373
+ | `hooks[].content` | string | Raw plist XML to inject. Typically `<key>name</key>\n<string>value</string>` or similar plist entries. Must be properly escaped in YAML. |
1374
+ | `parameters` | map | Map of parameter key to parameter definition. |
1375
+ | `parameters.<key>.parameter.identifier.name` | string | Variable name. Referenced in hook `content` as `{{variableName}}`. |
1376
+ | `parameters.<key>.parameter.dataType.scalarType` | enum | Data type (`String`, `Integer`, `Double`, `Boolean`, etc.). |
1377
+ | `parameters.<key>.value` | object | Value source — either `inputValue.serializedValue` (literal) or `variable` (environment reference). |
1378
+
1379
+ ### Parameter value sources
1380
+
1381
+ Parameters support two value sources:
1382
+
1383
+ **Literal value:**
1384
+ ```yaml
1385
+ value:
1386
+ inputValue:
1387
+ serializedValue: my-value # Hardcoded value
1388
+ ```
1389
+
1390
+ **Environment variable reference:**
1391
+ ```yaml
1392
+ value:
1393
+ variable:
1394
+ source: DEV_ENVIRONMENT
1395
+ baseVariable:
1396
+ environmentValue:
1397
+ identifier:
1398
+ name: myEnvVar # References environment-settings.yaml
1399
+ key: eji5p6
1400
+ ```
1401
+
1402
+ ### Hook content examples
1403
+
1404
+ **Simple string property:**
1405
+ ```yaml
1406
+ content: "<key>MyCustomKey</key>\n<string>MyCustomValue</string>"
1407
+ ```
1408
+
1409
+ **Boolean property:**
1410
+ ```yaml
1411
+ content: "<key>MyFeatureFlag</key>\n<true/>"
1412
+ ```
1413
+
1414
+ **Array property:**
1415
+ ```yaml
1416
+ content: "<key>LSApplicationQueriesSchemes</key>\n<array>\n <string>myapp</string>\n <string>myapp-dev</string>\n</array>"
1417
+ ```
1418
+
1419
+ **API capabilities:**
1420
+
1421
+ | Operation | Status | Notes |
1422
+ |---|---|---|
1423
+ | Reading config | Works | Full hook and parameter data returned. |
1424
+ | Adding hooks via API | Expected to work | Same structure as ANDROID_MANIFEST hooks. |
1425
+ | Adding parameters via API | Expected to work | Same structure as other custom file parameters. |
1426
+ | Hook name validation gap | Caution | Same as ANDROID_MANIFEST — avoid hyphens in names, use camelCase or spaces. |
1427
+
1428
+ ### Code sub-file (`custom-file/id-INFO_PLIST/custom-file-code.dart`)
1429
+
1430
+ Contains the full generated `Info.plist` XML. This is the complete plist including all standard iOS keys (bundle identifiers, permissions, URL schemes, etc.) plus any injected hook content. It is **read-only** — modifications should be made through the hooks/parameters config, not by editing the XML directly.
1431
+
1432
+ ---
1433
+
1434
+ ## custom-file/id-ENTITLEMENTS
1435
+
1436
+ **Purpose:** Allows injecting custom entitlements into the generated `Runner.entitlements` file for iOS builds. Entitlements declare app capabilities such as push notifications, App Groups, iCloud, associated domains, and other iOS-specific permissions.
1437
+
1438
+ > **This file only exists when it has content.** If all hooks and parameters are removed (via UI or API), the file disappears from the server (API returns 404). It reappears when a user adds an entitlement or parameter in the FF editor.
1439
+
1440
+ > **WARNING: Pushing any `custom-file` deletes siblings.** The API treats all `custom-file/id-*` keys as a single collection. Pushing this file alone will delete all other custom files (MAIN, ANDROID_MANIFEST, INFO_PLIST, etc.). **Always include all existing `custom-file` entries in the same push payload.** See [API Limitation #10](../flutterflow-api-limitations.md#10-pushing-one-custom-file-deletes-all-other-custom-file-entries).
1441
+
1442
+ > **Important — this is an abstraction layer.**
1443
+ > This config controls **entitlement injection into the generated `Runner.entitlements`** at build time. The file only appears after a user adds at least one entitlement in the FF editor (Settings > Custom Code > Custom Files > Runner.entitlements).
1444
+
1445
+ **Top-level keys:**
1446
+ - `type`
1447
+ - `identifier`
1448
+ - `hooks`
1449
+ - `parameters`
1450
+
1451
+ ### Hook Types
1452
+
1453
+ Only one hook type:
1454
+
1455
+ | Hook Type | Where it injects in Runner.entitlements |
1456
+ |---|---|
1457
+ | `ENTITLEMENT` | Inside the root `<dict>` of the entitlements plist — use for adding capability key-value pairs |
1458
+
1459
+ ### Schema
1460
+
1461
+ ```yaml
1462
+ type: ENTITLEMENTS
1463
+ identifier:
1464
+ name: Runner.entitlements
1465
+
1466
+ hooks:
1467
+ - type: ENTITLEMENT # Only hook type for entitlements
1468
+ identifier:
1469
+ name: environemnt # Human-readable hook name
1470
+ key: 5x527x # Unique 6-char alphanumeric key
1471
+ content: "<key>aps-environment</key>\n<string>development</string>" # Plist XML to inject
1472
+
1473
+ parameters: # Template variables — same as all other custom files
1474
+ f4cahu:
1475
+ parameter:
1476
+ identifier:
1477
+ name: variable1 # Variable name — use {{variable1}} in hook content
1478
+ dataType:
1479
+ scalarType: String
1480
+ value:
1481
+ inputValue:
1482
+ serializedValue: clear value # Default value
1483
+ ```
1484
+
1485
+ **Key fields:**
1486
+
1487
+ | Field | Type | Notes |
1488
+ |---|---|---|
1489
+ | `type` | string | Always `ENTITLEMENTS` for this file. |
1490
+ | `identifier.name` | string | Always `Runner.entitlements`. |
1491
+ | `hooks` | list | List of entitlement injection hooks. Each hook injects a capability key-value pair into the entitlements plist. |
1492
+ | `hooks[].type` | enum | Always `ENTITLEMENT`. |
1493
+ | `hooks[].identifier.name` | string | Human-readable name for the entitlement. |
1494
+ | `hooks[].identifier.key` | string | Unique 6-char alphanumeric key. |
1495
+ | `hooks[].content` | string | Raw plist XML to inject. Typically `<key>capability-name</key>` followed by a value (`<string>`, `<true/>`, `<array>`, etc.). Must be properly escaped in YAML. |
1496
+ | `parameters` | map | Template variables keyed by parameter ID. Use `{{variableName}}` in `content` to reference them. Same structure as all other custom files. |
1497
+
1498
+ ### Common entitlement content examples
1499
+
1500
+ **Push notifications (development):**
1501
+ ```yaml
1502
+ content: "<key>aps-environment</key>\n<string>development</string>"
1503
+ ```
1504
+
1505
+ **Push notifications (production):**
1506
+ ```yaml
1507
+ content: "<key>aps-environment</key>\n<string>production</string>"
1508
+ ```
1509
+
1510
+ **Associated domains (for universal links):**
1511
+ ```yaml
1512
+ content: "<key>com.apple.developer.associated-domains</key>\n<array>\n <string>applinks:example.com</string>\n</array>"
1513
+ ```
1514
+
1515
+ **App Groups:**
1516
+ ```yaml
1517
+ content: "<key>com.apple.security.application-groups</key>\n<array>\n <string>group.com.example.myapp</string>\n</array>"
1518
+ ```
1519
+
1520
+ ### API capabilities
1521
+
1522
+ | Operation | Status | Notes |
1523
+ |---|---|---|
1524
+ | Reading config | Works | Full hook and parameter data returned. |
1525
+ | Adding hooks via API | Expected to work | Same structure as other custom file hooks. |
1526
+ | Adding parameters via API | Expected to work | Same structure as other custom file parameters. |
1527
+ | Hook name validation gap | Caution | Same as other custom files — avoid hyphens in names, use camelCase or spaces. |
1528
+
1529
+ ---
1530
+
1531
+ ## custom-file/id-APP_DELEGATE
1532
+
1533
+ **Purpose:** Allows injecting custom Swift code into the generated `AppDelegate.swift` for iOS builds. Supports import statements and initialization code that runs during the iOS app launch sequence.
1534
+
1535
+ > **This file only exists when it has content.** If all hooks and parameters are removed (via UI or API), the file disappears from the server (API returns 404). It reappears when a user adds a hook or parameter in the FF editor.
1536
+
1537
+ > **WARNING: Pushing any `custom-file` deletes siblings.** The API treats all `custom-file/id-*` keys as a single collection. Pushing this file alone will delete all other custom files (MAIN, ANDROID_MANIFEST, INFO_PLIST, etc.). **Always include all existing `custom-file` entries in the same push payload.** See [API Limitation #10](../flutterflow-api-limitations.md#10-pushing-one-custom-file-deletes-all-other-custom-file-entries).
1538
+
1539
+ > **Important — this is an abstraction layer.**
1540
+ > This config controls **Swift code injection into the generated `AppDelegate.swift`** at build time. The file only appears after a user adds at least one hook in the FF editor (Settings > Custom Code > Custom Files > AppDelegate.swift).
1541
+
1542
+ **Top-level keys:**
1543
+ - `type`
1544
+ - `identifier`
1545
+ - `hooks`
1546
+ - `parameters`
1547
+
1548
+ ### Hook Types
1549
+
1550
+ Two hook types control where Swift code is injected in the generated `AppDelegate.swift`:
1551
+
1552
+ | Hook Type | Where it injects in AppDelegate.swift |
1553
+ |---|---|
1554
+ | `APP_DELEGATE_IMPORT_HOOK` | At the top of the file — use for `import` statements |
1555
+ | `APP_DELEGATE_INITIALIZATION_HOOK` | Inside `application(_:didFinishLaunchingWithOptions:)` — use for SDK init calls and setup code |
1556
+
1557
+ ### Schema
1558
+
1559
+ ```yaml
1560
+ type: APP_DELEGATE
1561
+ identifier:
1562
+ name: AppDelegate.swift
1563
+
1564
+ hooks:
1565
+ - type: APP_DELEGATE_IMPORT_HOOK # Injects at the top of the file (imports)
1566
+ identifier:
1567
+ name: UIkitimprot # Human-readable hook name
1568
+ key: rtmoen # Unique 6-char alphanumeric key
1569
+ content: import UIKit # Swift import statement
1570
+
1571
+ - type: APP_DELEGATE_INITIALIZATION_HOOK # Injects inside didFinishLaunchingWithOptions
1572
+ identifier:
1573
+ name: logsinit # Human-readable hook name
1574
+ key: 2b582i # Unique 6-char alphanumeric key
1575
+ content: Logs.init() # Swift initialization code
1576
+
1577
+ parameters: # Template variables — same as all other custom files
1578
+ 74q7xg:
1579
+ parameter:
1580
+ identifier:
1581
+ name: vars1 # Variable name — use {{vars1}} in hook content
1582
+ dataType:
1583
+ scalarType: String
1584
+ value:
1585
+ inputValue:
1586
+ serializedValue: var val # Default value
1587
+ ```
1588
+
1589
+ **Key fields:**
1590
+
1591
+ | Field | Type | Notes |
1592
+ |---|---|---|
1593
+ | `type` | string | Always `APP_DELEGATE` for this file. |
1594
+ | `identifier.name` | string | Always `AppDelegate.swift`. |
1595
+ | `hooks` | list | List of Swift code injection hooks. |
1596
+ | `hooks[].type` | enum | `APP_DELEGATE_IMPORT_HOOK` (imports) or `APP_DELEGATE_INITIALIZATION_HOOK` (init code). |
1597
+ | `hooks[].identifier.name` | string | Human-readable name for the hook. |
1598
+ | `hooks[].identifier.key` | string | Unique 6-char alphanumeric key. |
1599
+ | `hooks[].content` | string | Raw Swift code to inject. For imports: a single `import` statement. For initialization: Swift code that runs during app launch. |
1600
+ | `parameters` | map | Template variables keyed by parameter ID. Use `{{variableName}}` in `content` to reference them. Same structure as all other custom files. |
1601
+
1602
+ ### Generated AppDelegate execution order
1603
+
1604
+ The generated `AppDelegate.swift` follows this structure:
1605
+
1606
+ ```
1607
+ 1. import Flutter ← Standard Flutter import
1608
+ 2. import UIKit ← APP_DELEGATE_IMPORT_HOOK entries
1609
+ 3. import MySDK ← APP_DELEGATE_IMPORT_HOOK entries
1610
+ 4.
1611
+ 5. @UIApplicationMain
1612
+ 6. class AppDelegate: FlutterAppDelegate {
1613
+ 7. override func application(...) -> Bool {
1614
+ 8. GeneratedPluginRegistrant.register(with: self)
1615
+ 9. Logs.init() ← APP_DELEGATE_INITIALIZATION_HOOK entries
1616
+ 10. MySDK.configure() ← APP_DELEGATE_INITIALIZATION_HOOK entries
1617
+ 11. return super.application(...)
1618
+ 12. }
1619
+ 13. }
1620
+ ```
1621
+
1622
+ - **Import hooks** are placed at the top of the file alongside the standard Flutter/UIKit imports.
1623
+ - **Initialization hooks** are placed inside `application(_:didFinishLaunchingWithOptions:)` after plugin registration but before the `return` statement.
1624
+
1625
+ ### Common hook content examples
1626
+
1627
+ **Import a framework:**
1628
+ ```yaml
1629
+ - type: APP_DELEGATE_IMPORT_HOOK
1630
+ identifier:
1631
+ name: firebaseImport
1632
+ key: abc123
1633
+ content: import Firebase
1634
+ ```
1635
+
1636
+ **Initialize an SDK:**
1637
+ ```yaml
1638
+ - type: APP_DELEGATE_INITIALIZATION_HOOK
1639
+ identifier:
1640
+ name: firebaseInit
1641
+ key: def456
1642
+ content: FirebaseApp.configure()
1643
+ ```
1644
+
1645
+ **Multi-line initialization:**
1646
+ ```yaml
1647
+ - type: APP_DELEGATE_INITIALIZATION_HOOK
1648
+ identifier:
1649
+ name: oneSignalSetup
1650
+ key: ghi789
1651
+ content: "OneSignal.initialize(\"{{appId}}\")\nOneSignal.Notifications.requestPermission({ accepted in })"
1652
+ ```
1653
+
1654
+ ### API capabilities
1655
+
1656
+ | Operation | Status | Notes |
1657
+ |---|---|---|
1658
+ | Reading config | Works | Full hook and parameter data returned. |
1659
+ | Adding hooks via API | Expected to work | Same structure as other custom file hooks. |
1660
+ | Adding parameters via API | Expected to work | Same structure as other custom file parameters. |
1661
+ | Hook name validation gap | Caution | Same as other custom files — avoid hyphens in names, use camelCase or spaces. |
1662
+
1663
+ ---
1664
+
1245
1665
  ## custom-file/id-PROGUARD
1246
1666
 
1247
1667
  **Purpose:** Allows injecting custom ProGuard rules into the generated `proguard-rules.pro` file for Android builds. Controls code shrinking, obfuscation, and optimization rules.
@@ -335,6 +335,26 @@ widgetProperty:
335
335
  inputValue: 460
336
336
  ```
337
337
 
338
+ **Translatable text (i18n):**
339
+
340
+ Use `translatableText` inside `inputValue` to pass a string that should be translated at runtime based on the app's current locale. This is the correct way to make component parameter text translatable.
341
+
342
+ ```yaml
343
+ paramIdentifier:
344
+ name: title
345
+ key: p1titl
346
+ inputValue:
347
+ translatableText:
348
+ translationIdentifier:
349
+ key: ms01ttl1 # References languages/translation/id-ms01ttl1
350
+ textValue:
351
+ inputValue: Histamine / Low DAO # English default / editor preview
352
+ ```
353
+
354
+ Each `translationIdentifier.key` must have a corresponding `languages/translation/id-<key>` file with translations for all supported languages (see `01-project-files.md` for the translation file schema).
355
+
356
+ > **Note:** `translationIdentifier` is NOT valid as a direct sibling of `paramIdentifier` on a parameterPass — it must be nested inside `inputValue.translatableText`. Placing it at the wrong level causes a validation error.
357
+
338
358
  ---
339
359
 
340
360
  ## 7. isDummyRoot
@@ -831,4 +831,19 @@ inputValue:
831
831
  value: "4294967295" # ARGB integer as string
832
832
  ```
833
833
 
834
+ **Translatable text (i18n):**
835
+
836
+ Used when passing a string that should be translated at runtime based on the app's locale. Wraps `translationIdentifier` and `textValue` inside `inputValue.translatableText`. Commonly used in component parameter passes to make per-instance text translatable.
837
+
838
+ ```yaml
839
+ inputValue:
840
+ translatableText:
841
+ translationIdentifier:
842
+ key: ms01ttl1 # References languages/translation/id-ms01ttl1
843
+ textValue:
844
+ inputValue: Histamine / Low DAO # English default
845
+ ```
846
+
847
+ Each key must have a corresponding translation file at `languages/translation/id-<key>` with entries for all supported languages. See `01-project-files.md` for the translation file schema.
848
+
834
849
  The `mostRecentInputValue` field must always stay in sync with `inputValue` when both are present.
@@ -1,6 +1,6 @@
1
1
  # Custom Code
2
2
 
3
- FlutterFlow supports four types of custom code: **Custom Actions** (async Dart functions with side effects), **Custom Functions** (pure synchronous Dart functions), **Custom Widgets** (Flutter widgets with parameters), and **AI Agents** (LLM-powered processing pipelines). Additionally, **App Action Components** provide reusable action chains that can be invoked from any page, and **Custom Files** allow overriding special project-level files like `main.dart` and `AndroidManifest.xml`.
3
+ FlutterFlow supports four types of custom code: **Custom Actions** (async Dart functions with side effects), **Custom Functions** (pure synchronous Dart functions), **Custom Widgets** (Flutter widgets with parameters), and **AI Agents** (LLM-powered processing pipelines). Additionally, **App Action Components** provide reusable action chains that can be invoked from any page, and **Custom Files** allow overriding special project-level files like `main.dart`, `AndroidManifest.xml`, `Info.plist`, `proguard-rules.pro`, and `build.gradle`.
4
4
 
5
5
  ---
6
6
 
@@ -706,76 +706,21 @@ variable:
706
706
 
707
707
  ## Custom Files
708
708
 
709
- Custom Files are special project-level files such as `main.dart` (the app entry point) and `AndroidManifest.xml`. Each has a metadata YAML file and a companion code file. They live under `custom-file/`.
709
+ Custom Files are special project-level files that override platform-specific configuration (e.g., `main.dart`, `AndroidManifest.xml`, `Info.plist`, `proguard-rules.pro`, `build.gradle`). Each has a metadata YAML file and a companion code file under `custom-file/`.
710
710
 
711
- ### File layout
712
-
713
- ```
714
- custom-file/
715
- id-MAIN.yaml # Metadata for main.dart
716
- id-MAIN/custom-file-code.dart.yaml # main.dart Dart code
717
- id-ANDROID_MANIFEST.yaml # Metadata for AndroidManifest.xml
718
- id-ANDROID_MANIFEST/custom-file-code.dart.yaml # AndroidManifest.xml content
719
- ```
720
-
721
- ### MAIN metadata (id-MAIN.yaml)
722
-
723
- ```yaml
724
- type: MAIN
725
- isUnlocked: false
726
- actions:
727
- - type: FINAL_ACTION
728
- identifier:
729
- name: initializeUlink
730
- key: t6snt
731
- projectId: ulink-21sajt
732
- description: ""
733
- ```
734
-
735
- ### ANDROID_MANIFEST metadata (id-ANDROID_MANIFEST.yaml)
736
-
737
- ```yaml
738
- type: ANDROID_MANIFEST
739
- identifier:
740
- name: AndroidManifest.xml
741
- isUnlocked: true
742
- parameters:
743
- 29c3b7b9-afe7-4429-9d20-abb2e09f7a40:
744
- parameter:
745
- identifier:
746
- name: ulinkDomain
747
- dataType:
748
- scalarType: String
749
- value:
750
- variable:
751
- source: DEV_ENVIRONMENT
752
- baseVariable:
753
- environmentValue:
754
- identifier:
755
- name: ulinkDomain
756
- key: eji5p6
757
- description: ""
758
- ```
759
-
760
- ### Definition fields
761
-
762
- | Field | Required | Description |
763
- |-------|----------|-------------|
764
- | `type` | Yes | File type: `MAIN` or `ANDROID_MANIFEST` |
765
- | `identifier` | For ANDROID_MANIFEST | `name` of the file |
766
- | `isUnlocked` | No | Whether the file is editable (default: false) |
767
- | `actions` | No | List of final actions (library initializers) that run at startup |
768
- | `parameters` | No | Template parameter substitutions for the code file |
769
- | `description` | No | Human-readable description |
770
-
771
- ### Code files
772
-
773
- - **MAIN:** `custom-file/id-MAIN/custom-file-code.dart.yaml` contains the full `main.dart` with imports, `main()` function, Firebase/Supabase initialization, and library init calls.
774
- - **ANDROID_MANIFEST:** `custom-file/id-ANDROID_MANIFEST/custom-file-code.dart.yaml` contains the XML manifest with template placeholders (e.g., `{{unlinkDomain}}`, `{{ulink-21sajt.ulinkSchema}}`). Parameters in metadata map to these placeholders.
711
+ > **Full documentation:** Custom Files are documented in detail in [01-project-files.md](01-project-files.md) under the `custom-file/id-*` sections, since they are project-level configuration files. See those sections for complete schemas, hook types, parameters, API capabilities, and warnings.
775
712
 
776
- ### Parameter substitution
713
+ ### Quick reference
777
714
 
778
- Parameters in ANDROID_MANIFEST metadata define template variables. Each key is a UUID, and the value specifies where the parameter's value comes from (typically `DEV_ENVIRONMENT`). These get substituted into the code file's template placeholders at build time.
715
+ | Type | File key | Description |
716
+ |------|----------|-------------|
717
+ | `MAIN` | `custom-file/id-MAIN` | App entry point (`main.dart`) — lifecycle actions (INITIAL/FINAL) |
718
+ | `ANDROID_MANIFEST` | `custom-file/id-ANDROID_MANIFEST` | Android manifest — XML injection hooks, template parameters |
719
+ | `INFO_PLIST` | `custom-file/id-INFO_PLIST` | iOS Info.plist — property injection hooks, template parameters |
720
+ | `ENTITLEMENTS` | `custom-file/id-ENTITLEMENTS` | iOS Runner.entitlements — capability entitlement injection |
721
+ | `APP_DELEGATE` | `custom-file/id-APP_DELEGATE` | iOS AppDelegate.swift — import and initialization code injection |
722
+ | `PROGUARD` | `custom-file/id-PROGUARD` | ProGuard rules — rule injection hooks |
723
+ | `BUILD_GRADLE` | `custom-file/id-BUILD_GRADLE` | Gradle config — plugin/dependency/repository injection hooks |
779
724
 
780
725
  ---
781
726
 
@@ -788,4 +733,4 @@ Parameters in ANDROID_MANIFEST metadata define template variables. Each key is a
788
733
  | Custom Widget | `custom-widgets/id-<key>.yaml` | (code embedded or separate) | `custom-widgets/` |
789
734
  | AI Agent | `agent/id-<key>.yaml` | N/A (no code file) | `agent/` |
790
735
  | App Action Component | `app-action-components/id-<key>.yaml` | N/A (actions are inline) | `app-action-components/` |
791
- | Custom File | `custom-file/id-<TYPE>.yaml` | `custom-file/id-<TYPE>/custom-file-code.dart.yaml` | `custom-file/` |
736
+ | Custom File | `custom-file/id-<TYPE>` | `custom-file/id-<TYPE>/custom-file-code.dart` | `custom-file/` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flutterflow-mcp",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "MCP server for the FlutterFlow Project API — AI-assisted FlutterFlow development through Claude and other MCP-compatible clients",
5
5
  "type": "module",
6
6
  "main": "build/index.js",