• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# BuilderNode
2
3The **BuilderNode** module provides APIs for a BuilderNode – a custom node that can be used to mount built-in components. A BuilderNode can be used only as a leaf node. For details, see [BuilderNode Development](../../ui/arkts-user-defined-arktsNode-builderNode.md). For best practices, see [Dynamic Component Creation: Dynamically Adding, Updating, and Deleting Components](https://developer.huawei.com/consumer/en/doc/best-practices/bpta-ui-dynamic-operations#section153921947151012).
4
5> **NOTE**
6>
7> The initial APIs of this module are supported since API version 11. Newly added APIs will be marked with a superscript to indicate their earliest API version.
8>
9> If you encounter display issues when reusing a BuilderNode across pages, see [Cross-Page Reuse Considerations](../../ui/arkts-user-defined-arktsNode-builderNode.md#cross-page-reuse-considerations) for guidance.
10>
11> **BuilderNode** is not available in DevEco Studio Previewer.
12>
13> Custom components under a BuilderNode can use the [@Prop](../../ui/state-management/arkts-prop.md) decorator. The [@Link](../../ui/state-management/arkts-link.md) decorator cannot be used to synchronize data and states across BuilderNode boundaries.
14>
15> If a BuilderNode contains custom components as child nodes, these custom components cannot use the [@Reusable](../../ui/state-management/arkts-reusable.md) decorator. For details, see [Using the @Reusable Decorator with BuilderNode Child Components](../../ui/arkts-user-defined-arktsNode-builderNode.md#using-the-reusable-decorator-with-buildernode-child-components).
16>
17> Since API version 12, custom components can receive [LocalStorage](../../ui/state-management/arkts-localstorage.md) instances. You can use LocalStorage related decorators such as [@LocalStorageProp](../../ui/state-management/arkts-localstorage.md#localstorageprop) and [@LocalStorageLink](../../ui/state-management/arkts-localstorage.md#localstoragelink) by [passing LocalStorage instances](../../ui/state-management/arkts-localstorage.md#example-of-providing-a-custom-component-with-access-to-a-localstorage-instance).
18>
19> Since API version 20, when configured with [BuildOptions](#buildoptions12), custom components within a BuilderNode can access the host page's [@Provide](../../ui/state-management/arkts-provide-and-consume.md) data through their [@Consume](../../ui/state-management/arkts-provide-and-consume.md) decorated attributes.
20>
21> The behavior of other decorators is undefined. Avoid using those decorators.
22
23
24## Modules to Import
25
26```ts
27import { BuilderNode, RenderOptions, NodeRenderType } from "@kit.ArkUI";
28```
29
30## NodeRenderType
31
32Enumerates the node rendering types.
33
34**Atomic service API**: This API can be used in atomic services since API version 12.
35
36**System capability**: SystemCapability.ArkUI.ArkUI.Full
37
38| Name               | Value | Description                        |
39| ------------------- | --- | ---------------------------- |
40| RENDER_TYPE_DISPLAY | 0   | The node is displayed on the screen.|
41| RENDER_TYPE_TEXTURE | 1   | The node is exported as a texture.  |
42
43> **NOTE**
44>
45> Currently, the **RENDER_TYPE_TEXTURE** type takes effect only for the [XComponentNode](./js-apis-arkui-xcomponentNode.md) and the [BuilderNode](#buildernode-1) holding a component tree whose root node is a custom component.
46>
47> In the case of [BuilderNode](#buildernode-1), the following custom components that function as the root node support texture export: Badge, Blank, Button, CanvasGradient, CanvasPattern, CanvasRenderingContext2D, Canvas, CheckboxGroup, Checkbox, Circle, ColumnSplit, Column, ContainerSpan, Counter, DataPanel, Divider, Ellipse, Flex, Gauge, Hyperlink, ImageBitmap, ImageData, Image, Line, LoadingProgress, Marquee, Matrix2D, OffscreenCanvasRenderingContext2D, OffscreenCanvas, Path2D, Path, PatternLock, Polygon, Polyline, Progress, QRCode, Radio, Rating, Rect, RelativeContainer, RowSplit, Row, Shape, Slider, Span, Stack, TextArea, TextClock, TextInput, TextTimer, Text, Toggle, Video (without support for full-screen playback), Web, XComponent
48>
49> The following components support texture export since API version 12: DatePicker, ForEach, Grid, IfElse, LazyForEach, List, Scroll, Swiper, TimePicker, @Component decorated custom components, NodeContainer, and FrameNode and RenderNode mounted to a NodeContainer.
50>
51> For details, see [Rendering and Drawing Video and Button Components at the Same Layer](../../web/web-same-layer.md).
52
53## RenderOptions
54
55Provides optional parameters for creating a BuilderNode.
56
57**Atomic service API**: This API can be used in atomic services since API version 12.
58
59**System capability**: SystemCapability.ArkUI.ArkUI.Full
60
61| Name         | Type                                  | Mandatory| Description                                                        |
62| ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ |
63| selfIdealSize | [Size](js-apis-arkui-graphics.md#size) | No  | Ideal size of the node.<br>Default value: **{ width: 0, height: 0 }**.|
64| type          | [NodeRenderType](#noderendertype)      | No  | Rendering type of the node.<br>Default value: **NodeRenderType.RENDER_TYPE_DISPLAY**.|
65| surfaceId     | string                                 | No  | Surface ID of the texture receiver. Typically, the texture receiver is [OH_NativeImage](../apis-arkgraphics2d/capi-oh-nativeimage-oh-nativeimage.md).<br>This parameter is effective only when **type** is set to **NodeRenderType.RENDER_TYPE_TEXTURE**.<br>Default value: **""**.|
66
67## BuilderNode
68
69class BuilderNode\<Args extends Object[]>
70
71Implements a BuilderNode, which can create a component tree through the stateless UI method [@Builder](../../ui/state-management/arkts-builder.md) and hold the root node of the component tree. A BuilderNode cannot be defined as a state variable. The FrameNode held in the BuilderNode is only used to mount the BuilderNode to other FrameNodes as a child node. Undefined behavior may occur if you set attributes or perform operations on subnodes of the FrameNode held by the BuilderNode. Therefore, after you have obtained a [RenderNode](js-apis-arkui-renderNode.md) through the [getFrameNode](#getframenode) method of the BuilderNode and the [getRenderNode](js-apis-arkui-frameNode.md#getrendernode) method of the [FrameNode](js-apis-arkui-frameNode.md), avoid setting the attributes or operating the subnodes through APIs of [RenderNode](js-apis-arkui-renderNode.md).
72
73**Atomic service API**: This API can be used in atomic services since API version 12.
74
75**System capability**: SystemCapability.ArkUI.ArkUI.Full
76
77### constructor
78
79constructor(uiContext: UIContext, options?: RenderOptions)
80
81Constructor for creating a BuilderNode. When the content generated by the BuilderNode is embedded in another RenderNode for display, that is, the RenderNode corresponding to the BuilderNode is mounted to another RenderNode for display, **selfIdealSize** in **RenderOptions** must be explicitly specified. If **selfIdealSize** is not set, the node in the builder follows the default parent component layout constraint [0, 0], which means that the size of the root node of the subtree in BuilderNode is [0, 0].
82
83**Atomic service API**: This API can be used in atomic services since API version 12.
84
85**System capability**: SystemCapability.ArkUI.ArkUI.Full
86
87| Name   | Type                                   | Mandatory| Description                                                             |
88| --------- | --------------------------------------- | ---- | ----------------------------------------------------------------- |
89| uiContext | [UIContext](arkts-apis-uicontext-uicontext.md) | Yes  | UI context. For details about how to obtain it, see [Obtaining UI Context](./js-apis-arkui-node.md#obtaining-ui-context).|
90| options   | [RenderOptions](#renderoptions)         | No  | Parameters for creating a BuilderNode. Default value: **undefined**  |
91
92> **NOTE**
93> The input parameter for **uiContext** must be a valid value, that is, the UI context must be correct. If an invalid value is passed in or if no value is specified, creation will fail.
94
95### build
96
97build(builder: WrappedBuilder\<Args>, arg?: Object): void
98
99Creates a component tree based on the passed object and holds the root node of the component tree. The stateless UI method [@Builder](../../ui/state-management/arkts-builder.md) has at most one root node.
100Custom components are allowed.
101
102> **NOTE**
103>
104> When nesting @Builder, ensure that the input objects for the inner and outer @Builder methods are consistent.
105>
106> The outermost @Builder supports only one input parameter.
107>
108> The build parameter uses the pass-by-value semantics. To implement state updates, you must explicitly use the [update](#update) API.
109>
110> To operate objects in a BuilderNode, ensure that the reference to the BuilderNode is not garbage collected. Once a BuilderNode object is collected by the virtual machine, its FrameNode and RenderNode objects will also be dereferenced from the backend nodes. This means that any FrameNode objects obtained from a BuilderNode will no longer correspond to any actual node if the BuilderNode is garbage collected.
111>
112> The BuilderNode object maintains references to its underlying entity nodes. When the BuilderNode frontend object is no longer required for managing backend nodes, call the [dispose](#dispose12) API to release node references and unbind frontend and backend nodes.
113
114**Atomic service API**: This API can be used in atomic services since API version 12.
115
116**System capability**: SystemCapability.ArkUI.ArkUI.Full
117
118**Parameters**
119
120| Name | Type                                                           | Mandatory| Description                                                                                  |
121| ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- |
122| builder | [WrappedBuilder\<Args>](../../ui/state-management/arkts-wrapBuilder.md) | Yes  | Stateless UI method [@Builder](../../ui/state-management/arkts-builder.md) required for creating a component tree.|
123| arg     | Object                                                          | No  | Argument of the builder. Only one input parameter is supported, and the type of the input parameter must be consistent with the type defined by @Builder.<br>Default value: **undefined**.|
124
125### build<sup>12+</sup>
126
127build(builder: WrappedBuilder\<Args>, arg: Object, options: [BuildOptions](#buildoptions12)): void
128
129Creates a component tree based on the passed object and holds the root node of the component tree. The stateless UI method [@Builder](../../ui/state-management/arkts-builder.md) has at most one root node.
130Custom components are allowed.
131
132> **NOTE**
133>
134> For details about the creation and update using @Builder, see [@Builder](../../ui/state-management/arkts-builder.md).
135>
136> The outermost @Builder supports only one input parameter.
137
138**Atomic service API**: This API can be used in atomic services since API version 12.
139
140**System capability**: SystemCapability.ArkUI.ArkUI.Full
141
142**Parameters**
143
144| Name | Type                                                           | Mandatory| Description                                                                                   |
145| ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- |
146| builder | [WrappedBuilder\<Args>](../../ui/state-management/arkts-wrapBuilder.md) | Yes  | Stateless UI method [@Builder](../../ui/state-management/arkts-builder.md) required for creating a component tree.  |
147| arg     | Object                                                          | Yes  | Argument of the builder. Only one input parameter is supported, and the type of the input parameter must be consistent with the type defined by @Builder.                                                           |
148| options | [BuildOptions](#buildoptions12)                                           | Yes  | Build options, which determine whether to support nesting **@Builder** within **@Builder**.                                        |
149
150**Example**
151```ts
152import { BuilderNode, NodeContent } from "@kit.ArkUI";
153
154interface ParamsInterface {
155  text: string;
156  func: Function;
157}
158
159@Builder
160function buildTextWithFunc(fun: Function) {
161  Text(fun())
162    .fontSize(50)
163    .fontWeight(FontWeight.Bold)
164    .margin({ bottom: 36 })
165}
166
167@Builder
168function buildText(params: ParamsInterface) {
169  Column() {
170    Text(params.text)
171      .fontSize(50)
172      .fontWeight(FontWeight.Bold)
173      .margin({ bottom: 36 })
174    buildTextWithFunc(params.func)
175  }
176}
177
178
179@Entry
180@Component
181struct Index {
182  @State message: string = "HELLO";
183  private content: NodeContent = new NodeContent();
184
185  build() {
186    Row() {
187      Column() {
188        Button('addBuilderNode')
189          .onClick(() => {
190            let buildNode = new BuilderNode<[ParamsInterface]>(this.getUIContext());
191            buildNode.build(wrapBuilder<[ParamsInterface]>(buildText), {
192              text: this.message, func: () => {
193                return "FUNCTION";
194              }
195            }, { nestingBuilderSupported: true });
196            this.content.addFrameNode(buildNode.getFrameNode());
197            buildNode.dispose();
198          })
199        ContentSlot(this.content)
200      }
201      .id("column")
202      .width('100%')
203      .height('100%')
204    }
205    .height('100%')
206  }
207}
208```
209
210### BuildOptions<sup>12+</sup>
211
212Defines the optional build options.
213
214**System capability**: SystemCapability.ArkUI.ArkUI.Full
215
216| Name         | Type                                  | Mandatory| Description                                                        |
217| ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ |
218| nestingBuilderSupported |boolean | No  | Whether to support nesting **@Builder** within **@Builder**. The value **false** means that the input arguments for **@Builder** are consistent, and **true** means the opposite.<br> Default value: **false**.<br>**Atomic service API**: This API can be used in atomic services since API version 12.|
219| localStorage<sup>20+</sup> |[LocalStorage](../../ui/state-management/arkts-localstorage.md) | No  | LocalStorage for the current BuilderNode. Custom components mounted under this BuilderNode will share the specified LocalStorage. **NOTE**<br>If LocalStorage is also passed through a custom component's constructor, the constructor parameter takes precedence.<br> Default value: **null**.<br>**Atomic service API**: This API can be used in atomic services since API version 20.|
220| enableProvideConsumeCrossing<sup>20+</sup> | boolean | No| Whether @Consume in the custom components under the current BuilderNode can communicate with the host page's @Provide. The value **true** means that this feature is supported, and **false** means the opposite. Default value: **false**.<br>**Atomic service API**: This API can be used in atomic services since API version 20.|
221
222### InputEventType<sup>20+</sup>
223
224type InputEventType = TouchEvent | MouseEvent | AxisEvent
225
226Defines the parameter type for **postInputEvent**, which specifies the input event types that can be transmitted.
227
228**Atomic service API**: This API can be used in atomic services since API version 20.
229
230**System capability**: SystemCapability.ArkUI.ArkUI.Full
231
232| Type| Description                                  |
233| ------------- | -------------------------------------- |
234| [TouchEvent](arkui-ts/ts-universal-events-touch.md#touchevent)  | Touch event.|
235| [MouseEvent](arkui-ts/ts-universal-mouse-key.md#mouseevent)  | Mouse event.|
236| [AxisEvent](arkui-ts/ts-universal-events-axis.md#axisevent)  | Axis event.|
237
238### getFrameNode
239
240getFrameNode(): FrameNode | null
241
242Obtains the FrameNode in the BuilderNode. The FrameNode is generated only after the BuilderNode executes the build operation.
243
244**Atomic service API**: This API can be used in atomic services since API version 12.
245
246**System capability**: SystemCapability.ArkUI.ArkUI.Full
247
248**Return value**
249
250| Type                                                     | Description                                                                 |
251| --------------------------------------------------------- | --------------------------------------------------------------------- |
252| [FrameNode](js-apis-arkui-frameNode.md) \| null | **FrameNode** object. If no such object is held by the **BuilderNode** instance, null is returned.|
253
254**Example 1**
255
256In this example, the BuilderNode is returned as the root node of the **NodeContainer**.
257
258```ts
259import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI";
260
261class Params {
262  text: string = "";
263  constructor(text: string) {
264    this.text = text;
265  }
266}
267
268@Builder
269function buildText(params: Params) {
270  Column() {
271    Text(params.text)
272      .fontSize(50)
273      .fontWeight(FontWeight.Bold)
274      .margin({bottom: 36})
275  }
276}
277
278class TextNodeController extends NodeController {
279  private textNode: BuilderNode<[Params]> | null = null;
280  private message: string = "DEFAULT";
281
282  constructor(message: string) {
283    super();
284    this.message = message;
285  }
286
287  makeNode(context: UIContext): FrameNode | null {
288    this.textNode = new BuilderNode(context);
289    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message));
290
291    return this.textNode.getFrameNode();
292  }
293}
294
295@Entry
296@Component
297struct Index {
298  @State message: string = "hello";
299
300  build() {
301    Row() {
302      Column() {
303        NodeContainer(new TextNodeController(this.message))
304          .width('100%')
305          .height(100)
306          .backgroundColor('#FFF0F0F0')
307      }
308      .width('100%')
309      .height('100%')
310    }
311    .height('100%')
312  }
313}
314```
315
316**Example 2**
317
318This example shows how to mount a FrameNode within a BuilderNode to another FrameNode.
319
320```ts
321import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI";
322
323class Params {
324  text: string = "";
325
326  constructor(text: string) {
327    this.text = text;
328  }
329}
330
331@Builder
332function buildText(params: Params) {
333  Column() {
334    Text(params.text)
335      .fontSize(50)
336      .fontWeight(FontWeight.Bold)
337      .margin({ bottom: 36 })
338  }
339}
340
341class TextNodeController extends NodeController {
342  private rootNode: FrameNode | null = null;
343  private textNode: BuilderNode<[Params]> | null = null;
344  private message: string = "DEFAULT";
345
346  constructor(message: string) {
347    super();
348    this.message = message;
349  }
350
351  makeNode(context: UIContext): FrameNode | null {
352    this.rootNode = new FrameNode(context);
353    this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } });
354    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message));
355    if (this.rootNode !== null) {
356      this.rootNode.appendChild(this.textNode?.getFrameNode());
357    }
358
359    return this.rootNode;
360  }
361}
362
363@Entry
364@Component
365struct Index {
366  @State message: string = "hello";
367
368  build() {
369    Row() {
370      Column() {
371        NodeContainer(new TextNodeController(this.message))
372          .width('100%')
373          .height(100)
374          .backgroundColor('#FFF0F0F0')
375      }
376      .width('100%')
377      .height('100%')
378    }
379    .height('100%')
380  }
381}
382```
383
384**Example 3**
385
386This example shows how to mount a BuilderNode's RenderNode under another RenderNode. Since the RenderNode does not pass layout constraints, this mode of mounting nodes is not recommended.
387
388```ts
389import { NodeController, BuilderNode, FrameNode, UIContext, RenderNode } from "@kit.ArkUI";
390
391class Params {
392  text: string = "";
393
394  constructor(text: string) {
395    this.text = text;
396  }
397}
398
399@Builder
400function buildText(params: Params) {
401  Column() {
402    Text(params.text)
403      .fontSize(50)
404      .fontWeight(FontWeight.Bold)
405      .margin({ bottom: 36 })
406  }
407}
408
409class TextNodeController extends NodeController {
410  private rootNode: FrameNode | null = null;
411  private textNode: BuilderNode<[Params]> | null = null;
412  private message: string = "DEFAULT";
413
414  constructor(message: string) {
415    super();
416    this.message = message;
417  }
418
419  makeNode(context: UIContext): FrameNode | null {
420    this.rootNode = new FrameNode(context);
421    let renderNode = new RenderNode();
422    renderNode.clipToFrame = false;
423    this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } });
424    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message));
425    const textRenderNode = this.textNode?.getFrameNode()?.getRenderNode();
426
427    const rootRenderNode = this.rootNode.getRenderNode();
428    if (rootRenderNode !== null) {
429      rootRenderNode.appendChild(renderNode);
430      renderNode.appendChild(textRenderNode);
431    }
432
433    return this.rootNode;
434  }
435}
436
437@Entry
438@Component
439struct Index {
440  @State message: string = "hello";
441
442  build() {
443    Row() {
444      Column() {
445        NodeContainer(new TextNodeController(this.message))
446          .width('100%')
447          .height(100)
448          .backgroundColor('#FFF0F0F0')
449      }
450      .width('100%')
451      .height('100%')
452    }
453    .height('100%')
454  }
455}
456```
457
458### update
459
460update(arg: Object): void
461
462Updates this BuilderNode based on the provided parameter, which is of the same type as the input parameter passed to the [build](#build) API. To call this API on a custom component, the variable used in the component must be defined as the @Prop type.
463
464**Atomic service API**: This API can be used in atomic services since API version 12.
465
466**System capability**: SystemCapability.ArkUI.ArkUI.Full
467
468**Parameters**
469
470| Name| Type  | Mandatory| Description                                                                    |
471| ------ | ------ | ---- | ------------------------------------------------------------------------ |
472| arg    | Object | Yes  | Parameter used to update the BuilderNode. It is of the same type as the parameter passed to the [build](#build) API.|
473
474**Example**
475```ts
476import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI";
477
478class Params {
479  text: string = "";
480  constructor(text: string) {
481    this.text = text;
482  }
483}
484
485// Custom component
486@Component
487struct TextBuilder {
488  @Prop message: string = "TextBuilder";
489
490  build() {
491    Row() {
492      Column() {
493        Text(this.message)
494          .fontSize(50)
495          .fontWeight(FontWeight.Bold)
496          .margin({bottom: 36})
497          .backgroundColor(Color.Gray)
498      }
499    }
500  }
501}
502
503@Builder
504function buildText(params: Params) {
505  Column() {
506    Text(params.text)
507      .fontSize(50)
508      .fontWeight(FontWeight.Bold)
509      .margin({ bottom: 36 })
510    TextBuilder({message: params.text}) // Custom component
511  }
512}
513
514class TextNodeController extends NodeController {
515  private rootNode: FrameNode | null = null;
516  private textNode: BuilderNode<[Params]> | null = null;
517  private message: string = "";
518
519  constructor(message: string) {
520    super();
521    this.message = message;
522  }
523
524  makeNode(context: UIContext): FrameNode | null {
525    this.textNode = new BuilderNode(context);
526    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message));
527    return this.textNode.getFrameNode();
528  }
529
530  update(message: string) {
531    if (this.textNode !== null) {
532      this.textNode.update(new Params(message));
533    }
534  }
535}
536
537@Entry
538@Component
539struct Index {
540  @State message: string = "hello";
541  private textNodeController: TextNodeController = new TextNodeController(this.message);
542  private count = 0;
543
544  build() {
545    Row() {
546      Column() {
547        NodeContainer(this.textNodeController)
548          .width('100%')
549          .height(200)
550          .backgroundColor('#FFF0F0F0')
551        Button('Update')
552          .onClick(() => {
553            this.count += 1;
554            const message = "Update " + this.count.toString();
555            this.textNodeController.update(message);
556          })
557      }
558      .width('100%')
559      .height('100%')
560    }
561    .height('100%')
562  }
563}
564```
565
566### postTouchEvent
567
568postTouchEvent(event: TouchEvent): boolean
569
570Posts a raw touch event to the FrameNode created by this BuilderNode.
571
572**postTouchEvent** dispatches the event from a middle node in the component tree downwards. To ensure the event is dispatched correctly, it needs to be transformed into the coordinate system of the parent component, as shown in the figure below.
573
574**OffsetA** indicates the offset of the BuildNode relative to the parent component. You can obtain this offset by calling [getPositionToParent](js-apis-arkui-frameNode.md#getpositiontoparent12) in the FrameNode. **OffsetB** indicates the offset of the touch point relative to the BuildNode. You can obtain this offset from the [TouchEvent](arkui-ts/ts-universal-events-touch.md#touchevent) object. **OffsetC** is the sum of **OffsetA** and **OffsetB**. It represents the final offset that you need to pass to **postTouchEvent**.
575
576![postTouchEvent](figures/postTouchEvent.PNG)
577
578> **NOTE**
579>
580> The coordinates you pass in need to be converted to pixel values (px). If the BuilderNode has any affine transformations applied to it, they must be taken into account and combined with the touch event coordinates.
581>
582> In [Webview](../apis-arkweb/arkts-apis-webview.md), coordinate system transformations are already handled internally, so you can directly dispatch the touch event without additional adjustments.
583>
584> The **postTouchEvent** API can be called only once for the same timestamp.<!--Del-->
585>
586> [UIExtensionComponent](arkui-ts/ts-container-ui-extension-component-sys.md) is not supported.
587<!--DelEnd-->
588
589**Atomic service API**: This API can be used in atomic services since API version 12.
590
591**System capability**: SystemCapability.ArkUI.ArkUI.Full
592
593**Parameters**
594
595| Name| Type                                                                     | Mandatory| Description      |
596| ------ | ------------------------------------------------------------------------- | ---- | ---------- |
597| event  | [TouchEvent](arkui-ts/ts-universal-events-touch.md#touchevent) | Yes  | Touch event.|
598
599**Return value**
600
601| Type   | Description              |
602| ------- | ------------------ |
603| boolean | Whether the event is successfully dispatched. The value **true** means the event is consumed by a component that responds to the event, and **false** means that no component responds to the event.<br>**NOTE**<br>If the event does not hit the expected component, ensure the following:<br>1. The coordinate system has been correctly transformed<br>2. The component is in an interactive state.<br>3. The event has been bound to the component.|
604
605**Example**
606
607```ts
608import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI';
609
610class Params {
611  text: string = "this is a text";
612}
613
614@Builder
615function ButtonBuilder(params: Params) {
616  Column() {
617    Button(`button ` + params.text)
618      .borderWidth(2)
619      .backgroundColor(Color.Orange)
620      .width("100%")
621      .height("100%")
622      .gesture(
623        TapGesture()
624          .onAction((event: GestureEvent) => {
625            console.info("TapGesture");
626          })
627      )
628  }
629  .width(500)
630  .height(300)
631  .backgroundColor(Color.Gray)
632}
633
634class MyNodeController extends NodeController {
635  private rootNode: BuilderNode<[Params]> | null = null;
636  private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder);
637
638  makeNode(uiContext: UIContext): FrameNode | null {
639    this.rootNode = new BuilderNode(uiContext);
640    this.rootNode.build(this.wrapBuilder, { text: "this is a string" });
641    return this.rootNode.getFrameNode();
642  }
643
644  // Coordinate system transformation
645  postTouchEvent(event: TouchEvent, uiContext: UIContext): boolean {
646    if (this.rootNode == null) {
647      return false;
648    }
649    let node: FrameNode | null = this.rootNode.getFrameNode();
650    let offsetX: number | null | undefined = node?.getPositionToParent().x;
651    let offsetY: number | null | undefined = node?.getPositionToParent().y;
652
653    let changedTouchLen = event.changedTouches.length;
654    for (let i = 0; i < changedTouchLen; i++) {
655      if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
656        event.changedTouches[i].x = uiContext.vp2px(offsetX + event.changedTouches[i].x);
657        event.changedTouches[i].y = uiContext.vp2px(offsetY + event.changedTouches[i].y);
658      }
659    }
660    let result = this.rootNode.postTouchEvent(event);
661    console.info("result " + result);
662    return result;
663  }
664}
665
666@Entry
667@Component
668struct MyComponent {
669  private nodeController: MyNodeController = new MyNodeController();
670
671  build() {
672    Column() {
673      NodeContainer(this.nodeController)
674        .height(300)
675        .width(500)
676
677      Column()
678        .width(500)
679        .height(300)
680        .backgroundColor(Color.Pink)
681        .onTouch((event) => {
682          if (event != undefined) {
683            this.nodeController.postTouchEvent(event, this.getUIContext());
684          }
685        })
686    }
687  }
688}
689```
690
691### dispose<sup>12+</sup>
692
693dispose(): void
694
695Immediately releases the reference relationship between this BuilderNode object and its [entity node](../../ui/arkts-user-defined-node.md#basic-concepts). For details about the scenarios involving BuilderNode unbinding, see [Canceling the Reference to the Entity Node](../../ui/arkts-user-defined-arktsNode-builderNode.md#canceling-the-reference-to-the-entity-node).
696
697> **NOTE**
698>
699> Failure to properly release BuilderNode objects may result in memory leaks. To avoid this, be sure to call **dispose()** on the BuilderNode when you no longer need it. This reduces the complexity of reference relationships and lowers the risk of memory leaks.
700
701**Atomic service API**: This API can be used in atomic services since API version 12.
702
703**System capability**: SystemCapability.ArkUI.ArkUI.Full
704
705```ts
706import { RenderNode, FrameNode, NodeController, BuilderNode } from "@kit.ArkUI";
707
708@Component
709struct TestComponent {
710  build() {
711    Column() {
712      Text('This is a BuilderNode.')
713        .fontSize(16)
714        .fontWeight(FontWeight.Bold)
715    }
716    .width('100%')
717    .backgroundColor(Color.Gray)
718  }
719
720  aboutToAppear() {
721    console.info('aboutToAppear');
722  }
723
724  aboutToDisappear() {
725    console.info('aboutToDisappear');
726  }
727}
728
729@Builder
730function buildComponent() {
731  TestComponent()
732}
733
734class MyNodeController extends NodeController {
735  private rootNode: FrameNode | null = null;
736  private builderNode: BuilderNode<[]> | null = null;
737
738  makeNode(uiContext: UIContext): FrameNode | null {
739    this.rootNode = new FrameNode(uiContext);
740    this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 200, height: 100 } });
741    this.builderNode.build(new WrappedBuilder(buildComponent));
742
743    const rootRenderNode = this.rootNode!.getRenderNode();
744    if (rootRenderNode !== null) {
745      rootRenderNode.size = { width: 200, height: 200 };
746      rootRenderNode.backgroundColor = 0xff00ff00;
747      rootRenderNode.appendChild(this.builderNode!.getFrameNode()!.getRenderNode());
748    }
749
750    return this.rootNode;
751  }
752
753  dispose() {
754    if (this.builderNode !== null) {
755      this.builderNode.dispose();
756    }
757  }
758
759  removeBuilderNode() {
760    const rootRenderNode = this.rootNode!.getRenderNode();
761    if (rootRenderNode !== null && this.builderNode !== null && this.builderNode.getFrameNode() !== null) {
762      rootRenderNode.removeChild(this.builderNode!.getFrameNode()!.getRenderNode());
763    }
764  }
765}
766
767@Entry
768@Component
769struct Index {
770  private myNodeController: MyNodeController = new MyNodeController();
771
772  build() {
773    Column({ space: 4 }) {
774      NodeContainer(this.myNodeController)
775      Button('BuilderNode dispose')
776        .onClick(() => {
777          this.myNodeController.removeBuilderNode();
778          this.myNodeController.dispose();
779        })
780        .width('100%')
781    }
782  }
783}
784```
785
786### reuse<sup>12+</sup>
787
788reuse(param?: Object): void
789
790Triggers component reuse for custom components under this BuilderNode. For details about component reuse, see [\@Reusable Decorator: Reusing Components](../../ui/state-management/arkts-reusable.md). For details about the scenarios involving BuilderNode unbinding, see [Canceling the Reference to the Entity Node](../../ui/arkts-user-defined-arktsNode-builderNode.md#canceling-the-reference-to-the-entity-node).
791
792**Atomic service API**: This API can be used in atomic services since API version 12.
793
794**System capability**: SystemCapability.ArkUI.ArkUI.Full
795
796**Parameters**
797
798| Name| Type  | Mandatory| Description                                                                    |
799| ------ | ------ | ---- | ------------------------------------------------------------------------ |
800| param  | Object | No  | Parameter used to reuse the BuilderNode. It is of the same type as the parameter passed to the [build](#build) API.|
801
802### recycle<sup>12+</sup>
803
804recycle(): void
805
806- Triggers recycling of custom components under this BuilderNode. Component recycling is part of the component reuse mechanism. For details, see [\@Reusable Decorator: Reusing Components](../../ui/state-management/arkts-reusable.md).
807- The BuilderNode completes the reuse event transfer between internal and external custom components through **reuse** and **recycle**. For specific usage scenarios, see [Implementing Node Reuse with the BuilderNode reuse and recycle APIs](../../ui/arkts-user-defined-arktsNode-builderNode.md#implementing-node-reuse-with-the-buildernode-reuse-and-recycle-apis).
808
809**Atomic service API**: This API can be used in atomic services since API version 12.
810
811**System capability**: SystemCapability.ArkUI.ArkUI.Full
812
813```ts
814import { FrameNode, NodeController, BuilderNode, UIContext } from "@kit.ArkUI";
815
816const TEST_TAG: string = "Reuse+Recycle";
817
818class MyDataSource {
819  private dataArray: string[] = [];
820  private listener: DataChangeListener | null = null;
821
822  public totalCount(): number {
823    return this.dataArray.length;
824  }
825
826  public getData(index: number) {
827    return this.dataArray[index];
828  }
829
830  public pushData(data: string) {
831    this.dataArray.push(data);
832  }
833
834  public reloadListener(): void {
835    this.listener?.onDataReloaded();
836  }
837
838  public registerDataChangeListener(listener: DataChangeListener): void {
839    this.listener = listener;
840  }
841
842  public unregisterDataChangeListener(): void {
843    this.listener = null;
844  }
845}
846
847class Params {
848  item: string = '';
849
850  constructor(item: string) {
851    this.item = item;
852  }
853}
854
855@Builder
856function buildNode(param: Params = new Params("hello")) {
857  Row() {
858    Text(`C${param.item} -- `)
859    ReusableChildComponent2({ item: param.item }) // This custom component cannot be correctly reused in the BuilderNode.
860  }
861}
862
863class MyNodeController extends NodeController {
864  public builderNode: BuilderNode<[Params]> | null = null;
865  public item: string = "";
866
867  makeNode(uiContext: UIContext): FrameNode | null {
868    if (this.builderNode == null) {
869      this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 300, height: 200 } });
870      this.builderNode.build(wrapBuilder<[Params]>(buildNode), new Params(this.item));
871    }
872    return this.builderNode.getFrameNode();
873  }
874}
875
876// The custom component that is reused and recycled will have its state variables updated, and the state variables of the nested custom component ReusableChildComponent3 will also be updated. However, the BuilderNode will block this propagation process.
877@Reusable
878@Component
879struct ReusableChildComponent {
880  @Prop item: string = '';
881  @Prop switch: string = '';
882  private controller: MyNodeController = new MyNodeController();
883
884  aboutToAppear() {
885    this.controller.item = this.item;
886  }
887
888  aboutToRecycle(): void {
889    console.info(`${TEST_TAG} ReusableChildComponent aboutToRecycle ${this.item}`);
890
891    // When the switch is open, pass the recycle event to the nested custom component, such as ReusableChildComponent2, through the BuilderNode's recycle API to complete recycling.
892    if (this.switch === 'open') {
893      this.controller?.builderNode?.recycle();
894    }
895  }
896
897  aboutToReuse(params: object): void {
898    console.info(`${TEST_TAG} ReusableChildComponent aboutToReuse ${JSON.stringify(params)}`);
899
900    // When the switch is open, pass the reuse event to the nested custom component, such as ReusableChildComponent2, through the BuilderNode's reuse API to complete reuse.
901    if (this.switch === 'open') {
902      this.controller?.builderNode?.reuse(params);
903    }
904  }
905
906  build() {
907    Row() {
908      Text(`A${this.item}--`)
909      ReusableChildComponent3({ item: this.item })
910      NodeContainer(this.controller);
911    }
912  }
913}
914
915@Component
916struct ReusableChildComponent2 {
917  @Prop item: string = "false";
918
919  aboutToReuse(params: Record<string, object>) {
920    console.info(`${TEST_TAG} ReusableChildComponent2 aboutToReuse ${JSON.stringify(params)}`);
921  }
922
923  aboutToRecycle(): void {
924    console.info(`${TEST_TAG} ReusableChildComponent2 aboutToRecycle ${this.item}`);
925  }
926
927  build() {
928    Row() {
929      Text(`D${this.item}`)
930        .fontSize(20)
931        .backgroundColor(Color.Yellow)
932        .margin({ left: 10 })
933    }.margin({ left: 10, right: 10 })
934  }
935}
936
937@Component
938struct ReusableChildComponent3 {
939  @Prop item: string = "false";
940
941  aboutToReuse(params: Record<string, object>) {
942    console.info(`${TEST_TAG} ReusableChildComponent3 aboutToReuse ${JSON.stringify(params)}`);
943  }
944
945  aboutToRecycle(): void {
946    console.info(`${TEST_TAG} ReusableChildComponent3 aboutToRecycle ${this.item}`);
947  }
948
949  build() {
950    Row() {
951      Text(`B${this.item}`)
952        .fontSize(20)
953        .backgroundColor(Color.Yellow)
954        .margin({ left: 10 })
955    }.margin({ left: 10, right: 10 })
956  }
957}
958
959
960@Entry
961@Component
962struct Index {
963  @State data: MyDataSource = new MyDataSource();
964
965  aboutToAppear() {
966    for (let i = 0; i < 100; i++) {
967      this.data.pushData(i.toString());
968    }
969  }
970
971  build() {
972    Column() {
973      List({ space: 3 }) {
974        LazyForEach(this.data, (item: string) => {
975          ListItem() {
976            ReusableChildComponent({
977              item: item,
978              switch: 'open' // Changing open to close can be used to observe the behavior of custom components inside the BuilderNode when reuse and recycle events are not passed through the BuilderNode's reuse and recycle APIs.
979            })
980          }
981        }, (item: string) => item)
982      }
983      .width('100%')
984      .height('100%')
985    }
986  }
987}
988```
989
990### updateConfiguration<sup>12+</sup>
991
992updateConfiguration(): void
993
994Updates the configuration of the entire node by passing in a [system environment change](../apis-ability-kit/js-apis-app-ability-configuration.md) event.
995
996**Atomic service API**: This API can be used in atomic services since API version 12.
997
998**System capability**: SystemCapability.ArkUI.ArkUI.Full
999
1000> **NOTE**
1001>
1002> The **updateConfiguration** API is used to instruct an object to update, with the system environment used for the update being determined by the changes in the application's current system environment.
1003
1004**Example**
1005```ts
1006import { NodeController, BuilderNode, FrameNode, UIContext, FrameCallback } from "@kit.ArkUI";
1007import { AbilityConstant, Configuration, ConfigurationConstant, EnvironmentCallback } from '@kit.AbilityKit';
1008
1009class Params {
1010  text: string = "";
1011
1012  constructor(text: string) {
1013    this.text = text;
1014  }
1015}
1016
1017// Custom component
1018@Component
1019struct TextBuilder {
1020  // The @Prop decorated attribute is the attribute to be updated in the custom component. It is a basic attribute.
1021  @Prop message: string = "TextBuilder";
1022
1023  build() {
1024    Row() {
1025      Column() {
1026        Text(this.message)
1027          .fontSize(50)
1028          .fontWeight(FontWeight.Bold)
1029          .margin({ bottom: 36 })
1030      }
1031    }
1032  }
1033}
1034
1035@Builder
1036function buildText(params: Params) {
1037  Column() {
1038    Text(params.text)
1039      .fontSize(50)
1040      .fontWeight(FontWeight.Bold)
1041      .margin({ bottom: 36 })
1042    TextBuilder({ message: params.text }) // Custom component
1043  }.backgroundColor($r('sys.color.ohos_id_color_background'))
1044}
1045
1046class TextNodeController extends NodeController {
1047  private textNode: BuilderNode<[Params]> | null = null;
1048  private message: string = "";
1049
1050  constructor(message: string) {
1051    super();
1052    this.message = message;
1053  }
1054
1055  makeNode(context: UIContext): FrameNode | null {
1056    return this.textNode?.getFrameNode() ? this.textNode?.getFrameNode() : null;
1057  }
1058
1059  createNode(context: UIContext) {
1060    this.textNode = new BuilderNode(context);
1061    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message));
1062    builderNodeMap.push(this.textNode);
1063  }
1064
1065  deleteNode() {
1066    let node = builderNodeMap.pop();
1067    node?.dispose();
1068  }
1069
1070  update(message: string) {
1071    if (this.textNode !== null) {
1072      // Call update to perform an update.
1073      this.textNode.update(new Params(message));
1074    }
1075  }
1076}
1077
1078// Record the created custom node object.
1079const builderNodeMap: Array<BuilderNode<[Params]>> = new Array();
1080
1081class MyFrameCallback extends FrameCallback {
1082  onFrame() {
1083    updateColorMode();
1084  }
1085}
1086
1087function updateColorMode() {
1088  builderNodeMap.forEach((value, index) => {
1089    // Notify the BuilderNode of the environment changes to trigger switching between light and dark modes.
1090    value.updateConfiguration();
1091  })
1092}
1093
1094@Entry
1095@Component
1096struct Index {
1097  @State message: string = "hello";
1098  private textNodeController: TextNodeController = new TextNodeController(this.message);
1099  private count = 0;
1100
1101  aboutToAppear(): void {
1102    let environmentCallback: EnvironmentCallback = {
1103      onMemoryLevel: (level: AbilityConstant.MemoryLevel): void => {
1104        console.info('onMemoryLevel');
1105      },
1106      onConfigurationUpdated: (config: Configuration): void => {
1107        console.info('onConfigurationUpdated ' + JSON.stringify(config));
1108        this.getUIContext()?.postFrameCallback(new MyFrameCallback());
1109      }
1110    };
1111    // Register a callback.
1112    this.getUIContext().getHostContext()?.getApplicationContext().on('environment', environmentCallback);
1113    // Set the application color mode to follow the system settings.
1114    this.getUIContext()
1115      .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
1116    // Create a custom node and add it to the map.
1117    this.textNodeController.createNode(this.getUIContext());
1118  }
1119
1120  aboutToDisappear(): void {
1121    // Remove the reference to the custom node from the map and release the node.
1122    this.textNodeController.deleteNode();
1123  }
1124
1125  build() {
1126    Row() {
1127      Column() {
1128        NodeContainer(this.textNodeController)
1129          .width('100%')
1130          .height(200)
1131          .backgroundColor('#FFF0F0F0')
1132        Button('Update')
1133          .onClick(() => {
1134            this.count += 1;
1135            const message = "Update " + this.count.toString();
1136            this.textNodeController.update(message);
1137          })
1138        Button('Switch to Dark Mode')
1139          .onClick(() => {
1140            this.getUIContext()
1141              .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK);
1142          })
1143        Button('Switch to Light Mode')
1144          .onClick(() => {
1145            this.getUIContext()
1146              .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT);
1147          })
1148      }
1149      .width('100%')
1150      .height('100%')
1151    }
1152    .height('100%')
1153  }
1154}
1155```
1156
1157### isDisposed<sup>20+</sup>
1158
1159isDisposed(): boolean
1160
1161Checks whether this BuilderNode object has released its reference to its backend entity node. All frontend nodes are bound to corresponding backend entity nodes. After **dispose()** is called, subsequent calls may cause crashes or return default values. This API facilitates validation of node validity prior to operations, thereby mitigating risks in scenarios where calls after disposal are required.
1162
1163**Atomic service API**: This API can be used in atomic services since API version 20.
1164
1165**System capability**: SystemCapability.ArkUI.ArkUI.Full
1166
1167**Return value**
1168
1169| Type   | Description              |
1170| ------- | ------------------ |
1171| boolean | Whether the reference to the backend node is released. The value **true** means that the reference to backend node is released, and **false** means the opposite.
1172
1173### postInputEvent<sup>20+</sup>
1174
1175postInputEvent(event: InputEventType): boolean
1176
1177Dispatches the specified input event to the target node.
1178
1179**offsetA** indicates the BuilderNode's offset relative to its parent component, **offsetB** the hit position's offset relative to the BuilderNode, **offsetC** the composite offset (offsetA + offsetB) passed to the window in **postInputEvent**.
1180
1181![Coordinate conversion example](figures/postInputEvent-point.png)
1182
1183> **NOTE**
1184>
1185> The passed coordinates must be converted to the unit of px. The sample code below demonstrates how to perform such coordinate conversion.
1186>
1187> Mouse left-click events are automatically converted to touch events. Avoid binding both touch and mouse events at the outer layer, as this may cause coordinate offsets. This is because the **SourceType** remains unchanged during event conversion. For details, see [onTouch](arkui-ts/ts-universal-events-touch.md#ontouch).
1188>
1189> Injected [axis events](arkui-ts/ts-universal-events-axis.md#axisevent) cannot trigger the following due to missing axis data: [pinch gesture](arkui-ts/ts-basic-gestures-pinchgesture.md) and [rotate gesture](arkui-ts/ts-basic-gestures-rotationgesture.md).
1190>
1191> A forwarded event undergoes touch testing in the target component's subtree and triggers corresponding gestures. The original event also triggers gestures in the source component tree. There is no guaranteed outcome for gesture competition between these two types of gestures.
1192>
1193> For developer-constructed events, mandatory fields must be assigned values, such as the **touches** field for touch events and the **scrollStep** field for axis events Ensure the completeness of the event, for example, both **DOWN** and **UP** [TouchType](arkui-ts/ts-appendix-enums.md#touchtype) states must be included for a touch event to prevent undefined behavior.
1194>
1195> [Webview](../apis-arkweb/arkts-apis-webview.md) has already handled coordinate system transformation, so events can be dispatched delivered.
1196>
1197> The **postTouchEvent** API needs to provide the gesture coordinates relative to the local coordinates of the target component, and the **postInputEvent** API needs to provide the gesture coordinates relative to the window coordinates.
1198>
1199> Avoid forwarding a single event multiple times. <!--Del-->[UIExtensionComponent](arkui-ts/ts-container-ui-extension-component-sys.md) is not supported.<!--DelEnd-->
1200
1201**Atomic service API**: This API can be used in atomic services since API version 20.
1202
1203**System capability**: SystemCapability.ArkUI.ArkUI.Full
1204
1205**Parameters**
1206
1207| Name| Type                                                                     | Mandatory| Description      |
1208| ------ | ------------------------------------------------------------------------- | ---- | ---------- |
1209| event  | [InputEventType](#inputeventtype20) | Yes  | Input event to dispatch.|
1210
1211**Return value**
1212
1213| Type   | Description              |
1214| ------- | ------------------ |
1215| boolean | Whether the event is successfully dispatched. The value **true** means that the event is successfully dispatched, and **false** means the opposite.|
1216
1217### inheritFreezeOptions<sup>20+</sup>
1218
1219inheritFreezeOptions(enabled: boolean): void
1220
1221Sets whether this **BuilderNode** object inherits the freeze policy from its parent component's custom components. When inheritance is disabled (set to **false**), the object's freeze policy is set to **false**, which means its associated node remains unfrozen even in an inactive state.
1222
1223**Atomic service API**: This API can be used in atomic services since API version 20.
1224
1225**System capability**: SystemCapability.ArkUI.ArkUI.Full
1226
1227**Parameters**
1228
1229| Name| Type  | Mandatory| Description                                                                    |
1230| ------ | ------ | ---- | ------------------------------------------------------------------------ |
1231| enabled  | boolean | Yes | Whether the current **BuilderNode** object inherits the freeze policy from its parent component's custom components. The value **true** means to inherit the freeze policy from parent component's custom components, and **false** means the opposite.|
1232
1233## Example
1234
1235### Example 1: Handling Mouse Events
1236
1237This example demonstrates the end-to-end process for intercepting mouse events in a custom component and performing coordinate conversion. The component: 1. reads local X and Y coordinates through the **onMouse** callback; 2. combines the result with the offset obtained from **FrameNode.getPositionToParent()**; 3. converts relative coordinates to pixel coordinates using **vp2px**; 4. updates the **windowX**, **windowY**, **displayX**, and **displayY** values in **MouseEvent**; 5. dispatches the transformed event to child nodes through **rootNode.postInputEvent(event)**.
1238
1239```ts
1240import { NodeController, BuilderNode, FrameNode, UIContext, InputEventType } from '@kit.ArkUI';
1241
1242class Params {
1243  text: string = "this is a text"
1244  uiContext: UIContext | null = null
1245}
1246
1247@Builder
1248function ButtonBuilder(params: Params) {
1249  Column() {
1250    Button(params.text)
1251      .borderWidth(2)
1252      .align(Alignment.Center)
1253      .backgroundColor(Color.Orange)
1254      .fontSize(20)
1255      .width("45%")
1256      .height("30%")
1257      .offset({ x: 100, y: 100 })
1258      .onMouse(() => {
1259        console.info('onMouse')
1260      })
1261      .onTouch(() => {
1262        console.info('onTouch')
1263      })
1264  }
1265  .width(500)
1266  .height(300)
1267  .backgroundColor(Color.Gray)
1268}
1269
1270class MyNodeController extends NodeController {
1271  private rootNode: BuilderNode<[Params]> | null = null;
1272  private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder);
1273
1274  makeNode(uiContext: UIContext): FrameNode | null {
1275    this.rootNode = new BuilderNode(uiContext);
1276    this.rootNode.build(this.wrapBuilder, { text: "This is a string", uiContext })
1277    return this.rootNode.getFrameNode();
1278  }
1279
1280  postMouseEvent(event: InputEventType, uiContext: UIContext): boolean {
1281    if (this.rootNode == null) {
1282      return false;
1283    }
1284    let node: FrameNode | null = this.rootNode.getFrameNode();
1285    let offsetX: number | null | undefined = node?.getPositionToParent().x;
1286    let offsetY: number | null | undefined = node?.getPositionToParent().y;
1287
1288    let mouseEvent = event as MouseEvent;
1289    if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
1290      mouseEvent.windowX = uiContext.vp2px(offsetX + mouseEvent.x)
1291      mouseEvent.windowY = uiContext.vp2px(offsetY + mouseEvent.y)
1292      mouseEvent.displayX = uiContext.vp2px(offsetX + mouseEvent.x)
1293      mouseEvent.displayY = uiContext.vp2px(offsetY + mouseEvent.y)
1294      mouseEvent.x = uiContext.vp2px(mouseEvent.x)
1295      mouseEvent.y = uiContext.vp2px(mouseEvent.y)
1296    }
1297
1298    let result = this.rootNode.postInputEvent(event);
1299    return result;
1300  }
1301
1302  postTouchEvent(event: InputEventType, uiContext: UIContext): boolean {
1303    if (this.rootNode == null) {
1304      return false;
1305    }
1306    let node: FrameNode | null = this.rootNode.getFrameNode();
1307    let offsetX: number | null | undefined = node?.getPositionToParent().x;
1308    let offsetY: number | null | undefined = node?.getPositionToParent().y;
1309
1310    let touchevent = event as TouchEvent;
1311    let changedTouchLen = touchevent.changedTouches.length;
1312    for (let i = 0; i < changedTouchLen; i++) {
1313      if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
1314        touchevent.changedTouches[i].windowX = uiContext.vp2px(offsetX + touchevent.changedTouches[i].x);
1315        touchevent.changedTouches[i].windowY = uiContext.vp2px(offsetY + touchevent.changedTouches[i].y);
1316        touchevent.changedTouches[i].displayX = uiContext.vp2px(offsetX + touchevent.changedTouches[i].x);
1317        touchevent.changedTouches[i].displayY = uiContext.vp2px(offsetY + touchevent.changedTouches[i].y);
1318      }
1319    }
1320    let touchesLen = touchevent.touches.length;
1321    for (let i = 0; i < touchesLen; i++) {
1322      if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
1323        touchevent.touches[i].windowX = uiContext.vp2px(offsetX + touchevent.touches[i].x);
1324        touchevent.touches[i].windowY = uiContext.vp2px(offsetY + touchevent.touches[i].y);
1325        touchevent.touches[i].displayX = uiContext.vp2px(offsetX + touchevent.touches[i].x);
1326        touchevent.touches[i].displayY = uiContext.vp2px(offsetY + touchevent.touches[i].y);
1327      }
1328    }
1329
1330    let result = this.rootNode.postInputEvent(event);
1331    return result;
1332  }
1333}
1334
1335@Entry
1336@Component
1337struct MyComponent {
1338  private nodeController: MyNodeController = new MyNodeController();
1339
1340  build() {
1341    Stack() {
1342      NodeContainer(this.nodeController)
1343        .height(300)
1344        .width(500)
1345      Column()
1346        .width(500)
1347        .height(300)
1348        .backgroundColor(Color.Transparent)
1349        .onMouse((event) => {
1350          if (event != undefined) {
1351            this.nodeController.postMouseEvent(event, this.getUIContext());
1352          }
1353        })
1354        .onTouch((event) => {
1355          if (event != undefined) {
1356            this.nodeController.postTouchEvent(event, this.getUIContext());
1357          }
1358        })
1359    }.offset({ top: 100 })
1360  }
1361}
1362```
1363
1364![OnMouse](figures/OnMouse.gif)
1365
1366### Example 2: Handling Touch Events
1367
1368This example shows the end-to-end process for intercepting touch events in a custom component and transforming touch point coordinates. The implementation: 1. iterates through **changedTouches** and **touches** arrays in the **onTouch** callback; 2. for each touch point, adds the component offset to the X and Y coordinates and converts the result to pixels using **vp2px**; 3. updates the **windowX**, **windowY**, **displayX**, and **displayY** values of each touch point; 4. dispatches the processed touch event to child nodes using **rootNode.postInputEvent(event)**.
1369
1370```ts
1371import { NodeController, BuilderNode, FrameNode, UIContext, PromptAction, InputEventType  } from '@kit.ArkUI';
1372
1373class Params {
1374  text: string = "this is a text"
1375  uiContext: UIContext | null = null
1376}
1377@Builder
1378function ButtonBuilder(params: Params) {
1379  Column() {
1380    Button(params.text)
1381      .borderWidth(2)
1382      .align(Alignment.Center)
1383      .backgroundColor(Color.Orange)
1384      .fontSize(20)
1385      .width("45%")
1386      .height("30%")
1387      .offset({x: 100, y: 100})
1388      .onTouch((event) => {
1389        let promptAction: PromptAction = params.uiContext!.getPromptAction();
1390        promptAction.showToast({
1391          message: 'onTouch',
1392          duration: 3000
1393        });
1394        console.info('onTouch')
1395      })
1396  }
1397  .width(500)
1398  .height(300)
1399  .backgroundColor(Color.Gray)
1400}
1401class MyNodeController extends NodeController {
1402  private rootNode: BuilderNode<[Params]> | null = null;
1403  private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder);
1404  makeNode(uiContext: UIContext): FrameNode | null {
1405    this.rootNode = new BuilderNode(uiContext);
1406    this.rootNode.build(this.wrapBuilder, { text: "This is a string", uiContext })
1407    return this.rootNode.getFrameNode();
1408  }
1409
1410  postInputEvent(event: InputEventType, uiContext: UIContext): boolean {
1411    if (this.rootNode == null) {
1412      return false;
1413    }
1414    let node: FrameNode | null = this.rootNode.getFrameNode();
1415    let offsetX: number | null | undefined = node?.getPositionToParent().x;
1416    let offsetY: number | null | undefined = node?.getPositionToParent().y;
1417
1418    // Forward only original touch events, not mouse-simulated touch events.
1419    if (event.source == SourceType.TouchScreen) {
1420      let touchevent = event as TouchEvent;
1421      let changedTouchLen = touchevent.changedTouches.length;
1422      for (let i = 0; i < changedTouchLen; i++) {
1423        if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
1424          touchevent.changedTouches[i].windowX = uiContext.vp2px(offsetX + touchevent.changedTouches[i].x);
1425          touchevent.changedTouches[i].windowY = uiContext.vp2px(offsetY + touchevent.changedTouches[i].y);
1426          touchevent.changedTouches[i].displayX = uiContext.vp2px(offsetX + touchevent.changedTouches[i].x);
1427          touchevent.changedTouches[i].displayY = uiContext.vp2px(offsetY + touchevent.changedTouches[i].y);
1428        }
1429      }
1430      let touchesLen = touchevent.touches.length;
1431      for (let i = 0; i < touchesLen; i++) {
1432        if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
1433          touchevent.touches[i].windowX = uiContext.vp2px(offsetX + touchevent.touches[i].x);
1434          touchevent.touches[i].windowY = uiContext.vp2px(offsetY + touchevent.touches[i].y);
1435          touchevent.touches[i].displayX = uiContext.vp2px(offsetX + touchevent.touches[i].x);
1436          touchevent.touches[i].displayY = uiContext.vp2px(offsetY + touchevent.touches[i].y);
1437        }
1438      }
1439    }
1440
1441    let result = this.rootNode.postInputEvent(event);
1442    return result;
1443  }
1444}
1445@Entry
1446@Component
1447struct MyComponent {
1448  private nodeController: MyNodeController = new MyNodeController();
1449  build() {
1450    Stack() {
1451      NodeContainer(this.nodeController)
1452        .height(300)
1453        .width(500)
1454      Column()
1455        .width(500)
1456        .height(300)
1457        .backgroundColor(Color.Transparent)
1458        .onTouch((event) => {
1459          if (event != undefined) {
1460            this.nodeController.postInputEvent(event, this.getUIContext());
1461          }
1462        })
1463    }.offset({top: 100})
1464  }
1465}
1466```
1467
1468![OnTouch](figures/OnTouch.gif)
1469
1470### Example 3: Handling Axis Events
1471
1472This example demonstrates the end-to-end process for intercepting wheel or trackpad axis events in a custom component and performing coordinate conversion. The implementation: 1. obtains relative X and Y coordinates from the **onAxisEvent** callback; 2. adds the component offset and converts the result to pixels using **vp2px**; 3. updates the **windowX**, **windowY**, **displayX**, and **displayY** values in **AxisEvent**; 4. dispatches the transformed axis event to child nodes using **rootNode.postInputEvent(event)**.
1473
1474```ts
1475import { NodeController, BuilderNode, FrameNode, UIContext, PromptAction, InputEventType } from '@kit.ArkUI';
1476
1477class Params {
1478  text: string = "this is a text"
1479  uiContext: UIContext | null = null
1480}
1481@Builder
1482function ButtonBuilder(params: Params) {
1483  Column() {
1484    Button(params.text)
1485      .borderWidth(2)
1486      .align(Alignment.Center)
1487      .backgroundColor(Color.Orange)
1488      .fontSize(20)
1489      .width("45%")
1490      .height("30%")
1491      .offset({x: 100, y: 100})
1492      .onAxisEvent((event) => {
1493        let promptAction: PromptAction = params.uiContext!.getPromptAction();
1494        promptAction.showToast({
1495          message: 'onAxisEvent',
1496          duration: 3000
1497        });
1498        console.info('onAxisEvent')
1499      })
1500  }
1501  .width(500)
1502  .height(300)
1503  .backgroundColor(Color.Gray)
1504}
1505class MyNodeController extends NodeController {
1506  private rootNode: BuilderNode<[Params]> | null = null;
1507  private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder);
1508  makeNode(uiContext: UIContext): FrameNode | null {
1509    this.rootNode = new BuilderNode(uiContext);
1510    this.rootNode.build(this.wrapBuilder, { text: "This is a string", uiContext })
1511    return this.rootNode.getFrameNode();
1512  }
1513
1514  postInputEvent(event: InputEventType, uiContext: UIContext): boolean {
1515    if (this.rootNode == null) {
1516      return false;
1517    }
1518    let node: FrameNode | null = this.rootNode.getFrameNode();
1519    let offsetX: number | null | undefined = node?.getPositionToParent().x;
1520    let offsetY: number | null | undefined = node?.getPositionToParent().y;
1521
1522    let axiseEvent = event as AxisEvent;
1523    if (offsetX != null && offsetY != null && offsetX != undefined && offsetY != undefined) {
1524      axiseEvent.windowX = uiContext.vp2px(offsetX + axiseEvent.x)
1525      axiseEvent.windowY = uiContext.vp2px(offsetY + axiseEvent.y)
1526      axiseEvent.displayX = uiContext.vp2px(offsetX + axiseEvent.x)
1527      axiseEvent.displayY = uiContext.vp2px(offsetY + axiseEvent.y)
1528      axiseEvent.x = uiContext.vp2px(axiseEvent.x)
1529      axiseEvent.y = uiContext.vp2px(axiseEvent.y)
1530    }
1531
1532    let result = this.rootNode.postInputEvent(event);
1533    return result;
1534  }
1535}
1536@Entry
1537@Component
1538struct MyComponent {
1539  private nodeController: MyNodeController = new MyNodeController();
1540  build() {
1541    Stack() {
1542      NodeContainer(this.nodeController)
1543        .height(300)
1544        .width(500)
1545      Column()
1546        .width(500)
1547        .height(300)
1548        .backgroundColor(Color.Transparent)
1549        .onAxisEvent((event) => {
1550          if (event != undefined) {
1551            this.nodeController.postInputEvent(event, this.getUIContext());
1552          }
1553        })
1554    }.offset({top: 100})
1555  }
1556}
1557```
1558
1559![onAxisEvent](figures/onAxisEvent.gif)
1560
1561### Example 4: Passing a BuilderNode Shared localStorage Instance
1562This example demonstrates how to pass an external **localStorage** instance to a BuilderNode through the **build** method, enabling all custom components mounted under the BuilderNode to share the same **localStorage** instance.
1563```ts
1564import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI';
1565
1566class Params {
1567  text: string = ""
1568  constructor(text: string) {
1569    this.text = text;
1570  }
1571}
1572
1573let globalBuilderNode: BuilderNode<[Params]> | null = null;
1574
1575@Builder
1576function buildText(params: Params) {
1577  Column() {
1578    Text('BuildNodeContentArea')
1579      .fontSize(25)
1580    CustomComp()
1581  }
1582}
1583
1584class TextNodeController extends NodeController {
1585  private rootNode: FrameNode | null = null;
1586  makeNode(context: UIContext): FrameNode | null {
1587    this.rootNode = new FrameNode(context);
1588    if (globalBuilderNode === null) {
1589      globalBuilderNode = new BuilderNode(context);
1590      globalBuilderNode.build(wrapBuilder<[Params]>(buildText), new Params('builder node text'), { localStorage: localStorage1 })
1591    }
1592    this.rootNode.appendChild(globalBuilderNode.getFrameNode());
1593    return this.rootNode;
1594  }
1595}
1596
1597let localStorage1: LocalStorage = new LocalStorage();
1598localStorage1.setOrCreate('PropA', 'PropA');
1599
1600@Entry(localStorage1)
1601@Component
1602struct Index {
1603  private controller: TextNodeController = new TextNodeController();
1604  @LocalStorageLink('PropA') PropA: string = 'Hello World';
1605  build() {
1606    Row() {
1607      Column() {
1608        Text(this.PropA)
1609        NodeContainer(this.controller)
1610        Button('changeLocalstorage').onClick(()=>{
1611          localStorage1.set('PropA','AfterChange')
1612        })
1613      }
1614    }
1615  }
1616}
1617@Component
1618struct CustomComp {
1619  @LocalStorageLink('PropA') PropA: string = 'Hello World';
1620  build() {
1621    Row() {
1622      Column() {
1623        Text(this.PropA)
1624      }
1625    }
1626  }
1627}
1628```
1629### Example 5: Checking BuilderNode Reference Status
1630
1631This example shows how to verify a BuilderNode's state using the **isDisposed** API. This API returns **true** before node release and **false** after node release.
1632
1633```ts
1634import { RenderNode, FrameNode, NodeController, BuilderNode } from "@kit.ArkUI";
1635
1636@Component
1637struct TestComponent {
1638  build() {
1639    Column() {
1640      Text('This is a BuilderNode.')
1641        .fontSize(25)
1642        .fontWeight(FontWeight.Bold)
1643    }
1644    .width('100%')
1645    .height(30)
1646    .backgroundColor(Color.Gray)
1647  }
1648
1649  aboutToAppear() {
1650    console.info('aboutToAppear');
1651  }
1652
1653  aboutToDisappear() {
1654    console.info('aboutToDisappear');
1655  }
1656}
1657
1658@Builder
1659function buildComponent() {
1660  TestComponent()
1661}
1662
1663class MyNodeController extends NodeController {
1664  private rootNode: FrameNode | null = null;
1665  private builderNode: BuilderNode<[]> | null = null;
1666
1667  makeNode(uiContext: UIContext): FrameNode | null {
1668    this.rootNode = new FrameNode(uiContext);
1669    this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 200, height: 100 } });
1670    this.builderNode.build(new WrappedBuilder(buildComponent));
1671
1672    const rootRenderNode = this.rootNode!.getRenderNode();
1673    if (rootRenderNode !== null) {
1674      rootRenderNode.size = { width: 300, height: 300 };
1675      rootRenderNode.backgroundColor = 0xffd5d5d5;
1676      rootRenderNode.appendChild(this.builderNode!.getFrameNode()!.getRenderNode());
1677    }
1678
1679    return this.rootNode;
1680  }
1681
1682  dispose() {
1683    if (this.builderNode !== null) {
1684      this.builderNode.dispose();
1685    }
1686  }
1687
1688  isDisposed() : string{
1689    if (this.builderNode !== null) {
1690      if (this.builderNode.isDisposed()) {
1691        return 'builderNode isDisposed is true';
1692      }
1693      else {
1694        return 'builderNode isDisposed is false';
1695      }
1696    }
1697    return 'builderNode is null';
1698  }
1699
1700  removeBuilderNode() {
1701    const rootRenderNode = this.rootNode!.getRenderNode();
1702    if (rootRenderNode !== null && this.builderNode !== null && this.builderNode.getFrameNode() !== null) {
1703      rootRenderNode.removeChild(this.builderNode!.getFrameNode()!.getRenderNode());
1704    }
1705  }
1706}
1707
1708@Entry
1709@Component
1710struct Index {
1711  @State text: string = ''
1712  private myNodeController: MyNodeController = new MyNodeController();
1713
1714  build() {
1715    Column({ space: 4 }) {
1716      NodeContainer(this.myNodeController)
1717      Button('BuilderNode dispose')
1718        .onClick(() => {
1719          this.myNodeController.removeBuilderNode();
1720          this.myNodeController.dispose();
1721          this.text = '';
1722        })
1723        .width(200)
1724        .height(50)
1725      Button('BuilderNode isDisposed')
1726        .onClick(() => {
1727          this.text = this.myNodeController.isDisposed();
1728        })
1729        .width(200)
1730        .height(50)
1731      Text(this.text)
1732        .fontSize(25)
1733    }
1734    .width('100%')
1735    .height('100%')
1736  }
1737}
1738```
1739
1740![isDisposed](figures/builderNode_isDisposed.gif)
1741
1742### Example 6: Configuring BuilderNode Freeze Inheritance
1743
1744This example illustrates how to configure the BuilderNode to inherit the freeze policy from its parent component, resulting in the following behavior: It automatically freezes when in inactive state and thaws and updates cached data when in active state.
1745
1746```ts
1747
1748import { BuilderNode, FrameNode, NodeController } from '@kit.ArkUI';
1749
1750class Params {
1751  count: number = 0;
1752
1753  constructor(count: number) {
1754    this.count = count;
1755  }
1756}
1757
1758@Builder // Builder component
1759function buildText(params: Params) {
1760
1761  Column() {
1762    TextBuilder({ message: params.count })
1763  }
1764}
1765
1766class TextNodeController extends NodeController {
1767  private rootNode: FrameNode | null = null;
1768  private textNode: BuilderNode<[Params]> | null = null;
1769  private count: number = 0;
1770
1771  makeNode(context: UIContext): FrameNode | null {
1772    this.rootNode = new FrameNode(context);
1773    this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } });
1774    this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.count)); // Create a BuilderNode node.
1775    this.textNode.inheritFreezeOptions(true); // Configure the BuilderNode to inherit the freeze policy from its parent component.
1776    if (this.rootNode !== null) {
1777      this.rootNode.appendChild(this.textNode.getFrameNode()); // Mount the BuilderNode to the component tree.
1778    }
1779    return this.rootNode;
1780  }
1781
1782  update(): void {
1783    if (this.textNode !== null) {
1784      this.count += 1;
1785      this.textNode.update(new Params(this.count)); // Update the BuilderNode data, which triggers logs.
1786    }
1787
1788  }
1789}
1790
1791const textNodeController: TextNodeController = new TextNodeController();
1792
1793@Entry
1794@Component
1795struct MyNavigationTestStack {
1796  @Provide('pageInfo') pageInfo: NavPathStack = new NavPathStack();
1797  @State message: number = 0;
1798  @State logNumber: number = 0;
1799
1800  @Builder
1801  PageMap(name: string) {
1802    if (name === 'pageOne') {
1803      pageOneStack({ message: this.message, logNumber: this.logNumber })
1804    } else if (name === 'pageTwo') {
1805      pageTwoStack({ message: this.message, logNumber: this.logNumber })
1806    }
1807  }
1808
1809  build() {
1810    Column() {
1811      Button('update builderNode') // Clicking the button updates builderNode.
1812        .onClick(() => {
1813          textNodeController.update();
1814        })
1815      Navigation(this.pageInfo) {
1816        Column() {
1817          Button('Next Page', { stateEffect: true, type: ButtonType.Capsule })
1818            .width('80%')
1819            .height(40)
1820            .margin(20)
1821            .onClick(() => {
1822              this.pageInfo.pushPath({ name: 'pageOne' }); // Push the navigation destination page specified by name to the navigation stack.
1823            })
1824        }
1825      }.title('NavIndex')
1826      .navDestination(this.PageMap)
1827      .mode(NavigationMode.Stack)
1828    }
1829  }
1830}
1831
1832@Component
1833struct pageOneStack { // Page 1
1834  @Consume('pageInfo') pageInfo: NavPathStack;
1835  @State index: number = 1;
1836  @Link message: number;
1837  @Link logNumber: number;
1838
1839  build() {
1840    NavDestination() {
1841      Column() {
1842        NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber })
1843        Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }) // Navigate to page 2.
1844          .width('80%')
1845          .height(40)
1846          .margin(20)
1847          .onClick(() => {
1848            this.pageInfo.pushPathByName('pageTwo', null);
1849          })
1850        Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }) // Return to the home page.
1851          .width('80%')
1852          .height(40)
1853          .margin(20)
1854          .onClick(() => {
1855            this.pageInfo.pop();
1856          })
1857      }.width('100%').height('100%')
1858    }.title('pageOne')
1859    .onBackPressed(() => {
1860      this.pageInfo.pop();
1861      return true;
1862    })
1863  }
1864}
1865
1866@Component
1867struct pageTwoStack { // Page 2
1868  @Consume('pageInfo') pageInfo: NavPathStack;
1869  @State index: number = 2;
1870  @Link message: number;
1871  @Link logNumber: number;
1872
1873  build() {
1874    NavDestination() {
1875      Column() {
1876        NavigationContentMsgStack({ message: this.message, index: this.index, logNumber: this.logNumber })
1877        Text('BuilderNode is frozen')
1878          .fontWeight(FontWeight.Bold)
1879          .margin({ top: 48, bottom: 48 })
1880        Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }) // Return to page 1.
1881          .width('80%')
1882          .height(40)
1883          .margin(20)
1884          .onClick(() => {
1885            this.pageInfo.pop();
1886          })
1887      }.width('100%').height('100%')
1888    }.title('pageTwo')
1889    .onBackPressed(() => {
1890      this.pageInfo.pop();
1891      return true;
1892    })
1893  }
1894}
1895
1896@Component({ freezeWhenInactive: true }) // Set the freeze policy to freeze when inactive.
1897struct NavigationContentMsgStack {
1898  @Link message: number;
1899  @Link index: number;
1900  @Link logNumber: number;
1901
1902  build() {
1903    Column() {
1904      if (this.index === 1) {
1905        NodeContainer(textNodeController)
1906      }
1907    }
1908  }
1909}
1910
1911@Component({ freezeWhenInactive: true }) // Set the freeze policy to freeze when inactive.
1912struct TextBuilder {
1913  @Prop @Watch("info") message: number = 0;
1914
1915  info() {
1916    console.info(`freeze-test TextBuilder message callback ${this.message}`); // Print logs based on the message content change to determine whether the freeze occurs.
1917  }
1918
1919  build() {
1920    Row() {
1921      Column() {
1922        Text(`Update count: ${this.message}`)
1923          .fontWeight(FontWeight.Bold)
1924          .margin({ top: 48, bottom: 48 })
1925      }
1926    }
1927  }
1928}
1929```
1930
1931
1932
1933### Example 7: Configuring the BuilderNode for Cross-Boundary @Provide-@Consume Communication
1934
1935This example demonstrates how to enable data flow between @Provide in host components and @Consume in BuilderNode's internal components by setting **enableProvideConsumeCrossing** to **true** in **BuildOptions**.
1936
1937```ts
1938import { BuilderNode, NodeContent } from '@kit.ArkUI';
1939
1940@Component
1941struct ConsumeChild {
1942  @Consume @Watch("ChangeData") message: string = ""
1943
1944  ChangeData() {
1945    console.info(`ChangeData ${this.message}`);
1946  }
1947
1948  build() {
1949    Column() {
1950      Text(this.message)
1951        .fontWeight(FontWeight.Bold)
1952        .fontSize(20)
1953      Button("Click to change message to append C")
1954        .fontWeight(FontWeight.Bold)
1955        .onClick(() => {
1956          // Modify the @Consume decorated variable.
1957          this.message = this.message + "C"
1958        })
1959    }
1960  }
1961}
1962
1963@Builder
1964function CreateText(textMessage: string) {
1965  Column() {
1966    Text(textMessage)
1967      .fontWeight(FontWeight.Bold)
1968      .fontSize(20)
1969    ConsumeChild()
1970  }
1971}
1972
1973@Entry
1974@Component
1975struct Index {
1976  @Provide message: string = 'Hello World';
1977  private content: NodeContent = new NodeContent();
1978  private builderNode: BuilderNode<[string]> = new BuilderNode<[string]>(this.getUIContext());
1979
1980  aboutToAppear(): void {
1981    // Enable data flow between @Provide in host components and @Consume in BuilderNode's internal components.
1982    this.builderNode.build(wrapBuilder(CreateText), "Test Consume", { enableProvideConsumeCrossing: true })
1983    this.content.addFrameNode(this.builderNode.getFrameNode())
1984  }
1985
1986  build() {
1987    Column() {
1988      Text(this.message)
1989        .fontWeight(FontWeight.Bold)
1990        .fontSize(20)
1991      Button("Click to change message to append I")
1992        .fontWeight(FontWeight.Bold)
1993        .onClick(() => {
1994          this.message = this.message + "I";
1995        })
1996      Column() {
1997        ContentSlot(this.content)
1998      }
1999    }
2000    .height('100%')
2001    .width('100%')
2002  }
2003}
2004```
2005
2006![enableProvideConsumeCrossing](figures/BuilderNode_Consume.gif)
2007<!--no_check-->