community-ff-mcp 0.4.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 (116) hide show
  1. package/README.md +300 -0
  2. package/build/api/flutterflow.d.ts +11 -0
  3. package/build/api/flutterflow.js +61 -0
  4. package/build/index.d.ts +2 -0
  5. package/build/index.js +78 -0
  6. package/build/prompts/dev-workflow.d.ts +2 -0
  7. package/build/prompts/dev-workflow.js +68 -0
  8. package/build/prompts/generate-page.d.ts +2 -0
  9. package/build/prompts/generate-page.js +37 -0
  10. package/build/prompts/inspect-project.d.ts +2 -0
  11. package/build/prompts/inspect-project.js +30 -0
  12. package/build/prompts/modify-component.d.ts +2 -0
  13. package/build/prompts/modify-component.js +40 -0
  14. package/build/resources/docs.d.ts +2 -0
  15. package/build/resources/docs.js +76 -0
  16. package/build/resources/projects.d.ts +3 -0
  17. package/build/resources/projects.js +60 -0
  18. package/build/tools/find-component-usages.d.ts +21 -0
  19. package/build/tools/find-component-usages.js +216 -0
  20. package/build/tools/find-page-navigations.d.ts +26 -0
  21. package/build/tools/find-page-navigations.js +220 -0
  22. package/build/tools/get-api-endpoints.d.ts +2 -0
  23. package/build/tools/get-api-endpoints.js +126 -0
  24. package/build/tools/get-app-settings.d.ts +2 -0
  25. package/build/tools/get-app-settings.js +169 -0
  26. package/build/tools/get-app-state.d.ts +2 -0
  27. package/build/tools/get-app-state.js +96 -0
  28. package/build/tools/get-component-summary.d.ts +22 -0
  29. package/build/tools/get-component-summary.js +195 -0
  30. package/build/tools/get-custom-code.d.ts +2 -0
  31. package/build/tools/get-custom-code.js +380 -0
  32. package/build/tools/get-data-models.d.ts +2 -0
  33. package/build/tools/get-data-models.js +266 -0
  34. package/build/tools/get-editing-guide.d.ts +7 -0
  35. package/build/tools/get-editing-guide.js +185 -0
  36. package/build/tools/get-general-settings.d.ts +2 -0
  37. package/build/tools/get-general-settings.js +116 -0
  38. package/build/tools/get-in-app-purchases.d.ts +2 -0
  39. package/build/tools/get-in-app-purchases.js +51 -0
  40. package/build/tools/get-integrations.d.ts +2 -0
  41. package/build/tools/get-integrations.js +137 -0
  42. package/build/tools/get-page-by-name.d.ts +3 -0
  43. package/build/tools/get-page-by-name.js +56 -0
  44. package/build/tools/get-page-summary.d.ts +22 -0
  45. package/build/tools/get-page-summary.js +205 -0
  46. package/build/tools/get-project-config.d.ts +2 -0
  47. package/build/tools/get-project-config.js +216 -0
  48. package/build/tools/get-project-setup.d.ts +2 -0
  49. package/build/tools/get-project-setup.js +212 -0
  50. package/build/tools/get-theme.d.ts +2 -0
  51. package/build/tools/get-theme.js +199 -0
  52. package/build/tools/get-yaml-docs.d.ts +6 -0
  53. package/build/tools/get-yaml-docs.js +116 -0
  54. package/build/tools/get-yaml.d.ts +2 -0
  55. package/build/tools/get-yaml.js +53 -0
  56. package/build/tools/list-files.d.ts +3 -0
  57. package/build/tools/list-files.js +49 -0
  58. package/build/tools/list-pages.d.ts +25 -0
  59. package/build/tools/list-pages.js +101 -0
  60. package/build/tools/list-projects.d.ts +3 -0
  61. package/build/tools/list-projects.js +23 -0
  62. package/build/tools/search-project-files.d.ts +2 -0
  63. package/build/tools/search-project-files.js +69 -0
  64. package/build/tools/sync-project.d.ts +3 -0
  65. package/build/tools/sync-project.js +147 -0
  66. package/build/tools/update-yaml.d.ts +3 -0
  67. package/build/tools/update-yaml.js +24 -0
  68. package/build/tools/validate-yaml.d.ts +3 -0
  69. package/build/tools/validate-yaml.js +39 -0
  70. package/build/utils/batch-process.d.ts +2 -0
  71. package/build/utils/batch-process.js +10 -0
  72. package/build/utils/cache.d.ts +58 -0
  73. package/build/utils/cache.js +199 -0
  74. package/build/utils/decode-yaml.d.ts +7 -0
  75. package/build/utils/decode-yaml.js +31 -0
  76. package/build/utils/page-summary/action-summarizer.d.ts +24 -0
  77. package/build/utils/page-summary/action-summarizer.js +291 -0
  78. package/build/utils/page-summary/formatter.d.ts +13 -0
  79. package/build/utils/page-summary/formatter.js +129 -0
  80. package/build/utils/page-summary/node-extractor.d.ts +24 -0
  81. package/build/utils/page-summary/node-extractor.js +227 -0
  82. package/build/utils/page-summary/tree-walker.d.ts +6 -0
  83. package/build/utils/page-summary/tree-walker.js +55 -0
  84. package/build/utils/page-summary/types.d.ts +58 -0
  85. package/build/utils/page-summary/types.js +4 -0
  86. package/build/utils/parse-folders.d.ts +9 -0
  87. package/build/utils/parse-folders.js +29 -0
  88. package/build/utils/resolve-data-type.d.ts +2 -0
  89. package/build/utils/resolve-data-type.js +18 -0
  90. package/build/utils/topic-map.d.ts +7 -0
  91. package/build/utils/topic-map.js +122 -0
  92. package/docs/ff-yaml/00-overview.md +166 -0
  93. package/docs/ff-yaml/01-project-files.md +2309 -0
  94. package/docs/ff-yaml/02-pages.md +572 -0
  95. package/docs/ff-yaml/03-components.md +784 -0
  96. package/docs/ff-yaml/04-widgets/README.md +122 -0
  97. package/docs/ff-yaml/04-widgets/button.md +444 -0
  98. package/docs/ff-yaml/04-widgets/container.md +358 -0
  99. package/docs/ff-yaml/04-widgets/dropdown.md +579 -0
  100. package/docs/ff-yaml/04-widgets/form.md +256 -0
  101. package/docs/ff-yaml/04-widgets/image.md +276 -0
  102. package/docs/ff-yaml/04-widgets/layout.md +355 -0
  103. package/docs/ff-yaml/04-widgets/misc.md +553 -0
  104. package/docs/ff-yaml/04-widgets/text-field.md +326 -0
  105. package/docs/ff-yaml/04-widgets/text.md +302 -0
  106. package/docs/ff-yaml/05-actions.md +953 -0
  107. package/docs/ff-yaml/06-variables.md +849 -0
  108. package/docs/ff-yaml/07-data.md +591 -0
  109. package/docs/ff-yaml/08-custom-code.md +736 -0
  110. package/docs/ff-yaml/09-theming.md +638 -0
  111. package/docs/ff-yaml/10-editing-guide.md +497 -0
  112. package/docs/ff-yaml/README.md +105 -0
  113. package/package.json +59 -0
  114. package/skills/community-ff-mcp/SKILL.md +201 -0
  115. package/skills/ff-widget-patterns.md +141 -0
  116. package/skills/ff-yaml-dev.md +70 -0
@@ -0,0 +1,784 @@
1
+ # Components
2
+
3
+ A component in FlutterFlow is a reusable `Container`-rooted widget tree. Components are structurally identical to pages but use `Container` instead of `Scaffold` as the root and support being embedded inside other pages or components via `componentClassKeyRef`.
4
+
5
+ ---
6
+
7
+ ## 1. Component Metadata
8
+
9
+ **File key:** `component/id-Container_XXX`
10
+
11
+ ### Component with string parameters
12
+
13
+ ```yaml
14
+ name: PremuimContentWall
15
+ description: "this would be a widget that acts as a blocked content behind pay button, maybe blurred view being a subscripe button\n"
16
+ params:
17
+ kw4qti:
18
+ identifier:
19
+ name: title
20
+ key: kw4qti
21
+ dataType:
22
+ scalarType: String
23
+ nonNullable: false
24
+ psru0e:
25
+ identifier:
26
+ name: subtitle
27
+ key: psru0e
28
+ dataType:
29
+ scalarType: String
30
+ nonNullable: false
31
+ node:
32
+ key: Container_ffzg5wc5
33
+ ```
34
+
35
+ ### Component with Action (callback) parameters
36
+
37
+ ```yaml
38
+ name: ExtendAnalysisComponent
39
+ description: ""
40
+ params:
41
+ 0qlpdk:
42
+ identifier:
43
+ name: foodId
44
+ key: 0qlpdk
45
+ dataType:
46
+ scalarType: String
47
+ nonNullable: true
48
+ 326n4o:
49
+ identifier:
50
+ name: extendAnalysis
51
+ key: 326n4o
52
+ dataType:
53
+ scalarType: Action
54
+ nonNullable: true
55
+ y3mj9l:
56
+ identifier:
57
+ name: deleteFood
58
+ key: y3mj9l
59
+ dataType:
60
+ scalarType: Action
61
+ nonNullable: true
62
+ node:
63
+ key: Container_9jc8lryj
64
+ ```
65
+
66
+ ### Component with state fields
67
+
68
+ ```yaml
69
+ name: EditProfile
70
+ description: ""
71
+ node:
72
+ key: Container_w4a9oyew
73
+ classModel:
74
+ stateFields:
75
+ - parameter:
76
+ identifier:
77
+ name: indexAnalysisSelect
78
+ key: ctls8
79
+ dataType:
80
+ scalarType: Integer
81
+ - parameter:
82
+ identifier:
83
+ name: valueAnalysisSelected
84
+ key: o29r4
85
+ dataType:
86
+ scalarType: String
87
+ nonNullable: false
88
+ ```
89
+
90
+ ### Schema reference
91
+
92
+ | Field | Type | Required | Description |
93
+ |---|---|---|---|
94
+ | `name` | string | yes | Human-readable component name |
95
+ | `description` | string | no | Component description |
96
+ | `params` | map | no | Component parameters keyed by param key hash |
97
+ | `node.key` | string | yes | Root Container key (format: `Container_XXXXXXXX`) |
98
+ | `classModel` | object | no | State fields (same structure as pages) |
99
+
100
+ ---
101
+
102
+ ## 2. How Components Differ from Pages
103
+
104
+ | Aspect | Page | Component |
105
+ |---|---|---|
106
+ | Root widget type | `Scaffold` | `Container` |
107
+ | Root key format | `Scaffold_XXXXXXXX` | `Container_XXXXXXXX` |
108
+ | Tree outline file key | `page-widget-tree-outline` | `component-widget-tree-outline` |
109
+ | File key prefix | `page/id-Scaffold_XXX` | `component/id-Container_XXX` |
110
+ | Can be embedded in other widgets | No | Yes (via `componentClassKeyRef`) |
111
+ | Supports route params (`params`) | Yes (passed via navigation) | Yes (passed via `parameterValues`) |
112
+ | Supports state fields | Yes (`classModel.stateFields`) | Yes (`classModel.stateFields`) |
113
+ | Has `isDummyRoot` | No | Yes (root container is a transparent wrapper) |
114
+ | Navigation target | Yes | No |
115
+ | `body` in tree outline | Yes (`node.body`) | No (`node.children` directly) |
116
+
117
+ ---
118
+
119
+ ## 3. Component Parameters
120
+
121
+ Parameters are defined in `params` at the top level of the component metadata file.
122
+
123
+ ### Parameter definition schema
124
+
125
+ ```yaml
126
+ params:
127
+ <paramKey>:
128
+ identifier:
129
+ name: <paramName>
130
+ key: <paramKey>
131
+ defaultValue: # optional
132
+ serializedValue: <value>
133
+ dataType:
134
+ scalarType: <type>
135
+ nonNullable: <bool>
136
+ subType: # for Document/DataStruct types
137
+ collectionIdentifier:
138
+ name: <name>
139
+ key: <key>
140
+ ```
141
+
142
+ ---
143
+
144
+ ## 4. Parameter Types
145
+
146
+ | scalarType | Description | Extra fields |
147
+ |---|---|---|
148
+ | `String` | Text value | -- |
149
+ | `Integer` | Integer number | -- |
150
+ | `Double` | Floating-point number | -- |
151
+ | `Boolean` | True/false | -- |
152
+ | `Color` | ARGB color value | -- |
153
+ | `Action` | Callback function passed from parent. Triggers appear as `CALLBACK-<key>`. | -- |
154
+ | `Document` | Firestore document reference | `subType.collectionIdentifier` required |
155
+ | `DataStruct` | Custom data type | `subType` with struct definition |
156
+
157
+ ### Action parameter example
158
+
159
+ When a component defines an Action parameter:
160
+
161
+ ```yaml
162
+ params:
163
+ 326n4o:
164
+ identifier:
165
+ name: extendAnalysis
166
+ key: 326n4o
167
+ dataType:
168
+ scalarType: Action
169
+ nonNullable: true
170
+ ```
171
+
172
+ It is invoked inside the component via `executeCallbackAction`:
173
+
174
+ ```yaml
175
+ key: gt91qt34
176
+ executeCallbackAction:
177
+ parameterIdentifier:
178
+ name: extendAnalysis
179
+ key: 326n4o
180
+ ```
181
+
182
+ The trigger type for callbacks is `CALLBACK-<paramKey>` (e.g., `id-CALLBACK-bins86`).
183
+
184
+ ---
185
+
186
+ ## 5. Component State
187
+
188
+ State fields work identically to page state fields. Defined in `classModel.stateFields`:
189
+
190
+ ```yaml
191
+ classModel:
192
+ stateFields:
193
+ - parameter:
194
+ identifier:
195
+ name: indexAnalysisSelect
196
+ key: ctls8
197
+ dataType:
198
+ scalarType: Integer
199
+ - parameter:
200
+ identifier:
201
+ name: valueAnalysisSelected
202
+ key: o29r4
203
+ dataType:
204
+ scalarType: String
205
+ nonNullable: false
206
+ ```
207
+
208
+ State updates from actions use `stateVariableType: WIDGET_CLASS_STATE`:
209
+
210
+ ```yaml
211
+ localStateUpdate:
212
+ updates:
213
+ - fieldIdentifier:
214
+ key: o29r4
215
+ setValue:
216
+ variable:
217
+ source: LOCAL_STATE
218
+ baseVariable:
219
+ localState:
220
+ fieldIdentifier:
221
+ name: user
222
+ key: cxensw6z
223
+ stateVariableType: APP_STATE
224
+ updateType: WIDGET
225
+ stateVariableType: WIDGET_CLASS_STATE
226
+ key: qe82s7k1
227
+ ```
228
+
229
+ ---
230
+
231
+ ## 6. Referencing a Component from Another Widget
232
+
233
+ To embed a component inside a page or another component, the host widget uses `componentClassKeyRef` and `parameterValues`.
234
+
235
+ ### Minimal reference (no params passed)
236
+
237
+ ```yaml
238
+ key: Container_jdtt4zdw
239
+ type: Container
240
+ props: {}
241
+ parameterValues:
242
+ widgetClassNodeKeyRef:
243
+ key: Container_qtmqdlq9
244
+ componentClassKeyRef:
245
+ key: Container_qtmqdlq9
246
+ ```
247
+
248
+ ### Reference with parameter passes
249
+
250
+ ```yaml
251
+ key: Container_h7c8oc57
252
+ type: Container
253
+ props:
254
+ expanded:
255
+ expandedType: EXPANDED
256
+ parameterValues:
257
+ parameterPasses:
258
+ w2td86:
259
+ paramIdentifier:
260
+ name: isPage
261
+ key: w2td86
262
+ inputValue:
263
+ serializedValue: "true"
264
+ widgetClassNodeKeyRef:
265
+ key: Container_od80u8ml
266
+ componentClassKeyRef:
267
+ key: Container_od80u8ml
268
+ ```
269
+
270
+ ### Reference with `updateContainingClassOnSetState`
271
+
272
+ When the component's state changes should trigger a rebuild of the parent:
273
+
274
+ ```yaml
275
+ key: Container_j48t6fjy
276
+ type: Container
277
+ props:
278
+ expanded:
279
+ expandedType: UNEXPANDED
280
+ responsiveVisibility: {}
281
+ parameterValues:
282
+ updateContainingClassOnSetState: true
283
+ widgetClassNodeKeyRef:
284
+ key: Container_ngmk9lon
285
+ componentClassKeyRef:
286
+ key: Container_ngmk9lon
287
+ ```
288
+
289
+ ### Required fields for component references
290
+
291
+ | Field | Description |
292
+ |---|---|
293
+ | `componentClassKeyRef.key` | The root Container key of the referenced component |
294
+ | `parameterValues.widgetClassNodeKeyRef.key` | Must match `componentClassKeyRef.key` |
295
+ | `parameterValues.parameterPasses` | Map of parameter values to pass (optional) |
296
+ | `parameterValues.updateContainingClassOnSetState` | Whether parent rebuilds on component state change (optional) |
297
+
298
+ ### Parameter pass value sources
299
+
300
+ Parameter values can come from multiple sources:
301
+
302
+ **Static value:**
303
+ ```yaml
304
+ paramIdentifier:
305
+ name: isPage
306
+ key: w2td86
307
+ inputValue:
308
+ serializedValue: "true"
309
+ ```
310
+
311
+ **Variable reference:**
312
+ ```yaml
313
+ paramIdentifier:
314
+ name: endDate
315
+ key: 0u8o42
316
+ variable:
317
+ source: GLOBAL_PROPERTIES
318
+ baseVariable:
319
+ globalProperties:
320
+ property: CURRENT_TIMESTAMP
321
+ ```
322
+
323
+ **Widget property pass-through:**
324
+ ```yaml
325
+ paramIdentifier:
326
+ name: dimensions
327
+ key: 5f7c90
328
+ widgetProperty:
329
+ dimensions:
330
+ width:
331
+ pixelsValue:
332
+ inputValue: Infinity
333
+ height:
334
+ pixelsValue:
335
+ inputValue: 460
336
+ ```
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
+
358
+ ---
359
+
360
+ ## 7. isDummyRoot
361
+
362
+ Every component's root Container node has `isDummyRoot: true`. This marks it as a transparent wrapper that FlutterFlow uses internally. The root container typically has minimal styling (transparent background, no dimensions).
363
+
364
+ ```yaml
365
+ key: Container_ffzg5wc5
366
+ type: Container
367
+ props:
368
+ container:
369
+ boxDecoration:
370
+ colorValue:
371
+ inputValue:
372
+ value: "0" # fully transparent
373
+ name: PremuimContentWall
374
+ isDummyRoot: true
375
+ ```
376
+
377
+ The `isDummyRoot` flag tells FlutterFlow to treat this Container as the component boundary, not as a visible widget. The actual visual content starts with the first child.
378
+
379
+ ---
380
+
381
+ ## 8. Component Widget Tree Outline
382
+
383
+ **File key:** `component/id-Container_XXX/component-widget-tree-outline`
384
+
385
+ Structurally similar to page tree outlines, but uses `children` directly under the root node instead of `body`:
386
+
387
+ ```yaml
388
+ node:
389
+ key: Container_ffzg5wc5
390
+ children:
391
+ - key: Container_tfliyp8f
392
+ children:
393
+ - key: Stack_xkb3xt70
394
+ children:
395
+ - key: Container_bwqw67by
396
+ children:
397
+ - key: Column_413255jc
398
+ children:
399
+ - key: Container_yl6sd9jh
400
+ - key: Container_2txrctco
401
+ - key: Container_056rmdev
402
+ - key: Container_dxsdiyyn
403
+ - key: Blur_1c9k5x0n
404
+ children:
405
+ - key: Container_q7m3k7c9
406
+ - key: Container_1k4ettsz
407
+ children:
408
+ - key: Column_ue835m8w
409
+ children:
410
+ - key: Icon_28qttdd5
411
+ - key: Text_vo3w8rsz
412
+ - key: Text_tg7gbbph
413
+ - key: Button_drlhww71
414
+ ```
415
+
416
+ ---
417
+
418
+ ## 9. File Key Patterns
419
+
420
+ | Pattern | Purpose |
421
+ |---|---|
422
+ | `component/id-Container_XXX` | Component metadata (name, params, classModel) |
423
+ | `component/id-Container_XXX/component-widget-tree-outline` | Widget tree hierarchy (keys only) |
424
+ | `component/id-Container_XXX/component-widget-tree-outline/node/id-Widget_YYY` | Individual widget definition |
425
+ | `component/id-Container_XXX/component-widget-tree-outline/node/id-Widget_YYY/trigger_actions/id-ON_TAP` | Action trigger |
426
+ | `component/id-Container_XXX/component-widget-tree-outline/node/id-Widget_YYY/trigger_actions/id-ON_TAP/action/id-ACTIONKEY` | Individual action |
427
+ | `component/id-Container_XXX/component-widget-tree-outline/node/id-Widget_YYY/trigger_actions/id-CALLBACK-<paramKey>/action/id-ACTIONKEY` | Callback action (for Action-type params) |
428
+
429
+ ### Notes
430
+
431
+ - **Component node files** follow the exact same structure as page node files. The only difference is the file key prefix (`component/` vs `page/`).
432
+ - **The tree outline key** is `component-widget-tree-outline` (not `page-widget-tree-outline`).
433
+ - **CALLBACK triggers** appear when an Action parameter is executed from a child widget that is itself a component reference. The trigger key includes the param key (e.g., `CALLBACK-bins86`).
434
+
435
+ ---
436
+
437
+ ## 10. Components in Summary Output
438
+
439
+ The `get_page_summary` and `get_component_summary` tools resolve component references and display them with a distinct format in the widget tree. This makes it easy to distinguish regular widgets from embedded components at a glance.
440
+
441
+ ### Format
442
+
443
+ Component instances appear as `[ComponentName] (Container_ID)` instead of just `Container`:
444
+
445
+ ```
446
+ FeedHomePage (Scaffold_e5ows2lg) — folder: home
447
+
448
+ ON_INIT_STATE → [customAction: checkNotificationPermissionResult, ...]
449
+
450
+ Widget Tree:
451
+ └── [body] Container
452
+ └── Column
453
+ └── Column
454
+ ├── [Header] (Container_ur4ml9qw)
455
+ ├── [SearchBar] (Container_qw4kqc4l)
456
+ └── Container
457
+ └── [PostsList] (Container_pgvko7fz)
458
+ ```
459
+
460
+ ### Reading the output
461
+
462
+ | Element | Meaning |
463
+ |---|---|
464
+ | `Container`, `Column`, `Row`, etc. | Regular widget — defined inline on this page/component |
465
+ | `[Header] (Container_ur4ml9qw)` | Component instance — `Header` is the component name, `Container_ur4ml9qw` is the component ID |
466
+ | `[body]`, `[appBar]`, etc. | Slot prefix — which slot of the parent widget this node fills |
467
+ | `→ ON_TAP → [navigate: to page]` | Trigger — action(s) attached to this widget |
468
+
469
+ ### Drilling into components
470
+
471
+ The component ID in parentheses (e.g. `Container_ur4ml9qw`) can be used to retrieve the full component structure:
472
+
473
+ - **`get_component_summary`** — pass `componentId: "Container_ur4ml9qw"` (or `componentName: "Header"`) to see the component's internal widget tree, params, and actions
474
+ - **`get_project_yaml`** — pass `fileName: "component/id-Container_ur4ml9qw"` to get the raw component metadata YAML
475
+ - **`find_component_usages`** — pass `componentId: "Container_ur4ml9qw"` to find all pages and components where this component is used
476
+
477
+ ### Nested components
478
+
479
+ Components can contain other components. The same `[Name] (ID)` format appears at any nesting level:
480
+
481
+ ```
482
+ PostsList (Container_pgvko7fz)
483
+ Params: customAudienceFilter (Enum), profileUserId (String), fetchType (Enum), itemId (String)
484
+
485
+ Widget Tree:
486
+ └── ConditionalBuilder
487
+ ├── PlaceholderWidget
488
+ │ └── ListView
489
+ │ └── [PostCard] (Container_abc12345) → CALLBACK → [updateState]
490
+ └── PlaceholderWidget
491
+ └── Column
492
+ └── [EmptyState] (Container_xyz98765)
493
+ ```
494
+
495
+ ---
496
+
497
+ ## 11. Creating a New Component
498
+
499
+ Creating a component requires pushing multiple files in a **single** `update_project_yaml` call, similar to adding widgets to a page.
500
+
501
+ ### Required files
502
+
503
+ | # | File Key | Purpose |
504
+ |---|----------|---------|
505
+ | 1 | `component/id-Container_XXX` | Component metadata (name, params, state) |
506
+ | 2 | `component/id-Container_XXX/component-widget-tree-outline` | Widget tree hierarchy |
507
+ | 3 | `component/id-Container_XXX/component-widget-tree-outline/node/id-Container_XXX` | Root Container node (`isDummyRoot: true`) |
508
+ | 4 | `component/id-Container_XXX/component-widget-tree-outline/node/id-Widget_YYY` | One file per child widget |
509
+
510
+ ### Step-by-step example
511
+
512
+ **Task:** Create a reusable `DealCard` component with `title` (String), `subtitle` (String), and `onTap` (Action) parameters.
513
+
514
+ **File 1 — Component metadata:**
515
+ ```yaml
516
+ # File key: component/id-Container_dc01root
517
+ name: DealCard
518
+ description: "Reusable card displaying a deal with title, subtitle, and tap action"
519
+ params:
520
+ p1titl:
521
+ identifier:
522
+ name: title
523
+ key: p1titl
524
+ dataType:
525
+ scalarType: String
526
+ nonNullable: true
527
+ p2subt:
528
+ identifier:
529
+ name: subtitle
530
+ key: p2subt
531
+ dataType:
532
+ scalarType: String
533
+ nonNullable: false
534
+ p3tap:
535
+ identifier:
536
+ name: onTap
537
+ key: p3tap
538
+ dataType:
539
+ scalarType: Action
540
+ nonNullable: false
541
+ node:
542
+ key: Container_dc01root
543
+ ```
544
+
545
+ **File 2 — Widget tree outline:**
546
+ ```yaml
547
+ # File key: component/id-Container_dc01root/component-widget-tree-outline
548
+ node:
549
+ key: Container_dc01root
550
+ children:
551
+ - key: Container_dc01card
552
+ children:
553
+ - key: Column_dc01col
554
+ children:
555
+ - key: Text_dc01titl
556
+ - key: Text_dc01sub
557
+ ```
558
+
559
+ **File 3 — Root Container node:**
560
+ ```yaml
561
+ # File key: component/id-Container_dc01root/component-widget-tree-outline/node/id-Container_dc01root
562
+ key: Container_dc01root
563
+ type: Container
564
+ props:
565
+ container:
566
+ boxDecoration:
567
+ colorValue:
568
+ inputValue:
569
+ value: "0"
570
+ mostRecentInputValue:
571
+ value: "0"
572
+ name: DealCard
573
+ isDummyRoot: true
574
+ ```
575
+
576
+ > The root Container must have `isDummyRoot: true` and a transparent background (`value: "0"`). This marks it as the component boundary — the actual visual content starts with the first child.
577
+
578
+ **File 4 — Card container node:**
579
+ ```yaml
580
+ # File key: component/id-Container_dc01root/component-widget-tree-outline/node/id-Container_dc01card
581
+ key: Container_dc01card
582
+ type: Container
583
+ props:
584
+ container:
585
+ boxDecoration:
586
+ colorValue:
587
+ inputValue:
588
+ themeColor: SECONDARY_BACKGROUND
589
+ mostRecentInputValue:
590
+ themeColor: SECONDARY_BACKGROUND
591
+ borderRadiusValue:
592
+ inputValue: 12
593
+ mostRecentInputValue: 12
594
+ paddingValue:
595
+ inputValue: "16,16,16,16"
596
+ mostRecentInputValue: "16,16,16,16"
597
+ widthValue:
598
+ inputValue: "double.infinity"
599
+ mostRecentInputValue: "double.infinity"
600
+ responsiveVisibility: {}
601
+ parameterValues: {}
602
+ valueKey: {}
603
+ ```
604
+
605
+ **File 5 — Column node:**
606
+ ```yaml
607
+ # File key: component/id-Container_dc01root/component-widget-tree-outline/node/id-Column_dc01col
608
+ key: Column_dc01col
609
+ type: Column
610
+ props:
611
+ column:
612
+ crossAxisAlignmentValue:
613
+ inputValue: START
614
+ mostRecentInputValue: START
615
+ responsiveVisibility: {}
616
+ parameterValues: {}
617
+ valueKey: {}
618
+ ```
619
+
620
+ **File 6 — Title text (references component parameter):**
621
+ ```yaml
622
+ # File key: component/id-Container_dc01root/component-widget-tree-outline/node/id-Text_dc01titl
623
+ key: Text_dc01titl
624
+ type: Text
625
+ props:
626
+ text:
627
+ themeStyle: TITLE_MEDIUM
628
+ selectable: false
629
+ textValue:
630
+ variable:
631
+ source: WIDGET_CLASS_PARAMETER
632
+ baseVariable:
633
+ widgetClass:
634
+ paramIdentifier:
635
+ name: title
636
+ key: p1titl
637
+ nodeKeyRef:
638
+ key: Container_dc01root
639
+ colorValue:
640
+ inputValue:
641
+ themeColor: PRIMARY_TEXT
642
+ fontWeightValue:
643
+ inputValue: w600
644
+ responsiveVisibility: {}
645
+ parameterValues: {}
646
+ valueKey: {}
647
+ ```
648
+
649
+ > To display a component parameter, use `source: WIDGET_CLASS_PARAMETER` with `baseVariable.widgetClass.paramIdentifier` pointing to the param key. `nodeKeyRef.key` must point to the component's root Container ID.
650
+
651
+ **File 7 — Subtitle text:**
652
+ ```yaml
653
+ # File key: component/id-Container_dc01root/component-widget-tree-outline/node/id-Text_dc01sub
654
+ key: Text_dc01sub
655
+ type: Text
656
+ props:
657
+ text:
658
+ themeStyle: BODY_MEDIUM
659
+ selectable: false
660
+ textValue:
661
+ variable:
662
+ source: WIDGET_CLASS_PARAMETER
663
+ baseVariable:
664
+ widgetClass:
665
+ paramIdentifier:
666
+ name: subtitle
667
+ key: p2subt
668
+ nodeKeyRef:
669
+ key: Container_dc01root
670
+ colorValue:
671
+ inputValue:
672
+ themeColor: SECONDARY_TEXT
673
+ responsiveVisibility: {}
674
+ parameterValues: {}
675
+ valueKey: {}
676
+ ```
677
+
678
+ **Push all files in one call:**
679
+ ```
680
+ update_project_yaml(projectId, {
681
+ "component/id-Container_dc01root": metadataYaml,
682
+ "component/id-Container_dc01root/component-widget-tree-outline": treeOutlineYaml,
683
+ "component/id-Container_dc01root/component-widget-tree-outline/node/id-Container_dc01root": rootNodeYaml,
684
+ "component/id-Container_dc01root/component-widget-tree-outline/node/id-Container_dc01card": cardNodeYaml,
685
+ "component/id-Container_dc01root/component-widget-tree-outline/node/id-Column_dc01col": columnNodeYaml,
686
+ "component/id-Container_dc01root/component-widget-tree-outline/node/id-Text_dc01titl": titleNodeYaml,
687
+ "component/id-Container_dc01root/component-widget-tree-outline/node/id-Text_dc01sub": subtitleNodeYaml
688
+ })
689
+ ```
690
+
691
+ ### Using the new component in a page
692
+
693
+ After creating the component, embed it in a page by adding a Container node with `componentClassKeyRef`:
694
+
695
+ ```yaml
696
+ # Node in the host page
697
+ key: Container_hostref1
698
+ type: Container
699
+ props:
700
+ expanded:
701
+ expandedType: UNEXPANDED
702
+ responsiveVisibility: {}
703
+ parameterValues:
704
+ parameterPasses:
705
+ p1titl:
706
+ paramIdentifier:
707
+ name: title
708
+ key: p1titl
709
+ inputValue:
710
+ serializedValue: "50% Off Coffee"
711
+ p2subt:
712
+ paramIdentifier:
713
+ name: subtitle
714
+ key: p2subt
715
+ inputValue:
716
+ serializedValue: "Valid until end of month"
717
+ widgetClassNodeKeyRef:
718
+ key: Container_dc01root
719
+ componentClassKeyRef:
720
+ key: Container_dc01root
721
+ ```
722
+
723
+ For Action-type parameters, use `executeCallbackAction` inside the component:
724
+ ```yaml
725
+ # Action inside the component that invokes the onTap callback
726
+ key: act01tap
727
+ executeCallbackAction:
728
+ parameterIdentifier:
729
+ name: onTap
730
+ key: p3tap
731
+ ```
732
+
733
+ ---
734
+
735
+ ## 12. Refactoring Page Widgets into a Component
736
+
737
+ To extract existing page widgets into a reusable component:
738
+
739
+ ### Step 1: Identify the widget subtree to extract
740
+
741
+ Use `get_page_summary` or `get_page_by_name` to find the subtree you want to extract. Note the root widget key and all descendant keys.
742
+
743
+ ### Step 2: Decide on component parameters
744
+
745
+ Any data that was previously hardcoded or came from page state/params needs to become a component parameter. Common patterns:
746
+
747
+ | Was | Becomes |
748
+ |-----|---------|
749
+ | Hardcoded text | String parameter |
750
+ | Page state variable | Parameter passed from page |
751
+ | Page parameter | Parameter passed from page |
752
+ | Navigation action | Action (callback) parameter |
753
+ | API call result | Parameter or kept internal |
754
+
755
+ ### Step 3: Create component files
756
+
757
+ 1. **Metadata file** — Define `name`, `params`, and optionally `classModel.stateFields`
758
+ 2. **Tree outline** — Copy the subtree from the page's `page-widget-tree-outline`, wrapping it under a new root Container with `isDummyRoot: true`
759
+ 3. **Root node** — Create the `isDummyRoot: true` Container
760
+ 4. **Child nodes** — Copy existing node files from the page, changing file key prefix from `page/id-Scaffold_XXX/page-widget-tree-outline/node/` to `component/id-Container_XXX/component-widget-tree-outline/node/`
761
+ 5. **Update data references** — Replace hardcoded values with `WIDGET_CLASS_PARAMETER` variable references
762
+
763
+ ### Step 4: Update the page
764
+
765
+ 1. **Remove extracted nodes** from the page's tree outline
766
+ 2. **Replace with a component reference** — Add a single Container node with `componentClassKeyRef` pointing to the new component
767
+ 3. **Pass parameters** via `parameterValues.parameterPasses`
768
+
769
+ ### Step 5: Push everything in one call
770
+
771
+ Push all component files AND the updated page files in a single `update_project_yaml` call to avoid an inconsistent state.
772
+
773
+ ### Checklist
774
+
775
+ - [ ] Component metadata has correct `name` and `node.key`
776
+ - [ ] All params have unique keys (5-6 char alphanumeric)
777
+ - [ ] Root Container has `isDummyRoot: true` and transparent background
778
+ - [ ] Tree outline uses `children` (not `body`) under the root node
779
+ - [ ] All file keys use `component/` prefix (not `page/`)
780
+ - [ ] Tree outline key is `component-widget-tree-outline` (not `page-widget-tree-outline`)
781
+ - [ ] Parameter references use `source: WIDGET_CLASS_PARAMETER` with correct `nodeKeyRef`
782
+ - [ ] Host Container has both `componentClassKeyRef.key` and `parameterValues.widgetClassNodeKeyRef.key` pointing to the same component root ID
783
+ - [ ] Action callbacks use `executeCallbackAction` with correct `parameterIdentifier`
784
+ - [ ] Validated all files before pushing