1# WaterFlow 2 3 4The **WaterFlow** component is a water flow container that consists of cells formed by rows and columns and arranges items of different sizes from top to bottom according to the preset rules. 5 6 7> **NOTE** 8> 9> This component is supported since API version 9. Updates will be marked with a superscript to indicate their earliest API version. 10 11 12## Child Components 13 14 15Only the [FlowItem](ts-container-flowitem.md) child component is allowed, with support for [if/else](../../../quick-start/arkts-rendering-control-ifelse.md), [ForEach](../../../quick-start/arkts-rendering-control-foreach.md), [LazyForEach](../../../quick-start/arkts-rendering-control-lazyforeach.md), and [Repeat](../../../quick-start/arkts-new-rendering-control-repeat.md) rendering control. 16 17> **NOTE** 18> 19> When its **visibility** attribute is set to **None**, a **FlowItem** is not displayed in the container, but its **columnsGap**, **rowsGap**, and **margin** settings are still effective. 20 21## APIs 22 23WaterFlow(options?: WaterFlowOptions) 24 25Creates a **WaterFlow** component. 26 27**Atomic service API**: This API can be used in atomic services since API version 11. 28 29**System capability**: SystemCapability.ArkUI.ArkUI.Full 30 31**Parameters** 32 33| Name| Type| Mandatory| Description| 34| -------- | -------- | -------- | -------- | 35| options | [WaterFlowOptions](#waterflowoptions)| No| Parameters of the **WaterFlow** component.| 36 37 38## WaterFlowOptions 39 40Provides parameters of the **WaterFlow** component. 41 42**System capability**: SystemCapability.ArkUI.ArkUI.Full 43 44| Name | Type | Mandatory| Description | 45| ---------- | ----------------------------------------------- | ------ | -------------------------------------------- | 46| footer | [CustomBuilder](ts-types.md#custombuilder8) | No | Footer of the **WaterFlow** component.<br>**Atomic service API**: This API can be used in atomic services since API version 11.| 47| footerContent<sup>18+</sup> | [ComponentContent](../js-apis-arkui-ComponentContent.md) | No| Footer of the **WaterFlow** component.<br>This parameter has a higher priority than **footer**. If both **footer** and **footerContent** are set, the component set by **footerContent** will be used.<br>**Atomic service API**: This API can be used in atomic services since API version 18.| 48| scroller | [Scroller](ts-container-scroll.md#scroller) | No | Controller of the scrollable component, bound to the scrollable component.<br>**NOTE**<br>The scroller cannot be bound to other scrollable components, such as [ArcList](ts-container-arclist.md), [List](ts-container-list.md), [Grid](ts-container-grid.md), or [Scroll](ts-container-scroll.md).<br>**Atomic service API**: This API can be used in atomic services since API version 11.| 49| sections<sup>12+</sup> | [WaterFlowSections](#waterflowsections12) | No | Water flow item sections. Different sections can have different numbers of columns.<br>**NOTE**<br>1. When **sections** is used, the **columnsTemplate** and **rowsTemplate** attributes are ignored.<br>2. When **sections** is used, the footer cannot be set separately. The last section can function as the footer.<br>**Atomic service API**: This API can be used in atomic services since API version 12. | 50| layoutMode<sup>12+</sup> |[WaterFlowLayoutMode](#waterflowlayoutmode12)| No| Layout mode of the **WaterFlow** component.<br>**NOTE**<br>Default value: [ALWAYS_TOP_DOWN](#waterflowlayoutmode12)<br>**Atomic service API**: This API can be used in atomic services since API version 12. 51 52 53## WaterFlowSections<sup>12+</sup> 54 55Describes the water flow item sections. 56 57> **NOTE** 58> 59> After the section information is modified using **splice**, **push**, and **update**, ensure that the total number of child nodes in all sections matches the actual total number of child nodes in the **WaterFlow** component. Any failure to do so may result in layout issues that prevent the **WaterFlow** component from scrolling properly. 60 61### constructor 62 63constructor() 64 65A constructor used to create a **WaterFlowSections** object. 66 67**Atomic service API**: This API can be used in atomic services since API version 12. 68 69**System capability**: SystemCapability.ArkUI.ArkUI.Full 70 71### splice<sup>12+</sup> 72 73splice(start: number, deleteCount?: number, sections?: Array\<SectionOptions\>): boolean 74 75Changes sections by removing or replacing an existing section and/or adding a section. 76 77**Atomic service API**: This API can be used in atomic services since API version 12. 78 79**System capability**: SystemCapability.ArkUI.ArkUI.Full 80 81**Parameters** 82 83| Name | Type | Mandatory | Description | 84| ---- | ----------------------------- | ---- | -------------------- | 85| start | number | Yes | Zero-based index at which the changing starts. The value is converted to an integer.<br>**NOTE**<br>1. A negative index counts back from the end of the section list. If -**WaterFlowSections.length()** <= **start** < **0**, **start** + **array.length** is used.<br>2. If **start** < -**WaterFlowSections.length()**, **0** is used.<br>3. If **start** >= **WaterFlowSections.length()**, a new section is added at the end.| 86| deleteCount | number | No | Number of sections to be deleted from the position specified by **start**.<br>**NOTE**<br>1. If **deleteCount** is omitted, or if its value is greater than or equal to the number of sections from the position specified by **start** to the end of the **WaterFlowSections**, then all sections from the position specified by **start** to the end of the **WaterFlowSections** will be deleted.<br>2. If **deleteCount** is **0** or a negative number, no sections are deleted.| 87| sections | Array<[SectionOptions](#sectionoptions12)> | No | Sections to add to the section list, beginning from the position specified by **start**. If no section is specified, **splice()** will only delete sections from the **WaterFlow** component.| 88 89**Return value** 90 91| Type | Description | 92| ------------------------------------------------------------ | ------------------------------------------------------------ | 93| boolean | Whether the changing is successful. If the value of **itemsCount** in any section to add is not a positive integer, **false** is returned.| 94 95 96### push<sup>12+</sup> 97 98push(section: SectionOptions): boolean 99 100Adds the specified sections to the end of the **WaterFlow** component. 101 102**Atomic service API**: This API can be used in atomic services since API version 12. 103 104**System capability**: SystemCapability.ArkUI.ArkUI.Full 105 106**Parameters** 107 108| Name | Type | Mandatory | Description | 109| ---- | ----------------------------- | ---- | -------------------- | 110| section | [SectionOptions](#sectionoptions12) | Yes | Sections to add to the end of the **WaterFlow** component.| 111 112**Return value** 113 114| Type | Description | 115| ------------------------------------------------------------ | ------------------------------------------------------------ | 116| boolean | Whether the adding is successful. If the value of **itemsCount** in any section to add is not a positive integer, **false** is returned.| 117 118### update<sup>12+</sup> 119 120update(sectionIndex: number, section: SectionOptions): boolean 121 122Updates the configuration of a specified water flow item section. 123 124**Atomic service API**: This API can be used in atomic services since API version 12. 125 126**System capability**: SystemCapability.ArkUI.ArkUI.Full 127 128**Parameters** 129 130| Name | Type | Mandatory | Description | 131| ---- | ----------------------------- | ---- | -------------------- | 132| sectionIndex | number | Yes | Zero-based index of the water flow item section to update. The value is converted to an integer.<br>**NOTE**<br>1. A negative index counts back from the end of the section list. If -**WaterFlowSections.length()** <= **sectionIndex** < **0**, **sectionIndex** + **array.length** is used.<br>2. If **sectionIndex** < -**WaterFlowSections.length()**, **0** is used.<br>3. If **sectionIndex** >= **WaterFlowSections.length()**, a new section is added at the end.| 133| section | [SectionOptions](#sectionoptions12) | Yes | New section configuration.| 134 135**Return value** 136 137| Type | Description | 138| ------------------------------------------------------------ | ------------------------------------------------------------ | 139| boolean | Whether the update is successful. If the value of **itemsCount** in any section to add is not a positive integer, **false** is returned.| 140 141### values<sup>12+</sup> 142 143values(): Array\<SectionOptions\> 144 145Obtains the configuration of all sections in the **WaterFlow** component. 146 147**Atomic service API**: This API can be used in atomic services since API version 12. 148 149**System capability**: SystemCapability.ArkUI.ArkUI.Full 150 151**Return value** 152 153| Type | Description | 154| ------------------------------------------------------------ | ------------------------------------------------------------ | 155| Array<[SectionOptions](#sectionoptions12)> | Configuration of all sections in the **WaterFlow** component.| 156 157### length<sup>12+</sup> 158 159length(): number 160 161Obtains the number of sections in the **WaterFlow** component. 162 163**Atomic service API**: This API can be used in atomic services since API version 12. 164 165**System capability**: SystemCapability.ArkUI.ArkUI.Full 166 167**Return value** 168 169| Type | Description | 170| ------------------------------------------------------------ | ------------------------------------------------------------ | 171| number | Number of sections in the **WaterFlow** component.| 172 173## SectionOptions<sup>12+</sup> 174 175Describes the configuration of the water flow item section. 176 177**Atomic service API**: This API can be used in atomic services since API version 12. 178 179**System capability**: SystemCapability.ArkUI.ArkUI.Full 180 181| Name| Type| Mandatory| Description| 182|------|-----|-----|-----| 183| itemsCount | number | Yes| Number of water flow items in the section. The value must be a positive integer. If the **splice**, **push**, or **update** APIs receive a section where the **itemsCount** value is less than 0, these APIs will not be executed.| 184| crossCount | number | No| Number of columns (in vertical layout) or rows (in horizontal layout).<br>Default value: **1**<br> If the value is less than 1, the default value is used.| 185| columnsGap | [Dimension](ts-types.md#dimension10) | No| Gap between columns. If this parameter is not set, the value of **columnsGap** for the water flow is used. If this parameter is set to an invalid value, 0 vp is used.| 186| rowsGap | [Dimension](ts-types.md#dimension10) | No| Gap between rows. If this parameter is not set, the value of **rowsGap** for the water flow is used. If this parameter is set to an invalid value, 0 vp is used.| 187| margin | [Margin](ts-types.md#margin) \| [Dimension](ts-types.md#dimension10) | No| Padding of the section. A value of the Length type specifies the margin for all the four sides.<br>Default value: **0**<br>Unit: vp<br>When **margin** is set to a percentage, the width of the **WaterFlow** component is used as the base value for the top, bottom, left, and right margins.| 188| onGetItemMainSizeByIndex | [GetItemMainSizeByIndex](#getitemmainsizebyindex12) | No| Callback used to obtain the main axis size, in vp, of the water flow item at a specified index during the layout process of the **WaterFlow** component. For a vertical **WaterFlow** component, this size refers to the height, and for a horizontal **WaterFlow** component, it refers to the width.<br>**NOTE**<br>1. When both **onGetItemMainSizeByIndex** and the width or height attribute of the water flow item are used, the main axis size is determined by the return value of **onGetItemMainSizeByIndex**, which will override the main axis length of water flow item.<br>2. Using **onGetItemMainSizeByIndex** can improve the efficiency of jumping to a specific position or index in the **WaterFlow** component. Avoid mixing the use of **onGetItemMainSizeByIndex** with sections that do not have it set, as this can cause layout exceptions.<br>3. If **onGetItemMainSizeByIndex** returns a negative number, the height of the water flow item is 0.| 189 190 191## GetItemMainSizeByIndex<sup>12+</sup> 192 193type GetItemMainSizeByIndex = (index: number) => number 194 195Obtains the main axis size of a specified water flow item based on its index. 196 197**Atomic service API**: This API can be used in atomic services since API version 12. 198 199**System capability**: SystemCapability.ArkUI.ArkUI.Full 200 201**Parameters** 202 203| Name | Type | Mandatory | Description | 204| ---- | ----------------------------- | ---- | -------------------- | 205| index | number | Yes | Index of the target water flow item.<br>Value range: [0, total number of child nodes - 1].| 206 207**Return value** 208 209| Type | Description | 210| ------------------------------------------------------------ | ------------------------------------------------------------ | 211| number | Main axis size, in vp, of the water flow item at the specified index, which is the height for a vertical **WaterFlow** component and the width for a horizontal **WaterFlow** component.| 212 213## WaterFlowLayoutMode<sup>12+</sup> 214 215Enumerates the layout modes of the **WaterFlow** component. 216 217**Atomic service API**: This API can be used in atomic services since API version 12. 218 219**System capability**: SystemCapability.ArkUI.ArkUI.Full 220 221| Name| Value| Description| 222| ------ | ------ | -------------------- | 223| ALWAYS_TOP_DOWN | 0 | Default layout mode where water flow items are arranged from top to bottom. Items in the viewport depend on the layout of all items above them. As such, in cases of redirection or switching the number of columns, the layout of all items above the viewport must be recalculated.| 224| SLIDING_WINDOW | 1 | Sliding window mode. This mode only takes into account the layout in the viewport, without depending on water flow items above the viewport. As such, in cases of redirection backward or switching the number of columns, only the water flow items within the viewport need to be laid out. This mode is recommended.<br>**NOTE**<br>1. During a non-animated redirection to a distant location, water flow items are laid out forward or backward based on the target position. If the user then swipes back to the position prior to the redirection, the layout of the content may not be consistent with its previous state. This can lead to misalignment of the top nodes when the user swipes back to the top after the redirection. To counteract this issue, in this layout mode, the layout will be automatically adjusted after reaching the top of the viewport to ensure that the top is aligned. If there are multiple sections, adjustments will be made to the sections within the viewport when scrolling ends.<br> 2. The total offset returned by the [currentOffset](ts-container-scroll.md#currentoffset) API of [scroller](#waterflowoptions) is inaccurate after a redirection or data update. This offset will be recalibrated when the user swipes back to the top.<br> 3. If a jump action (for example, by calling [scrollToIndex](ts-container-scroll.md#scrolltoindex) without animation or [scrollEdge](ts-container-scroll.md#scrolledge)) and an input offset (such as from a swipe gesture or a scrolling animation) are both initiated within the same frame, both will be executed.<br> 4. If the [scrollToIndex](ts-container-scroll.md#scrolltoindex) API is called without animation to jump to a distant position (beyond the range of visible water flow items in the window), the total offset is calculated in the sliding window mode.| 225 226 227## Attributes 228 229In addition to [universal attributes](ts-component-general-attributes.md) and [scrollable component common attributes](ts-container-scrollable-common.md#attributes), the following attributes are also supported. 230 231### columnsTemplate 232 233columnsTemplate(value: string) 234 235Sets the number of columns in the layout. If this attribute is not set, one column is used by default. 236 237For example, **'1fr 1fr 2fr'** indicates three columns, with the first column taking up 1/4 of the parent component's full width, the second column 1/4, and the third column 2/4. 238 239You can use **columnsTemplate('repeat(auto-fill,track-size)')** to automatically calculate the number of columns based on the specified column width **track-size**. **repeat** and **auto-fill** are keywords. The units for **track-size** can be px, vp (default), %, or a valid number. For details, see Example 2. 240 241**Atomic service API**: This API can be used in atomic services since API version 11. 242 243**System capability**: SystemCapability.ArkUI.ArkUI.Full 244 245**Parameters** 246 247| Name| Type | Mandatory| Description | 248| ------ | ------ | ---- | ---------------------------------------------- | 249| value | string | Yes | Number of columns in the layout.<br>Default value: **'1fr'**| 250 251### rowsTemplate 252 253rowsTemplate(value: string) 254 255Sets the number of rows in the layout. If this attribute is not set, one row is used by default. 256 257For example, **'1fr 1fr 2fr'** indicates three rows, with the first row taking up 1/4 of the parent component's full height, the second row 1/4, and the third row 2/4. 258 259You can use **rowsTemplate('repeat(auto-fill,track-size)')** to automatically calculate the number of rows based on the specified row height **track-size**. **repeat** and **auto-fill** are keywords. The units for **track-size** can be px, vp (default), %, or a valid number. 260 261**Atomic service API**: This API can be used in atomic services since API version 11. 262 263**System capability**: SystemCapability.ArkUI.ArkUI.Full 264 265**Parameters** 266 267| Name| Type | Mandatory| Description | 268| ------ | ------ | ---- | ---------------------------------------------- | 269| value | string | Yes | Number of rows in the layout.<br>Default value: **'1fr'**| 270 271### itemConstraintSize 272 273itemConstraintSize(value: ConstraintSizeOptions) 274 275Sets the size constraints of the child components during layout. 276 277**Atomic service API**: This API can be used in atomic services since API version 11. 278 279**System capability**: SystemCapability.ArkUI.ArkUI.Full 280 281**Parameters** 282 283| Name| Type | Mandatory| Description | 284| ------ | ---------------------------------------------------------- | ---- | ---------- | 285| value | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions) | Yes | Size constraints of the child components during layout. If the value specified is less than 0, this parameter does not take effect.<br>**NOTE**<br>1. If both **itemConstraintSize** and the [constraintSize](ts-universal-attributes-size.md#constraintsize) attribute of the **FlowItem** are set, the **minWidth** (or **minHeight**) will be the larger of the two values, and the **maxWidth** (or **maxHeight**) will be the smaller of the two values. The resulting values will then be used as the **constraintSize** for the **FlowItem**. 2. When only **itemConstraintSize** is set, it effectively applies a uniform size constraint to all child components in the **WaterFlow**. 3. The **itemConstraintSize** attribute, once converted to the **constraintSize** attribute of the **FlowItem** through the two methods mentioned above, follows the same rules for taking effect as the universal attribute [constraintSize](./ts-universal-attributes-size.md#constraintsize).| 286 287### columnsGap 288 289columnsGap(value: Length) 290 291Sets the gap between columns. 292 293**Atomic service API**: This API can be used in atomic services since API version 11. 294 295**System capability**: SystemCapability.ArkUI.ArkUI.Full 296 297**Parameters** 298 299| Name| Type | Mandatory| Description | 300| ------ | ---------------------------- | ---- | ----------------------------- | 301| value | [Length](ts-types.md#length) | Yes | Gap between columns.<br>Default value: **0**<br>Value range: [0, +∞).| 302 303### rowsGap 304 305rowsGap(value: Length) 306 307Sets the gap between rows. 308 309**Atomic service API**: This API can be used in atomic services since API version 11. 310 311**System capability**: SystemCapability.ArkUI.ArkUI.Full 312 313**Parameters** 314 315| Name| Type | Mandatory| Description | 316| ------ | ---------------------------- | ---- | ----------------------------- | 317| value | [Length](ts-types.md#length) | Yes | Gap between rows.<br>Default value: **0**<br>Value range: [0, +∞).| 318 319### layoutDirection 320 321layoutDirection(value: FlexDirection) 322 323Sets the main axis direction of the layout. 324 325**Atomic service API**: This API can be used in atomic services since API version 11. 326 327**System capability**: SystemCapability.ArkUI.ArkUI.Full 328 329**Parameters** 330 331| Name| Type | Mandatory| Description | 332| ------ | --------------------------------------------------- | ---- | ------------------------------------------------- | 333| value | [FlexDirection](ts-appendix-enums.md#flexdirection) | Yes | Main axis direction of the layout.<br>Default value: **FlexDirection.Column**| 334 335The priority of **layoutDirection** is higher than that of **rowsTemplate** and **columnsTemplate**. Depending on the **layoutDirection** settings, there are three layout modes: 336 337- **layoutDirection** is set to **FlexDirection.Column** or **FlexDirection.ColumnReverse** 338 339 In this case, **columnsTemplate** is valid. If it is not set, the default value is used. For example, if **columnsTemplate** is set to **"1fr 1fr"** and **rowsTemplate** **"1fr 1fr 1fr"**, child components are arranged in vertical layout, with the cross axis equally divided into two columns. 340 341- **layoutDirection** set to **FlexDirection.Row** or **FlexDirection.RowReverse** 342 343 In this case, **rowsTemplate** is valid. If it is not set, the default value is used. For example, if **columnsTemplate** is set to **"1fr 1fr"** and **rowsTemplate** **"1fr 1fr 1fr"**, child components are arranged in horizontal layout, with the cross axis equally divided into three columns. 344 345- **layoutDirection** is not set 346 347 In this case, the default value of **layoutDirection** is used, which is **FlexDirection.Column**, and **columnsTemplate** is valid. For example, if **columnsTemplate** is set to **"1fr 1fr"** and **rowsTemplate** **"1fr 1fr 1fr"**, child components are arranged in vertical layout, with the cross axis equally divided into two columns. 348 349### enableScrollInteraction<sup>10+</sup> 350 351enableScrollInteraction(value: boolean) 352 353Sets whether to support scroll gestures. When this attribute is set to **false**, scrolling by finger or mouse is not supported, but the scrolling controller API is not affected. 354 355**Atomic service API**: This API can be used in atomic services since API version 11. 356 357**System capability**: SystemCapability.ArkUI.ArkUI.Full 358 359**Parameters** 360 361| Name| Type | Mandatory| Description | 362| ------ | ------- | ---- | ----------------------------------- | 363| value | boolean | Yes | Whether to support scroll gestures.<br>Default value: **true**| 364 365### nestedScroll<sup>10+</sup> 366 367nestedScroll(value: NestedScrollOptions) 368 369Sets the nested scrolling mode in the forward and backward directions to implement scrolling linkage with the parent component. 370 371**Atomic service API**: This API can be used in atomic services since API version 11. 372 373**System capability**: SystemCapability.ArkUI.ArkUI.Full 374 375**Parameters** 376 377| Name| Type | Mandatory| Description | 378| ------ | ------------------------------------------------------------ | ---- | -------------- | 379| value | [NestedScrollOptions](ts-container-scrollable-common.md#nestedscrolloptions10) | Yes | Nested scrolling options.| 380 381### friction<sup>10+</sup> 382 383friction(value: number | Resource) 384 385Sets the friction coefficient. It applies only to gestures in the scrolling area, and it affects only indirectly the scroll chaining during the inertial scrolling process. 386 387**Atomic service API**: This API can be used in atomic services since API version 11. 388 389**System capability**: SystemCapability.ArkUI.ArkUI.Full 390 391**Parameters** 392 393| Name| Type | Mandatory| Description | 394| ------ | ---------------------------------------------------- | ---- | --------------------------------------------------------- | 395| value | number \| [Resource](ts-types.md#resource) | Yes | Friction coefficient.<br>Default value: **0.9** for wearable devices and **0.6** for non-wearable devices.<br>Since API version 11, the default value for non-wearable devices is **0.7**.<br>Since API version 12, the default value for non-wearable devices is **0.75**.<br>Value range: (0, +∞).<br>If the value is less than or equal to 0, the default value is used.| 396 397### cachedCount<sup>11+</sup> 398 399cachedCount(value: number) 400 401Sets the number of items to be cached. This attribute is effective only in [LazyForEach](../../../quick-start/arkts-rendering-control-lazyforeach.md). After this attribute is set, items that exceed the display and cache range are released. 402 403**Atomic service API**: This API can be used in atomic services since API version 12. 404 405**System capability**: SystemCapability.ArkUI.ArkUI.Full 406 407**Parameters** 408 409| Name| Type | Mandatory| Description | 410| ------ | ------ | ---- | ------------------------------------------------------------ | 411| value | number | Yes | Number of water flow items to be preloaded (cached).<br>Default value: number of nodes visible on the screen, with the maximum value of 16<br>Value range: [0, +∞).<br>Values less than 0 are treated as **1**.| 412 413### cachedCount<sup>14+</sup> 414 415cachedCount(count: number, show: boolean) 416 417Sets the number of water flow items to be cached (preloaded) and specifies whether to display the cached nodes. 418 419When this attribute is used in conjunction with the [clip](ts-universal-attributes-sharp-clipping.md#clip12) or [content clipping](ts-container-scrollable-common.md#clipcontent14) attributes, the cached nodes can be displayed. 420 421In [LazyForEach](../../../quick-start/arkts-rendering-control-lazyforeach.md) and [Repeat](../../../quick-start/arkts-new-rendering-control-repeat.md) with the **virtualScroll** option enabled, water flow items that are outside the display and cache range will be released. 422 423**Atomic service API**: This API can be used in atomic services since API version 14. 424 425**System capability**: SystemCapability.ArkUI.ArkUI.Full 426 427**Parameters** 428 429| Name| Type | Mandatory| Description | 430| ------ | ------ | ---- | ---------------------------------------- | 431| count | number | Yes | Number of water flow items to be preloaded (cached).<br>Default value: number of nodes visible on the screen, with the maximum value of 16<br>Value range: [0, +∞).<br>Values less than 0 are treated as **1**.| 432| show | boolean | Yes | Whether to display the cached water flow items.<br> Default value: **false** (the preloaded water flow items are not displayed).| 433 434## Events 435 436In addition to [universal events](ts-component-general-events.md) and [scrollable component common events](ts-container-scrollable-common.md#events), the following events are also supported. 437 438### onReachStart 439 440onReachStart(event: () => void) 441 442Triggered when the component reaches the start. 443 444**Atomic service API**: This API can be used in atomic services since API version 11. 445 446**System capability**: SystemCapability.ArkUI.ArkUI.Full 447 448### onReachEnd 449 450onReachEnd(event: () => void) 451 452Triggered when the component reaches the end position. 453 454**Atomic service API**: This API can be used in atomic services since API version 11. 455 456**System capability**: SystemCapability.ArkUI.ArkUI.Full 457 458### onScrollFrameBegin<sup>10+</sup> 459 460onScrollFrameBegin(event: (offset: number, state: ScrollState) => { offsetRemain: number; }) 461 462Triggered when the component starts to scroll. The input parameters indicate the amount by which the component will scroll. The event handler then works out the amount by which the component needs to scroll based on the real-world situation and returns the result. 463 464This event is triggered when the user starts dragging the component or the component starts inertial scrolling. It is not triggered when the component rebounds, the scrolling controller is used, or the scrollbar is dragged. 465 466**Atomic service API**: This API can be used in atomic services since API version 11. 467 468**System capability**: SystemCapability.ArkUI.ArkUI.Full 469 470**Parameters** 471 472| Name| Type | Mandatory| Description | 473| ------ | ------------------------------------------------------- | ---- | -------------------------- | 474| offset | number | Yes | Amount to scroll by, in vp.| 475| state | [ScrollState](ts-container-list.md#scrollstate) | Yes | Current scroll state. | 476 477**Return value** 478 479| Type | Description | 480| ------------------------ | -------------------- | 481| { offsetRemain: number } | Actual amount by which the component scrolls, in vp.| 482 483### onScrollIndex<sup>11+</sup> 484 485onScrollIndex(event: (first: number, last: number) => void) 486 487Triggered when the first or last item displayed in the component changes. It is triggered once when the component is initialized. 488 489This event is triggered when either of the preceding indexes changes. 490 491**Atomic service API**: This API can be used in atomic services since API version 11. 492 493**System capability**: SystemCapability.ArkUI.ArkUI.Full 494 495**Parameters** 496 497| Name| Type | Mandatory| Description | 498| ------ | ------ | ---- | ------------------------------------- | 499| first | number | Yes | Index of the first item of the component.<br>Value range: [0, total number of child nodes - 1].| 500| last | number | Yes | Index of the last item of the component.<br>Value range: [0, total number of child nodes - 1].| 501 502## Example 503 504### Example 1: Using a Basic WaterFlow Component 505This example demonstrates the basic usage of the **WaterFlow** component, including data loading, attribute setting, and event callbacks. 506 507<!--code_no_check--> 508```ts 509// WaterFlowDataSource.ets 510 511// Object that implements the IDataSource API, which is used by the WaterFlow component to load data. 512export class WaterFlowDataSource implements IDataSource { 513 private dataArray: number[] = []; 514 private listeners: DataChangeListener[] = []; 515 516 constructor() { 517 for (let i = 0; i < 100; i++) { 518 this.dataArray.push(i); 519 } 520 } 521 522 // Obtain the data corresponding to the specified index. 523 public getData(index: number): number { 524 return this.dataArray[index]; 525 } 526 527 // Notify the controller of data reloading. 528 notifyDataReload(): void { 529 this.listeners.forEach(listener => { 530 listener.onDataReloaded(); 531 }) 532 } 533 534 // Notify the controller of data addition. 535 notifyDataAdd(index: number): void { 536 this.listeners.forEach(listener => { 537 listener.onDataAdd(index); 538 }) 539 } 540 541 // Notify the controller of data changes. 542 notifyDataChange(index: number): void { 543 this.listeners.forEach(listener => { 544 listener.onDataChange(index); 545 }) 546 } 547 548 // Notify the controller of data deletion. 549 notifyDataDelete(index: number): void { 550 this.listeners.forEach(listener => { 551 listener.onDataDelete(index); 552 }) 553 } 554 555 // Notify the controller of the data location change. 556 notifyDataMove(from: number, to: number): void { 557 this.listeners.forEach(listener => { 558 listener.onDataMove(from, to); 559 }) 560 } 561 562 // Notify the controller of batch data modification. 563 notifyDatasetChange(operations: DataOperation[]): void { 564 this.listeners.forEach(listener => { 565 listener.onDatasetChange(operations); 566 }) 567 } 568 569 // Obtain the total number of data records. 570 public totalCount(): number { 571 return this.dataArray.length; 572 } 573 574 // Register the data change listener. 575 registerDataChangeListener(listener: DataChangeListener): void { 576 if (this.listeners.indexOf(listener) < 0) { 577 this.listeners.push(listener); 578 } 579 } 580 581 // Unregister the data change listener. 582 unregisterDataChangeListener(listener: DataChangeListener): void { 583 const pos = this.listeners.indexOf(listener); 584 if (pos >= 0) { 585 this.listeners.splice(pos, 1); 586 } 587 } 588 589 // Add data. 590 public add1stItem(): void { 591 this.dataArray.splice(0, 0, this.dataArray.length); 592 this.notifyDataAdd(0); 593 } 594 595 // Add an item to the end of the data. 596 public addLastItem(): void { 597 this.dataArray.splice(this.dataArray.length, 0, this.dataArray.length); 598 this.notifyDataAdd(this.dataArray.length - 1); 599 } 600 601 // Add an item to the position corresponding to the specified index. 602 public addItem(index: number): void { 603 this.dataArray.splice(index, 0, this.dataArray.length); 604 this.notifyDataAdd(index); 605 } 606 607 // Delete the first item. 608 public delete1stItem(): void { 609 this.dataArray.splice(0, 1); 610 this.notifyDataDelete(0); 611 } 612 613 // Delete the second item. 614 public delete2ndItem(): void { 615 this.dataArray.splice(1, 1); 616 this.notifyDataDelete(1); 617 } 618 619 // Delete the last item. 620 public deleteLastItem(): void { 621 this.dataArray.splice(-1, 1); 622 this.notifyDataDelete(this.dataArray.length); 623 } 624 625 // Delete an item at the specified index position. 626 public deleteItem(index: number): void { 627 this.dataArray.splice(index, 1); 628 this.notifyDataDelete(index); 629 } 630 631 // Reload data. 632 public reload(): void { 633 this.dataArray.splice(1, 1); 634 this.dataArray.splice(3, 2); 635 this.notifyDataReload(); 636 } 637} 638``` 639 640<!--code_no_check--> 641```ts 642// Index.ets 643import { WaterFlowDataSource } from './WaterFlowDataSource'; 644 645@Entry 646@Component 647struct WaterFlowDemo { 648 @State minSize: number = 80; 649 @State maxSize: number = 180; 650 @State fontSize: number = 24; 651 @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]; 652 scroller: Scroller = new Scroller(); 653 dataSource: WaterFlowDataSource = new WaterFlowDataSource(); 654 private itemWidthArray: number[] = []; 655 private itemHeightArray: number[] = []; 656 657 // Calculate the width and height of a water flow item. 658 getSize() { 659 let ret = Math.floor(Math.random() * this.maxSize); 660 return (ret > this.minSize ? ret : this.minSize); 661 } 662 663 // Set the width and height array of the water flow item. 664 setItemSizeArray() { 665 for (let i = 0; i < 100; i++) { 666 this.itemWidthArray.push(this.getSize()); 667 this.itemHeightArray.push(this.getSize()); 668 } 669 } 670 671 aboutToAppear() { 672 this.setItemSizeArray(); 673 } 674 675 @Builder 676 itemFoot() { 677 Column() { 678 Text(`Footer`) 679 .fontSize(10) 680 .backgroundColor(Color.Red) 681 .width(50) 682 .height(50) 683 .align(Alignment.Center) 684 .margin({ top: 2 }) 685 } 686 } 687 688 build() { 689 Column({ space: 2 }) { 690 WaterFlow() { 691 LazyForEach(this.dataSource, (item: number) => { 692 FlowItem() { 693 Column() { 694 Text("N" + item).fontSize(12).height('16') 695 // The image is displayed only when there is a corresponding JPG file. 696 Image('res/waterFlowTest(' + item % 5 + ').jpg') 697 .objectFit(ImageFit.Fill) 698 .width('100%') 699 .layoutWeight(1) 700 } 701 } 702 .onAppear(() => { 703 // Add data in advance when scrolling is about to end. 704 if (item + 20 == this.dataSource.totalCount()) { 705 for (let i = 0; i < 100; i++) { 706 this.dataSource.addLastItem(); 707 } 708 } 709 }) 710 .width('100%') 711 .height(this.itemHeightArray[item % 100]) 712 .backgroundColor(this.colors[item % 5]) 713 }, (item: string) => item) 714 } 715 .columnsTemplate("1fr 1fr") 716 .columnsGap(10) 717 .rowsGap(5) 718 .backgroundColor(0xFAEEE0) 719 .width('100%') 720 .height('100%') 721 .onReachStart(() => { 722 console.info('waterFlow reach start'); 723 }) 724 .onScrollStart(() => { 725 console.info('waterFlow scroll start'); 726 }) 727 .onScrollStop(() => { 728 console.info('waterFlow scroll stop'); 729 }) 730 .onScrollFrameBegin((offset: number, state: ScrollState) => { 731 console.info('waterFlow scrollFrameBegin offset: ' + offset + ' state: ' + state.toString()); 732 return { offsetRemain: offset }; 733 }) 734 } 735 } 736} 737``` 738 739 740 741### Example 2: Implementing Automatic Column Count Calculation 742This example showcases how to implement automatic column count calculation using the **auto-fill** feature. 743 744<!--code_no_check--> 745```ts 746// Index.ets 747import { WaterFlowDataSource } from './WaterFlowDataSource'; 748 749@Entry 750@Component 751struct WaterFlowDemo { 752 @State minSize: number = 80; 753 @State maxSize: number = 180; 754 @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]; 755 dataSource: WaterFlowDataSource = new WaterFlowDataSource(); 756 private itemWidthArray: number[] = []; 757 private itemHeightArray: number[] = []; 758 759 // Calculate the width and height of a water flow item. 760 getSize() { 761 let ret = Math.floor(Math.random() * this.maxSize); 762 return (ret > this.minSize ? ret : this.minSize); 763 } 764 765 // Set the width and height array of the water flow item. 766 setItemSizeArray() { 767 for (let i = 0; i < 100; i++) { 768 this.itemWidthArray.push(this.getSize()); 769 this.itemHeightArray.push(this.getSize()); 770 } 771 } 772 773 aboutToAppear() { 774 this.setItemSizeArray(); 775 } 776 777 build() { 778 Column({ space: 2 }) { 779 WaterFlow() { 780 LazyForEach(this.dataSource, (item: number) => { 781 FlowItem() { 782 Column() { 783 Text("N" + item).fontSize(12).height('16') 784 Image('res/waterFlowTest(' + item % 5 + ').jpg') 785 } 786 } 787 .width('100%') 788 .height(this.itemHeightArray[item % 100]) 789 .backgroundColor(this.colors[item % 5]) 790 }, (item: string) => item) 791 } 792 .columnsTemplate('repeat(auto-fill,80)') 793 .columnsGap(10) 794 .rowsGap(5) 795 .padding({left:5}) 796 .backgroundColor(0xFAEEE0) 797 .width('100%') 798 .height('100%') 799 } 800 } 801} 802``` 803 804 805 806 807### Example 3: Using WaterFlowSections 808This example illustrates the initialization of **WaterFlowSections** and the different effects of various APIs such as **splice**, **push**, **update**, **values**, and **length**. 809For details about how to use these features in conjunction with state management V2, see [WaterFlow](../../../quick-start/arkts-v1-v2-migration.md#waterflow). 810 811<!--code_no_check--> 812```ts 813// Index.ets 814import { WaterFlowDataSource } from './WaterFlowDataSource'; 815 816@Reusable 817@Component 818struct ReusableFlowItem { 819 @State item: number = 0; 820 821 // Invoked when a reusable custom component is re-added to the component tree from the reuse cache. The component state variable can be updated here to display the correct content. 822 aboutToReuse(params: Record<string, number>) { 823 this.item = params.item; 824 console.info('Reuse item:' + this.item); 825 } 826 827 aboutToAppear() { 828 console.info('new item:' + this.item); 829 } 830 831 build() { 832 Image('res/waterFlowTest(' + this.item % 5 + ').jpg') 833 .overlay('N' + this.item, { align: Alignment.Top }) 834 .objectFit(ImageFit.Fill) 835 .width('100%') 836 .layoutWeight(1) 837 } 838} 839 840@Entry 841@Component 842struct WaterFlowDemo { 843 minSize: number = 80; 844 maxSize: number = 180; 845 fontSize: number = 24; 846 colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]; 847 scroller: Scroller = new Scroller(); 848 dataSource: WaterFlowDataSource = new WaterFlowDataSource(); 849 dataCount: number = this.dataSource.totalCount(); 850 private itemHeightArray: number[] = []; 851 @State sections: WaterFlowSections = new WaterFlowSections(); 852 sectionMargin: Margin = { top: 10, left: 5, bottom: 10, right: 5 }; 853 oneColumnSection: SectionOptions = { 854 itemsCount: 4, 855 crossCount: 1, 856 columnsGap: '5vp', 857 rowsGap: 10, 858 margin: this.sectionMargin, 859 onGetItemMainSizeByIndex: (index: number) => { 860 return this.itemHeightArray[index % 100]; 861 } 862 } 863 twoColumnSection: SectionOptions = { 864 itemsCount: 2, 865 crossCount: 2, 866 onGetItemMainSizeByIndex: (index: number) => { 867 return 100; 868 } 869 } 870 lastSection: SectionOptions = { 871 itemsCount: 20, 872 crossCount: 2, 873 onGetItemMainSizeByIndex: (index: number) => { 874 return this.itemHeightArray[index % 100]; 875 } 876 } 877 878 // Calculate the FlowItem height. 879 getSize() { 880 let ret = Math.floor(Math.random() * this.maxSize); 881 return (ret > this.minSize ? ret : this.minSize); 882 } 883 884 // Set the height array for FlowItems. 885 setItemSizeArray() { 886 for (let i = 0; i < 100; i++) { 887 this.itemHeightArray.push(this.getSize()); 888 } 889 } 890 891 aboutToAppear() { 892 this.setItemSizeArray(); 893 // Initialize the water flow section information. 894 let sectionOptions: SectionOptions[] = []; 895 let count = 0; 896 let oneOrTwo = 0; 897 while (count < this.dataCount) { 898 if (this.dataCount - count < 20) { 899 this.lastSection.itemsCount = this.dataCount - count; 900 sectionOptions.push(this.lastSection); 901 break; 902 } 903 if (oneOrTwo++ % 2 == 0) { 904 sectionOptions.push(this.oneColumnSection); 905 count += this.oneColumnSection.itemsCount; 906 } else { 907 sectionOptions.push(this.twoColumnSection); 908 count += this.twoColumnSection.itemsCount; 909 } 910 } 911 this.sections.splice(0, 0, sectionOptions); 912 } 913 914 build() { 915 Column({ space: 2 }) { 916 Row() { 917 Button('splice') 918 .height('5%') 919 .onClick(() => { 920 // Replace all sections with a new section. Ensure that the number of data array items in LazyForEach is the same as the value of itemsCount of the new section. 921 let totalCount: number = this.dataSource.totalCount(); 922 let newSection: SectionOptions = { 923 itemsCount: totalCount, 924 crossCount: 2, 925 onGetItemMainSizeByIndex: (index: number) => { 926 return this.itemHeightArray[index % 100]; 927 } 928 } 929 let oldLength: number = this.sections.length(); 930 this.sections.splice(0, oldLength, [newSection]); 931 }) 932 .margin({ top: 10, left: 20 }) 933 Button('update') 934 .height('5%') 935 .onClick(() => { 936 // Add four FlowItems to the second section. Ensure that the number of data array items in LazyForEach is the same as the sum of itemsCount values of all sections. 937 let newSection: SectionOptions = { 938 itemsCount: 6, 939 crossCount: 3, 940 columnsGap: 5, 941 rowsGap: 10, 942 margin: this.sectionMargin, 943 onGetItemMainSizeByIndex: (index: number) => { 944 return this.itemHeightArray[index % 100]; 945 } 946 } 947 this.dataSource.addItem(this.oneColumnSection.itemsCount); 948 this.dataSource.addItem(this.oneColumnSection.itemsCount + 1); 949 this.dataSource.addItem(this.oneColumnSection.itemsCount + 2); 950 this.dataSource.addItem(this.oneColumnSection.itemsCount + 3); 951 const result: boolean = this.sections.update(1, newSection); 952 console.info('update:' + result); 953 }) 954 .margin({ top: 10, left: 20 }) 955 Button('delete') 956 .height('5%') 957 .onClick(() => { 958 // Click Update and then Delete. 959 let newSection: SectionOptions = { 960 itemsCount: 2, 961 crossCount: 2, 962 columnsGap: 5, 963 rowsGap: 10, 964 margin: this.sectionMargin, 965 onGetItemMainSizeByIndex: (index: number) => { 966 return this.itemHeightArray[index % 100]; 967 } 968 } 969 this.dataSource.deleteItem(this.oneColumnSection.itemsCount); 970 this.dataSource.deleteItem(this.oneColumnSection.itemsCount); 971 this.dataSource.deleteItem(this.oneColumnSection.itemsCount); 972 this.dataSource.deleteItem(this.oneColumnSection.itemsCount); 973 this.sections.update(1, newSection); 974 }) 975 .margin({ top: 10, left: 20 }) 976 Button('values') 977 .height('5%') 978 .onClick(() => { 979 const sections: Array<SectionOptions> = this.sections.values(); 980 for (const value of sections) { 981 console.log(JSON.stringify(value)); 982 } 983 console.info('count:' + this.sections.length()); 984 }) 985 .margin({ top: 10, left: 20 }) 986 }.margin({ bottom: 20 }) 987 988 WaterFlow({ scroller: this.scroller, sections: this.sections }) { 989 LazyForEach(this.dataSource, (item: number) => { 990 FlowItem() { 991 ReusableFlowItem({ item: item }) 992 } 993 .width('100%') 994 // The value of onGetItemMainSizeByIndex is used. 995 // .height(this.itemHeightArray[item % 100]) 996 .backgroundColor(this.colors[item % 5]) 997 }, (item: string) => item) 998 } 999 .columnsTemplate('1fr 1fr') // This attribute is ineffective when the sections parameter is used. 1000 .columnsGap(10) 1001 .rowsGap(5) 1002 .backgroundColor(0xFAEEE0) 1003 .width('100%') 1004 .height('100%') 1005 .layoutWeight(1) 1006 .onScrollIndex((first: number, last: number) => { 1007 // Add data in advance when scrolling is about to end. 1008 if (last + 20 >= this.dataSource.totalCount()) { 1009 for (let i = 0; i < 100; i++) { 1010 this.dataSource.addLastItem(); 1011 } 1012 // After the data source is updated, update sections synchronously and change the number of FlowItems in the last section. 1013 const sections: Array<SectionOptions> = this.sections.values(); 1014 let newSection: SectionOptions = sections[this.sections.length() - 1]; 1015 newSection.itemsCount += 100; 1016 this.sections.update(-1, newSection); 1017 } 1018 }) 1019 } 1020 } 1021} 1022``` 1023 1024 1025 1026### Example 4: Using the Pinch Gesture to Change the Column Count 1027This example demonstrates how to use [priorityGesture](ts-gesture-settings.md) and [PinchGesture](ts-basic-gestures-pinchgesture.md) to implement the feature of using a pinch gesture to change the number of columns in a layout. 1028 1029<!--code_no_check--> 1030```ts 1031// Index.ets 1032import { WaterFlowDataSource } from './WaterFlowDataSource'; 1033import { image } from '@kit.ImageKit'; 1034 1035@Reusable 1036@Component 1037struct ReusableFlowItem { 1038 @State item: number = 0; 1039 1040 // Invoked when a reusable custom component is re-added to the component tree from the reuse cache. The component state variable can be updated here to display the correct content. 1041 aboutToReuse(params: Record<string, number>) { 1042 this.item = params.item; 1043 } 1044 1045 build() { 1046 Column() { 1047 Text("N" + this.item).fontSize(12).height('16') 1048 Image('res/waterFlow(' + this.item % 5 + ').JPG') 1049 .objectFit(ImageFit.Fill) 1050 .width('100%') 1051 .layoutWeight(1) 1052 } 1053 } 1054} 1055 1056@Entry 1057@Component 1058struct WaterFlowDemo { 1059 minSize: number = 80; 1060 maxSize: number = 180; 1061 colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]; 1062 dataSource: WaterFlowDataSource = new WaterFlowDataSource(100); 1063 private itemWidthArray: number[] = []; 1064 private itemHeightArray: number[] = []; 1065 @State columns: number = 2; 1066 @State waterflowScale: number = 1; 1067 @State imageScale: number = 1; 1068 @State waterFlowOpacity: number = 1; 1069 @State waterflowSnapshot: image.PixelMap | undefined = undefined; 1070 private columnChanged: boolean = false; 1071 private oldColumn: number = this.columns; 1072 private pinchTime: number = 0; 1073 1074 // Calculate the width and height of a water flow item. 1075 getSize() { 1076 let ret = Math.floor(Math.random() * this.maxSize); 1077 return (ret > this.minSize ? ret : this.minSize); 1078 } 1079 1080 // Set the width and height array of the water flow item. 1081 setItemSizeArray() { 1082 for (let i = 0; i < 100; i++) { 1083 this.itemWidthArray.push(this.getSize()); 1084 this.itemHeightArray.push(this.getSize()); 1085 } 1086 } 1087 1088 aboutToAppear() { 1089 // Read the last switched column count. 1090 let lastCount = AppStorage.get<number>('columnsCount'); 1091 if (typeof lastCount != 'undefined') { 1092 this.columns = lastCount; 1093 } 1094 this.setItemSizeArray(); 1095 } 1096 1097 // Change the number of columns based on the scale threshold and trigger the WaterFlow component to re-layout. 1098 changeColumns(scale: number) { 1099 if (scale > (this.columns / (this.columns - 0.5)) && this.columns > 1) { 1100 this.columns--; 1101 this.columnChanged = true; 1102 } else if (scale < 1 && this.columns < 4) { 1103 this.columns++; 1104 this.columnChanged = true; 1105 } 1106 } 1107 1108 build() { 1109 Column({ space: 2 }) { 1110 Row() { 1111 Text('Pinch to change the number of columns') 1112 .height('5%') 1113 .margin({ top: 10, left: 20 }) 1114 } 1115 1116 Stack() { 1117 // Display the WaterFlow snapshot before scaling. 1118 Image(this.waterflowSnapshot) 1119 .width('100%') 1120 .height('100%') 1121 .scale({ 1122 x: this.imageScale, 1123 y: this.imageScale, 1124 centerX: 0, 1125 centerY: 0 1126 }) 1127 1128 WaterFlow() { 1129 LazyForEach(this.dataSource, (item: number) => { 1130 FlowItem() { 1131 ReusableFlowItem({ item: item }) 1132 } 1133 .width('100%') 1134 .aspectRatio(this.itemHeightArray[item % 100] / this.itemWidthArray[item%100]) 1135 .backgroundColor(this.colors[item % 5]) 1136 }, (item: string) => item) 1137 } 1138 .id('waterflow') // Set the ID for capturing snapshots. 1139 .columnsTemplate('1fr '.repeat(this.columns)) 1140 .backgroundColor(0xFAEEE0) 1141 .width('100%') 1142 .height('100%') 1143 .layoutWeight(1) 1144 .opacity(this.waterFlowOpacity) 1145 .scale({ 1146 x: this.waterflowScale, 1147 y: this.waterflowScale, 1148 centerX: 0, 1149 centerY: 0 1150 }) 1151 .priorityGesture( 1152 PinchGesture() 1153 .onActionStart((event: GestureEvent) => { 1154 // Take a snapshot when the pinch gesture is recognized. 1155 this.pinchTime = event.timestamp; 1156 this.columnChanged = false; 1157 this.oldColumn = this.columns; 1158 this.getUIContext().getComponentSnapshot().get('waterflow', (error: Error, pixmap: image.PixelMap) => { 1159 if (error) { 1160 console.info('error:' + JSON.stringify(error)); 1161 return; 1162 } 1163 this.waterflowSnapshot = pixmap; 1164 }) 1165 }) 1166 .onActionUpdate((event: GestureEvent) => { 1167 // Limit column scaling. 1168 if ((this.oldColumn === 1 && event.scale > 1) || (this.oldColumn === 4 && event.scale < 1)) { 1169 return; 1170 } 1171 if (event.timestamp - this.pinchTime < 10000000) { 1172 return; 1173 } 1174 this.pinchTime = event.timestamp; 1175 1176 this.waterflowScale = event.scale; 1177 this.imageScale = event.scale; 1178 // Set the WaterFlow opacity based on the scale factor. 1179 this.waterFlowOpacity = (this.waterflowScale > 1) ? (this.waterflowScale - 1) : (1 - this.waterflowScale); 1180 this.waterFlowOpacity *= 3; 1181 if (!this.columnChanged) { 1182 this.changeColumns(event.scale); 1183 } 1184 // Limit the scale factor to avoid blank areas. 1185 if (this.columnChanged) { 1186 this.waterflowScale = this.imageScale * this.columns / this.oldColumn; 1187 if (event.scale < 1) { 1188 this.waterflowScale = this.waterflowScale > 1 ? this.waterflowScale : 1; 1189 } else { 1190 this.waterflowScale = this.waterflowScale < 1 ? this.waterflowScale : 1; 1191 } 1192 } 1193 }) 1194 .onActionEnd((event: GestureEvent) => { 1195 // Animate back to the original position when the finger is released. 1196 animateTo({ duration: 300 }, () => { 1197 this.waterflowScale = 1; 1198 this.waterFlowOpacity = 1; 1199 }) 1200 // Record the current column count. 1201 AppStorage.setOrCreate<number>('columnsCount', this.columns); 1202 }) 1203 ) 1204 } 1205 } 1206 } 1207} 1208``` 1209 1210 1211 1212### Example 5: Setting the Edge Fading Effect 1213This example demonstrates how to enable the edge fading effect for the **WaterFlow** component using the [fadingEdge](ts-container-scrollable-common.md#fadingedge14) API and set the length of the fading edge using the **fadingEdgeLength** parameter. 1214 1215<!--code_no_check--> 1216```ts 1217// Index.ets 1218import { LengthMetrics } from '@kit.ArkUI'; 1219import { WaterFlowDataSource } from './WaterFlowDataSource'; 1220 1221@Entry 1222@Component 1223struct WaterFlowDemo { 1224 @State minSize: number = 80; 1225 @State maxSize: number = 180; 1226 @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]; 1227 dataSource: WaterFlowDataSource = new WaterFlowDataSource(); 1228 scroller: Scroller = new Scroller(); 1229 private itemWidthArray: number[] = []; 1230 private itemHeightArray: number[] = []; 1231 1232 // Calculate the width and height of a water flow item. 1233 getSize() { 1234 let ret = Math.floor(Math.random() * this.maxSize); 1235 return (ret > this.minSize ? ret : this.minSize); 1236 } 1237 1238 // Set the width and height array of the water flow item. 1239 setItemSizeArray() { 1240 for (let i = 0; i < 100; i++) { 1241 this.itemWidthArray.push(this.getSize()); 1242 this.itemHeightArray.push(this.getSize()); 1243 } 1244 } 1245 1246 aboutToAppear() { 1247 this.setItemSizeArray(); 1248 } 1249 1250 build() { 1251 Column({ space: 2 }) { 1252 WaterFlow({ scroller: this.scroller }) { 1253 LazyForEach(this.dataSource, (item: number) => { 1254 FlowItem() { 1255 Column() { 1256 Text("N" + item).fontSize(12).height('16') 1257 } 1258 } 1259 .width('100%') 1260 .height(this.itemHeightArray[item % 100]) 1261 .backgroundColor(this.colors[item % 5]) 1262 }, (item: string) => item) 1263 } 1264 .columnsTemplate('repeat(auto-fill,80)') 1265 .columnsGap(10) 1266 .rowsGap(5) 1267 .height('90%') 1268 .scrollBar(BarState.On) 1269 .fadingEdge(true, { fadingEdgeLength: LengthMetrics.vp(80) }) 1270 } 1271 } 1272} 1273``` 1274 1275 1276 1277### Example 6: Setting the Single-Side Edge Effect 1278 1279This example demonstrates how to set a single-side edge effect for the **WaterFlow** component using the **edgeEffect** API. 1280 1281<!--code_no_check--> 1282```ts 1283// Index.ets 1284import { WaterFlowDataSource } from './WaterFlowDataSource'; 1285 1286@Entry 1287@Component 1288struct WaterFlowDemo { 1289 @State minSize: number = 80; 1290 @State maxSize: number = 180; 1291 @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]; 1292 dataSource: WaterFlowDataSource = new WaterFlowDataSource(); 1293 scroller: Scroller = new Scroller(); 1294 private itemWidthArray: number[] = []; 1295 private itemHeightArray: number[] = []; 1296 1297 // Calculate the width and height of a water flow item. 1298 getSize() { 1299 let ret = Math.floor(Math.random() * this.maxSize); 1300 return (ret > this.minSize ? ret : this.minSize); 1301 } 1302 1303 // Set the width and height array of the water flow item. 1304 setItemSizeArray() { 1305 for (let i = 0; i < 100; i++) { 1306 this.itemWidthArray.push(this.getSize()); 1307 this.itemHeightArray.push(this.getSize()); 1308 } 1309 } 1310 1311 aboutToAppear() { 1312 this.setItemSizeArray(); 1313 } 1314 1315 build() { 1316 Column({ space: 2 }) { 1317 WaterFlow({ scroller: this.scroller }) { 1318 LazyForEach(this.dataSource, (item: number) => { 1319 FlowItem() { 1320 Column() { 1321 Text("N" + item).fontSize(12).height('16') 1322 } 1323 } 1324 .width('100%') 1325 .height(this.itemHeightArray[item % 100]) 1326 .backgroundColor(this.colors[item % 5]) 1327 }, (item: string) => item) 1328 } 1329 .columnsTemplate('repeat(auto-fill,80)') 1330 .columnsGap(10) 1331 .rowsGap(5) 1332 .height('90%') 1333 .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true, effectEdge: EffectEdge.START }) 1334 1335 } 1336 } 1337} 1338``` 1339 1340 1341 1342### Example 7: Setting and Changing the Footer Component in the WaterFlow Component 1343 1344This example demonstrates how to set and dynamically change the footer component in the **WaterFlow** component using **footerContent**. The footer component is updated using the **update** API of **ComponentContent**. 1345 1346<!--code_no_check--> 1347```ts 1348// Index.ets 1349import { ComponentContent, UIContext } from "@kit.ArkUI"; 1350import { WaterFlowDataSource } from './WaterFlowDataSource'; 1351 1352class Params { 1353 text: string = ""; 1354 1355 constructor(text: string) { 1356 this.text = text; 1357 } 1358} 1359 1360@Builder 1361function buildText(params: Params) { 1362 Column() { 1363 Text(params.text) 1364 .fontSize(20) 1365 .fontWeight(FontWeight.Bold) 1366 .margin(20) 1367 } 1368} 1369 1370@Entry 1371@Component 1372struct Index { 1373 @State message1: string = "No more content"; 1374 @State message2: string = "Load more"; 1375 @State colors: number[] = [0xD5D5D5, 0x7F7F7F, 0xF7F7F7]; 1376 @State minSize: number = 80; 1377 @State maxSize: number = 180; 1378 context: UIContext = this.getUIContext(); 1379 footerContent: ComponentContent<Params> = new ComponentContent<Params>(this.context, wrapBuilder<[Params]>(buildText), new Params(this.message1)); 1380 dataSource: WaterFlowDataSource = new WaterFlowDataSource(); 1381 private itemWidthArray: number[] = []; 1382 private itemHeightArray: number[] = []; 1383 1384 // Calculate the width and height of a water flow item. 1385 getSize() { 1386 let ret = Math.floor(Math.random() * this.maxSize); 1387 return (ret > this.minSize ? ret : this.minSize); 1388 } 1389 1390 // Set the width and height array of the water flow item. 1391 setItemSizeArray() { 1392 for (let i = 0; i < 100; i++) { 1393 this.itemWidthArray.push(this.getSize()); 1394 this.itemHeightArray.push(this.getSize()); 1395 } 1396 } 1397 1398 aboutToAppear() { 1399 this.setItemSizeArray(); 1400 } 1401 1402 build() { 1403 Row() { 1404 Column() { 1405 Button("Update footer").width('90%').margin(20) 1406 .onClick((event?: ClickEvent) => { 1407 this.footerContent.update(new Params(this.message2)); 1408 }) 1409 WaterFlow({ footerContent: this.footerContent }) { 1410 LazyForEach(this.dataSource, (item: number) => { 1411 FlowItem() { 1412 Column() { 1413 Text("N" + item).fontSize(12).height('16') 1414 } 1415 .width('100%') 1416 .height(this.itemHeightArray[item % 100]) 1417 .backgroundColor(this.colors[item % 3]) 1418 .justifyContent(FlexAlign.Center) 1419 .alignItems(HorizontalAlign.Center) 1420 } 1421 }, (item: string) => item) 1422 } 1423 .columnsTemplate('1fr') 1424 .height("90%") 1425 } 1426 .width('100%') 1427 .height('100%') 1428 } 1429 .height('100%') 1430 } 1431} 1432``` 1433 1434 1435