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.
- package/README.md +300 -0
- package/build/api/flutterflow.d.ts +11 -0
- package/build/api/flutterflow.js +61 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +78 -0
- package/build/prompts/dev-workflow.d.ts +2 -0
- package/build/prompts/dev-workflow.js +68 -0
- package/build/prompts/generate-page.d.ts +2 -0
- package/build/prompts/generate-page.js +37 -0
- package/build/prompts/inspect-project.d.ts +2 -0
- package/build/prompts/inspect-project.js +30 -0
- package/build/prompts/modify-component.d.ts +2 -0
- package/build/prompts/modify-component.js +40 -0
- package/build/resources/docs.d.ts +2 -0
- package/build/resources/docs.js +76 -0
- package/build/resources/projects.d.ts +3 -0
- package/build/resources/projects.js +60 -0
- package/build/tools/find-component-usages.d.ts +21 -0
- package/build/tools/find-component-usages.js +216 -0
- package/build/tools/find-page-navigations.d.ts +26 -0
- package/build/tools/find-page-navigations.js +220 -0
- package/build/tools/get-api-endpoints.d.ts +2 -0
- package/build/tools/get-api-endpoints.js +126 -0
- package/build/tools/get-app-settings.d.ts +2 -0
- package/build/tools/get-app-settings.js +169 -0
- package/build/tools/get-app-state.d.ts +2 -0
- package/build/tools/get-app-state.js +96 -0
- package/build/tools/get-component-summary.d.ts +22 -0
- package/build/tools/get-component-summary.js +195 -0
- package/build/tools/get-custom-code.d.ts +2 -0
- package/build/tools/get-custom-code.js +380 -0
- package/build/tools/get-data-models.d.ts +2 -0
- package/build/tools/get-data-models.js +266 -0
- package/build/tools/get-editing-guide.d.ts +7 -0
- package/build/tools/get-editing-guide.js +185 -0
- package/build/tools/get-general-settings.d.ts +2 -0
- package/build/tools/get-general-settings.js +116 -0
- package/build/tools/get-in-app-purchases.d.ts +2 -0
- package/build/tools/get-in-app-purchases.js +51 -0
- package/build/tools/get-integrations.d.ts +2 -0
- package/build/tools/get-integrations.js +137 -0
- package/build/tools/get-page-by-name.d.ts +3 -0
- package/build/tools/get-page-by-name.js +56 -0
- package/build/tools/get-page-summary.d.ts +22 -0
- package/build/tools/get-page-summary.js +205 -0
- package/build/tools/get-project-config.d.ts +2 -0
- package/build/tools/get-project-config.js +216 -0
- package/build/tools/get-project-setup.d.ts +2 -0
- package/build/tools/get-project-setup.js +212 -0
- package/build/tools/get-theme.d.ts +2 -0
- package/build/tools/get-theme.js +199 -0
- package/build/tools/get-yaml-docs.d.ts +6 -0
- package/build/tools/get-yaml-docs.js +116 -0
- package/build/tools/get-yaml.d.ts +2 -0
- package/build/tools/get-yaml.js +53 -0
- package/build/tools/list-files.d.ts +3 -0
- package/build/tools/list-files.js +49 -0
- package/build/tools/list-pages.d.ts +25 -0
- package/build/tools/list-pages.js +101 -0
- package/build/tools/list-projects.d.ts +3 -0
- package/build/tools/list-projects.js +23 -0
- package/build/tools/search-project-files.d.ts +2 -0
- package/build/tools/search-project-files.js +69 -0
- package/build/tools/sync-project.d.ts +3 -0
- package/build/tools/sync-project.js +147 -0
- package/build/tools/update-yaml.d.ts +3 -0
- package/build/tools/update-yaml.js +24 -0
- package/build/tools/validate-yaml.d.ts +3 -0
- package/build/tools/validate-yaml.js +39 -0
- package/build/utils/batch-process.d.ts +2 -0
- package/build/utils/batch-process.js +10 -0
- package/build/utils/cache.d.ts +58 -0
- package/build/utils/cache.js +199 -0
- package/build/utils/decode-yaml.d.ts +7 -0
- package/build/utils/decode-yaml.js +31 -0
- package/build/utils/page-summary/action-summarizer.d.ts +24 -0
- package/build/utils/page-summary/action-summarizer.js +291 -0
- package/build/utils/page-summary/formatter.d.ts +13 -0
- package/build/utils/page-summary/formatter.js +129 -0
- package/build/utils/page-summary/node-extractor.d.ts +24 -0
- package/build/utils/page-summary/node-extractor.js +227 -0
- package/build/utils/page-summary/tree-walker.d.ts +6 -0
- package/build/utils/page-summary/tree-walker.js +55 -0
- package/build/utils/page-summary/types.d.ts +58 -0
- package/build/utils/page-summary/types.js +4 -0
- package/build/utils/parse-folders.d.ts +9 -0
- package/build/utils/parse-folders.js +29 -0
- package/build/utils/resolve-data-type.d.ts +2 -0
- package/build/utils/resolve-data-type.js +18 -0
- package/build/utils/topic-map.d.ts +7 -0
- package/build/utils/topic-map.js +122 -0
- package/docs/ff-yaml/00-overview.md +166 -0
- package/docs/ff-yaml/01-project-files.md +2309 -0
- package/docs/ff-yaml/02-pages.md +572 -0
- package/docs/ff-yaml/03-components.md +784 -0
- package/docs/ff-yaml/04-widgets/README.md +122 -0
- package/docs/ff-yaml/04-widgets/button.md +444 -0
- package/docs/ff-yaml/04-widgets/container.md +358 -0
- package/docs/ff-yaml/04-widgets/dropdown.md +579 -0
- package/docs/ff-yaml/04-widgets/form.md +256 -0
- package/docs/ff-yaml/04-widgets/image.md +276 -0
- package/docs/ff-yaml/04-widgets/layout.md +355 -0
- package/docs/ff-yaml/04-widgets/misc.md +553 -0
- package/docs/ff-yaml/04-widgets/text-field.md +326 -0
- package/docs/ff-yaml/04-widgets/text.md +302 -0
- package/docs/ff-yaml/05-actions.md +953 -0
- package/docs/ff-yaml/06-variables.md +849 -0
- package/docs/ff-yaml/07-data.md +591 -0
- package/docs/ff-yaml/08-custom-code.md +736 -0
- package/docs/ff-yaml/09-theming.md +638 -0
- package/docs/ff-yaml/10-editing-guide.md +497 -0
- package/docs/ff-yaml/README.md +105 -0
- package/package.json +59 -0
- package/skills/community-ff-mcp/SKILL.md +201 -0
- package/skills/ff-widget-patterns.md +141 -0
- 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
|