• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![zh-cn_image_WaterFlow.gif](figures/waterflow-perf-demo.gif)
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![waterflow_auto-fill.png](figures/waterflow_auto-fill.png)
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![waterflowSections.png](figures/waterflowSections.png)
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![pinch](figures/waterflow-pinch.gif)
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![fadingEdge_waterFlow](figures/fadingEdge_waterFlow.gif)
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![edgeEffect_waterFlow](figures/edgeEffect_waterflow.gif)
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![waterFlow_footerContent](figures/waterFlow_footerContent.gif)
1435