• 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| scroller | [Scroller](ts-container-scroll.md#scroller) | No  | Controller of the scrollable component, bound to the scrollable component.<br>**NOTE**<br>The same scroller cannot be bound to other scrollable components, such as [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.|
48| 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. |
49| 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.
50
51
52## WaterFlowSections<sup>12+</sup>
53
54Describes the water flow item sections.
55
56> **NOTE**
57>
58> 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.
59
60### constructor
61
62constructor()
63
64A constructor used to create a **WaterFlowSections** object.
65
66**Atomic service API**: This API can be used in atomic services since API version 12.
67
68**System capability**: SystemCapability.ArkUI.ArkUI.Full
69
70### splice<sup>12+</sup>
71
72splice(start: number, deleteCount?: number, sections?: Array\<SectionOptions\>): boolean
73
74Changes sections by removing or replacing an existing section and/or adding a section.
75
76**Atomic service API**: This API can be used in atomic services since API version 12.
77
78**System capability**: SystemCapability.ArkUI.ArkUI.Full
79
80**Parameters**
81
82| Name  | Type                           | Mandatory  | Description                  |
83| ---- | ----------------------------- | ---- | -------------------- |
84| 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.|
85| 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.|
86| 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.|
87
88**Return value**
89
90| Type                                                        | Description                                                        |
91| ------------------------------------------------------------ | ------------------------------------------------------------ |
92| boolean | Whether the changing is successful. If the value of **itemsCount** in any section to add is not a positive integer, **false** is returned.|
93
94
95### push<sup>12+</sup>
96
97push(section: SectionOptions): boolean
98
99Adds the specified sections to the end of the **WaterFlow** component.
100
101**Atomic service API**: This API can be used in atomic services since API version 12.
102
103**System capability**: SystemCapability.ArkUI.ArkUI.Full
104
105**Parameters**
106
107| Name  | Type                           | Mandatory  | Description                  |
108| ---- | ----------------------------- | ---- | -------------------- |
109| section | [SectionOptions](#sectionoptions12) | Yes   | Sections to add to the end of the **WaterFlow** component.|
110
111**Return value**
112
113| Type                                                        | Description                                                        |
114| ------------------------------------------------------------ | ------------------------------------------------------------ |
115| boolean | Whether the adding is successful. If the value of **itemsCount** in any section to add is not a positive integer, **false** is returned.|
116
117### update<sup>12+</sup>
118
119update(sectionIndex: number, section: SectionOptions): boolean
120
121Updates the configuration of a specified water flow item section.
122
123**Atomic service API**: This API can be used in atomic services since API version 12.
124
125**System capability**: SystemCapability.ArkUI.ArkUI.Full
126
127**Parameters**
128
129| Name  | Type                           | Mandatory  | Description                  |
130| ---- | ----------------------------- | ---- | -------------------- |
131| 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.|
132| section | [SectionOptions](#sectionoptions12) | Yes   | New section configuration.|
133
134**Return value**
135
136| Type                                                        | Description                                                        |
137| ------------------------------------------------------------ | ------------------------------------------------------------ |
138| boolean | Whether the update is successful. If the value of **itemsCount** in any section to add is not a positive integer, **false** is returned.|
139
140### values<sup>12+</sup>
141
142values(): Array\<SectionOptions\>
143
144Obtains the configuration of all sections in the **WaterFlow** component.
145
146**Atomic service API**: This API can be used in atomic services since API version 12.
147
148**System capability**: SystemCapability.ArkUI.ArkUI.Full
149
150**Return value**
151
152| Type                                                        | Description                                                        |
153| ------------------------------------------------------------ | ------------------------------------------------------------ |
154| Array<[SectionOptions](#sectionoptions12)> | Configuration of all sections in the **WaterFlow** component.|
155
156### length<sup>12+</sup>
157
158length(): number
159
160Obtains the number of sections in the **WaterFlow** component.
161
162**Atomic service API**: This API can be used in atomic services since API version 12.
163
164**System capability**: SystemCapability.ArkUI.ArkUI.Full
165
166**Return value**
167
168| Type                                                        | Description                                                        |
169| ------------------------------------------------------------ | ------------------------------------------------------------ |
170| number | Number of sections in the **WaterFlow** component.|
171
172## SectionOptions<sup>12+</sup>
173
174Describes the configuration of the water flow item section.
175
176**Atomic service API**: This API can be used in atomic services since API version 12.
177
178**System capability**: SystemCapability.ArkUI.ArkUI.Full
179
180| Name| Type| Mandatory| Description|
181|------|-----|-----|-----|
182| 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.|
183| 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.|
184| 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.|
185| 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.|
186| 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.|
187| 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.|
188
189
190## GetItemMainSizeByIndex<sup>12+</sup>
191
192type GetItemMainSizeByIndex = (index: number) => number
193
194Obtains the main axis size of a specified water flow item based on its index.
195
196**Atomic service API**: This API can be used in atomic services since API version 12.
197
198**System capability**: SystemCapability.ArkUI.ArkUI.Full
199
200**Parameters**
201
202| Name  | Type                           | Mandatory  | Description                  |
203| ---- | ----------------------------- | ---- | -------------------- |
204| index | number | Yes   | Index of the target water flow item.|
205
206**Return value**
207
208| Type                                                        | Description                                                        |
209| ------------------------------------------------------------ | ------------------------------------------------------------ |
210| 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.|
211
212## WaterFlowLayoutMode<sup>12+</sup>
213
214Enumerates the layout modes of the **WaterFlow** component.
215
216**Atomic service API**: This API can be used in atomic services since API version 12.
217
218**System capability**: SystemCapability.ArkUI.ArkUI.Full
219
220| Name| Value| Description|
221| ------ | ------ | -------------------- |
222| 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.|
223| 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 for applications that involves frequent switching between different numbers of columns.<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 mode does not support the use of scrollbars; they will not be displayed even if set.<br> 3. This mode does not support the [scrollTo](ts-container-scroll.md#scrollto) API of [scroller](#waterflowoptions).<br> 4. 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> 5. 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> 6. 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 not calculated in the sliding window mode, so the total offset remains unchanged. Consequently, the [onDidScroll](ts-container-scroll.md#ondidscroll12) event is not triggered.|
224
225
226## Attributes
227
228In 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.
229
230### columnsTemplate
231
232columnsTemplate(value: string)
233
234Sets the number of columns in the layout. If this attribute is not set, one column is used by default.
235
236For 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.
237
238You 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.
239
240**Atomic service API**: This API can be used in atomic services since API version 11.
241
242**System capability**: SystemCapability.ArkUI.ArkUI.Full
243
244**Parameters**
245
246| Name| Type  | Mandatory| Description                                          |
247| ------ | ------ | ---- | ---------------------------------------------- |
248| value  | string | Yes  | Number of columns in the layout.<br>Default value: **'1fr'**|
249
250### rowsTemplate
251
252rowsTemplate(value: string)
253
254Sets the number of rows in the layout. If this attribute is not set, one row is used by default.
255
256For 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.
257
258You 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.
259
260**Atomic service API**: This API can be used in atomic services since API version 11.
261
262**System capability**: SystemCapability.ArkUI.ArkUI.Full
263
264**Parameters**
265
266| Name| Type  | Mandatory| Description                                          |
267| ------ | ------ | ---- | ---------------------------------------------- |
268| value  | string | Yes  | Number of rows in the layout.<br>Default value: **'1fr'**|
269
270### itemConstraintSize
271
272itemConstraintSize(value: ConstraintSizeOptions)
273
274Sets the size constraints of the child components during layout.
275
276**Atomic service API**: This API can be used in atomic services since API version 11.
277
278**System capability**: SystemCapability.ArkUI.ArkUI.Full
279
280**Parameters**
281
282| Name| Type                                                      | Mandatory| Description      |
283| ------ | ---------------------------------------------------------- | ---- | ---------- |
284| 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).|
285
286### columnsGap
287
288columnsGap(value: Length)
289
290Sets the gap between columns.
291
292**Atomic service API**: This API can be used in atomic services since API version 11.
293
294**System capability**: SystemCapability.ArkUI.ArkUI.Full
295
296**Parameters**
297
298| Name| Type                        | Mandatory| Description                         |
299| ------ | ---------------------------- | ---- | ----------------------------- |
300| value  | [Length](ts-types.md#length) | Yes  | Gap between columns.<br>Default value: **0**|
301
302### rowsGap
303
304rowsGap(value: Length)
305
306Sets the gap between rows.
307
308**Atomic service API**: This API can be used in atomic services since API version 11.
309
310**System capability**: SystemCapability.ArkUI.ArkUI.Full
311
312**Parameters**
313
314| Name| Type                        | Mandatory| Description                         |
315| ------ | ---------------------------- | ---- | ----------------------------- |
316| value  | [Length](ts-types.md#length) | Yes  | Gap between rows.<br>Default value: **0**|
317
318### layoutDirection
319
320layoutDirection(value: FlexDirection)
321
322Sets the main axis direction of the layout.
323
324**Atomic service API**: This API can be used in atomic services since API version 11.
325
326**System capability**: SystemCapability.ArkUI.ArkUI.Full
327
328**Parameters**
329
330| Name| Type                                               | Mandatory| Description                                             |
331| ------ | --------------------------------------------------- | ---- | ------------------------------------------------- |
332| value  | [FlexDirection](ts-appendix-enums.md#flexdirection) | Yes  | Main axis direction of the layout.<br>Default value: **FlexDirection.Column**|
333
334The priority of **layoutDirection** is higher than that of **rowsTemplate** and **columnsTemplate**. Depending on the **layoutDirection** settings, there are three layout modes:
335
336- **layoutDirection** is set to **FlexDirection.Column** or **FlexDirection.ColumnReverse**
337
338  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.
339
340- **layoutDirection** set to **FlexDirection.Row** or **FlexDirection.RowReverse**
341
342  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.
343
344- **layoutDirection** is not set
345
346  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.
347
348### enableScrollInteraction<sup>10+</sup>
349
350enableScrollInteraction(value: boolean)
351
352Sets 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.
353
354**Atomic service API**: This API can be used in atomic services since API version 11.
355
356**System capability**: SystemCapability.ArkUI.ArkUI.Full
357
358**Parameters**
359
360| Name| Type   | Mandatory| Description                               |
361| ------ | ------- | ---- | ----------------------------------- |
362| value  | boolean | Yes  | Whether to support scroll gestures.<br>Default value: **true**|
363
364### nestedScroll<sup>10+</sup>
365
366nestedScroll(value: NestedScrollOptions)
367
368Sets the nested scrolling mode in the forward and backward directions to implement scrolling linkage with the parent component.
369
370**Atomic service API**: This API can be used in atomic services since API version 11.
371
372**System capability**: SystemCapability.ArkUI.ArkUI.Full
373
374**Parameters**
375
376| Name| Type                                                        | Mandatory| Description          |
377| ------ | ------------------------------------------------------------ | ---- | -------------- |
378| value  | [NestedScrollOptions](ts-container-scrollable-common.md#nestedscrolloptions10) | Yes  | Nested scrolling options.|
379
380### friction<sup>10+</sup>
381
382friction(value: number | Resource)
383
384Sets 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. If this attribute is set to a value less than or equal to 0, the default value is used.
385
386**Atomic service API**: This API can be used in atomic services since API version 11.
387
388**System capability**: SystemCapability.ArkUI.ArkUI.Full
389
390**Parameters**
391
392| Name| Type                                                | Mandatory| Description                                                     |
393| ------ | ---------------------------------------------------- | ---- | --------------------------------------------------------- |
394| 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**.|
395
396### cachedCount<sup>11+</sup>
397
398cachedCount(value: number)
399
400Sets 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. A value less than 0 evaluates to the default value.
401
402**Atomic service API**: This API can be used in atomic services since API version 12.
403
404**System capability**: SystemCapability.ArkUI.ArkUI.Full
405
406**Parameters**
407
408| Name| Type  | Mandatory| Description                                                        |
409| ------ | ------ | ---- | ------------------------------------------------------------ |
410| 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|
411
412### cachedCount<sup>14+</sup>
413
414cachedCount(count: number, show: boolean)
415
416Sets the number of water flow items to be cached (preloaded) and specifies whether to display the cached nodes.
417
418When 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.
419
420In [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.
421
422**Atomic service API**: This API can be used in atomic services since API version 14.
423
424**System capability**: SystemCapability.ArkUI.ArkUI.Full
425
426**Parameters**
427
428| Name| Type  | Mandatory| Description                                    |
429| ------ | ------ | ---- | ---------------------------------------- |
430| 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|
431| show  | boolean | Yes  | Whether to display the cached water flow items.<br> Default value: **false**|
432
433## Events
434
435In 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.
436
437### onReachStart
438
439onReachStart(event: () => void)
440
441Triggered when the component reaches the start.
442
443**Atomic service API**: This API can be used in atomic services since API version 11.
444
445**System capability**: SystemCapability.ArkUI.ArkUI.Full
446
447### onReachEnd
448
449onReachEnd(event: () => void)
450
451Triggered when the component reaches the end position.
452
453**Atomic service API**: This API can be used in atomic services since API version 11.
454
455**System capability**: SystemCapability.ArkUI.ArkUI.Full
456
457### onScrollFrameBegin<sup>10+</sup>
458
459onScrollFrameBegin(event: (offset: number, state: ScrollState) => { offsetRemain: number; })
460
461Triggered 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.
462
463This 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.
464
465**Atomic service API**: This API can be used in atomic services since API version 11.
466
467**System capability**: SystemCapability.ArkUI.ArkUI.Full
468
469**Parameters**
470
471| Name| Type                                                   | Mandatory| Description                      |
472| ------ | ------------------------------------------------------- | ---- | -------------------------- |
473| offset | number                                                  | Yes  | Amount to scroll by, in vp.|
474| state  | [ScrollState](ts-container-list.md#scrollstate) | Yes  | Current scroll state.            |
475
476**Return value**
477
478| Type                    | Description                |
479| ------------------------ | -------------------- |
480| { offsetRemain: number } | Actual amount by which the component scrolls, in vp.|
481
482### onScrollIndex<sup>11+</sup>
483
484onScrollIndex(event: (first: number, last: number) => void)
485
486Triggered when the first or last item displayed in the component changes. It is triggered once when the component is initialized.
487
488This event is triggered when either of the preceding indexes changes.
489
490**Atomic service API**: This API can be used in atomic services since API version 11.
491
492**System capability**: SystemCapability.ArkUI.ArkUI.Full
493
494**Parameters**
495
496| Name| Type  | Mandatory| Description                                 |
497| ------ | ------ | ---- | ------------------------------------- |
498| first  | number | Yes  | Index of the first item of the component.|
499| last   | number | Yes  | Index of the last item of the component.   |
500
501## Example
502
503### Example 1: Using a Basic WaterFlow Component
504This example demonstrates the basic usage of the **WaterFlow** component, including data loading, attribute setting, and event callbacks.
505
506<!--code_no_check-->
507```ts
508// WaterFlowDataSource.ets
509
510// Object that implements the IDataSource API, which is used by the WaterFlow component to load data.
511export class WaterFlowDataSource implements IDataSource {
512  private dataArray: number[] = [];
513  private listeners: DataChangeListener[] = [];
514
515  constructor() {
516    for (let i = 0; i < 100; i++) {
517      this.dataArray.push(i);
518    }
519  }
520
521  // Obtain the data corresponding to the specified index.
522  public getData(index: number): number {
523    return this.dataArray[index];
524  }
525
526  // Notify the controller of data reloading.
527  notifyDataReload(): void {
528    this.listeners.forEach(listener => {
529      listener.onDataReloaded();
530    })
531  }
532
533  // Notify the controller of data addition.
534  notifyDataAdd(index: number): void {
535    this.listeners.forEach(listener => {
536      listener.onDataAdd(index);
537    })
538  }
539
540  // Notify the controller of data changes.
541  notifyDataChange(index: number): void {
542    this.listeners.forEach(listener => {
543      listener.onDataChange(index);
544    })
545  }
546
547  // Notify the controller of data deletion.
548  notifyDataDelete(index: number): void {
549    this.listeners.forEach(listener => {
550      listener.onDataDelete(index);
551    })
552  }
553
554  // Notify the controller of the data location change.
555  notifyDataMove(from: number, to: number): void {
556    this.listeners.forEach(listener => {
557      listener.onDataMove(from, to);
558    })
559  }
560
561  // Notify the controller of batch data modification.
562  notifyDatasetChange(operations: DataOperation[]): void {
563    this.listeners.forEach(listener => {
564      listener.onDatasetChange(operations);
565    })
566  }
567
568  // Obtain the total number of data records.
569  public totalCount(): number {
570    return this.dataArray.length;
571  }
572
573  // Register the data change listener.
574  registerDataChangeListener(listener: DataChangeListener): void {
575    if (this.listeners.indexOf(listener) < 0) {
576      this.listeners.push(listener);
577    }
578  }
579
580  // Unregister the data change listener.
581  unregisterDataChangeListener(listener: DataChangeListener): void {
582    const pos = this.listeners.indexOf(listener);
583    if (pos >= 0) {
584      this.listeners.splice(pos, 1);
585    }
586  }
587
588  // Add data.
589  public add1stItem(): void {
590    this.dataArray.splice(0, 0, this.dataArray.length);
591    this.notifyDataAdd(0);
592  }
593
594  // Add an item to the end of the data.
595  public addLastItem(): void {
596    this.dataArray.splice(this.dataArray.length, 0, this.dataArray.length);
597    this.notifyDataAdd(this.dataArray.length - 1);
598  }
599
600  // Add an item to the position corresponding to the specified index.
601  public addItem(index: number): void {
602    this.dataArray.splice(index, 0, this.dataArray.length);
603    this.notifyDataAdd(index);
604  }
605
606  // Delete the first item.
607  public delete1stItem(): void {
608    this.dataArray.splice(0, 1);
609    this.notifyDataDelete(0);
610  }
611
612  // Delete the second item.
613  public delete2ndItem(): void {
614    this.dataArray.splice(1, 1);
615    this.notifyDataDelete(1);
616  }
617
618  // Delete the last item.
619  public deleteLastItem(): void {
620    this.dataArray.splice(-1, 1);
621    this.notifyDataDelete(this.dataArray.length);
622  }
623
624  // Delete an item at the specified index position.
625  public deleteItem(index: number): void {
626    this.dataArray.splice(index, 1);
627    this.notifyDataDelete(index);
628  }
629
630  // Reload data.
631  public reload(): void {
632    this.dataArray.splice(1, 1);
633    this.dataArray.splice(3, 2);
634    this.notifyDataReload();
635  }
636}
637```
638
639<!--code_no_check-->
640```ts
641// Index.ets
642import { WaterFlowDataSource } from './WaterFlowDataSource';
643
644@Entry
645@Component
646struct WaterFlowDemo {
647  @State minSize: number = 80;
648  @State maxSize: number = 180;
649  @State fontSize: number = 24;
650  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F];
651  scroller: Scroller = new Scroller();
652  dataSource: WaterFlowDataSource = new WaterFlowDataSource();
653  private itemWidthArray: number[] = [];
654  private itemHeightArray: number[] = [];
655
656  // Calculate the width and height of a water flow item.
657  getSize() {
658    let ret = Math.floor(Math.random() * this.maxSize);
659    return (ret > this.minSize ? ret : this.minSize);
660  }
661
662  // Set the width and height array of the water flow item.
663  setItemSizeArray() {
664    for (let i = 0; i < 100; i++) {
665      this.itemWidthArray.push(this.getSize());
666      this.itemHeightArray.push(this.getSize());
667    }
668  }
669
670  aboutToAppear() {
671    this.setItemSizeArray();
672  }
673
674  @Builder
675  itemFoot() {
676    Column() {
677      Text(`Footer`)
678        .fontSize(10)
679        .backgroundColor(Color.Red)
680        .width(50)
681        .height(50)
682        .align(Alignment.Center)
683        .margin({ top: 2 })
684    }
685  }
686
687  build() {
688    Column({ space: 2 }) {
689      WaterFlow() {
690        LazyForEach(this.dataSource, (item: number) => {
691          FlowItem() {
692            Column() {
693              Text("N" + item).fontSize(12).height('16')
694              // The image is displayed only when there is a corresponding JPG file.
695              Image('res/waterFlowTest(' + item % 5 + ').jpg')
696                .objectFit(ImageFit.Fill)
697                .width('100%')
698                .layoutWeight(1)
699            }
700          }
701          .onAppear(() => {
702            // Add data in advance when scrolling is about to end.
703            if (item + 20 == this.dataSource.totalCount()) {
704              for (let i = 0; i < 100; i++) {
705                this.dataSource.addLastItem();
706              }
707            }
708          })
709          .width('100%')
710          .height(this.itemHeightArray[item % 100])
711          .backgroundColor(this.colors[item % 5])
712        }, (item: string) => item)
713      }
714      .columnsTemplate("1fr 1fr")
715      .columnsGap(10)
716      .rowsGap(5)
717      .backgroundColor(0xFAEEE0)
718      .width('100%')
719      .height('100%')
720      .onReachStart(() => {
721        console.info('waterFlow reach start');
722      })
723      .onScrollStart(() => {
724        console.info('waterFlow scroll start');
725      })
726      .onScrollStop(() => {
727        console.info('waterFlow scroll stop');
728      })
729      .onScrollFrameBegin((offset: number, state: ScrollState) => {
730        console.info('waterFlow scrollFrameBegin offset: ' + offset + ' state: ' + state.toString());
731        return { offsetRemain: offset };
732      })
733    }
734  }
735}
736```
737
738![zh-cn_image_WaterFlow.gif](figures/waterflow-perf-demo.gif)
739
740### Example 2: Implementing Automatic Column Count Calculation
741This example showcases how to implement automatic column count calculation using the **auto-fill** feature.
742
743<!--code_no_check-->
744```ts
745// Index.ets
746import { WaterFlowDataSource } from './WaterFlowDataSource';
747
748@Entry
749@Component
750struct WaterFlowDemo {
751  @State minSize: number = 80;
752  @State maxSize: number = 180;
753  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F];
754  dataSource: WaterFlowDataSource = new WaterFlowDataSource();
755  private itemWidthArray: number[] = [];
756  private itemHeightArray: number[] = [];
757
758  // Calculate the width and height of a water flow item.
759  getSize() {
760    let ret = Math.floor(Math.random() * this.maxSize);
761    return (ret > this.minSize ? ret : this.minSize);
762  }
763
764  // Set the width and height array of the water flow item.
765  setItemSizeArray() {
766    for (let i = 0; i < 100; i++) {
767      this.itemWidthArray.push(this.getSize());
768      this.itemHeightArray.push(this.getSize());
769    }
770  }
771
772  aboutToAppear() {
773    this.setItemSizeArray();
774  }
775
776  build() {
777    Column({ space: 2 }) {
778      WaterFlow() {
779        LazyForEach(this.dataSource, (item: number) => {
780          FlowItem() {
781            Column() {
782              Text("N" + item).fontSize(12).height('16')
783              Image('res/waterFlowTest(' + item % 5 + ').jpg')
784            }
785          }
786          .width('100%')
787          .height(this.itemHeightArray[item % 100])
788          .backgroundColor(this.colors[item % 5])
789        }, (item: string) => item)
790      }
791      .columnsTemplate('repeat(auto-fill,80)')
792      .columnsGap(10)
793      .rowsGap(5)
794      .padding({left:5})
795      .backgroundColor(0xFAEEE0)
796      .width('100%')
797      .height('100%')
798    }
799  }
800}
801```
802
803![waterflow_auto-fill.png](figures/waterflow_auto-fill.png)
804
805
806### Example 3: Using WaterFlowSections
807This example illustrates the initialization of **WaterFlowSections** and the different effects of various APIs such as **splice**, **push**, **update**, **values**, and **length**.
808For details about how to use these features in conjunction with state management V2, see [WaterFlow](../../../quick-start/arkts-v1-v2-migration.md#waterflow).
809
810<!--code_no_check-->
811```ts
812// Index.ets
813import { WaterFlowDataSource } from './WaterFlowDataSource';
814
815@Reusable
816@Component
817struct ReusableFlowItem {
818  @State item: number = 0;
819
820  // 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.
821  aboutToReuse(params: Record<string, number>) {
822    this.item = params.item;
823    console.info('Reuse item:' + this.item);
824  }
825
826  aboutToAppear() {
827    console.info('new item:' + this.item);
828  }
829
830  build() {
831    Image('res/waterFlowTest(' + this.item % 5 + ').jpg')
832        .overlay('N' + this.item, { align: Alignment.Top })
833        .objectFit(ImageFit.Fill)
834        .width('100%')
835        .layoutWeight(1)
836  }
837}
838
839@Entry
840@Component
841struct WaterFlowDemo {
842  minSize: number = 80;
843  maxSize: number = 180;
844  fontSize: number = 24;
845  colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F];
846  scroller: Scroller = new Scroller();
847  dataSource: WaterFlowDataSource = new WaterFlowDataSource();
848  dataCount: number = this.dataSource.totalCount();
849  private itemHeightArray: number[] = [];
850  @State sections: WaterFlowSections = new WaterFlowSections();
851  sectionMargin: Margin = { top: 10, left: 5, bottom: 10, right: 5 };
852  oneColumnSection: SectionOptions = {
853    itemsCount: 4,
854    crossCount: 1,
855    columnsGap: '5vp',
856    rowsGap: 10,
857    margin: this.sectionMargin,
858    onGetItemMainSizeByIndex: (index: number) => {
859      return this.itemHeightArray[index % 100];
860    }
861  }
862  twoColumnSection: SectionOptions = {
863    itemsCount: 2,
864    crossCount: 2,
865    onGetItemMainSizeByIndex: (index: number) => {
866      return 100;
867    }
868  }
869  lastSection: SectionOptions = {
870    itemsCount: 20,
871    crossCount: 2,
872    onGetItemMainSizeByIndex: (index: number) => {
873      return this.itemHeightArray[index % 100];
874    }
875  }
876
877  // Calculate the FlowItem height.
878  getSize() {
879    let ret = Math.floor(Math.random() * this.maxSize);
880    return (ret > this.minSize ? ret : this.minSize);
881  }
882
883  // Set the height array for FlowItems.
884  setItemSizeArray() {
885    for (let i = 0; i < 100; i++) {
886      this.itemHeightArray.push(this.getSize());
887    }
888  }
889
890  aboutToAppear() {
891    this.setItemSizeArray();
892    // Initialize the water flow section information.
893    let sectionOptions: SectionOptions[] = [];
894    let count = 0;
895    let oneOrTwo = 0;
896    while (count < this.dataCount) {
897      if (this.dataCount - count < 20) {
898        this.lastSection.itemsCount = this.dataCount - count;
899        sectionOptions.push(this.lastSection);
900        break;
901      }
902      if (oneOrTwo++ % 2 == 0) {
903        sectionOptions.push(this.oneColumnSection);
904        count += this.oneColumnSection.itemsCount;
905      } else {
906        sectionOptions.push(this.twoColumnSection);
907        count += this.twoColumnSection.itemsCount;
908      }
909    }
910    this.sections.splice(0, 0, sectionOptions);
911  }
912
913  build() {
914    Column({ space: 2 }) {
915      Row() {
916        Button('splice')
917          .height('5%')
918          .onClick(() => {
919            // 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.
920            let totalCount: number = this.dataSource.totalCount();
921            let newSection: SectionOptions = {
922              itemsCount: totalCount,
923              crossCount: 2,
924              onGetItemMainSizeByIndex: (index: number) => {
925                return this.itemHeightArray[index % 100];
926              }
927            }
928            let oldLength: number = this.sections.length();
929            this.sections.splice(0, oldLength, [newSection]);
930          })
931          .margin({ top: 10, left: 20 })
932        Button('update')
933          .height('5%')
934          .onClick(() => {
935            // 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.
936            let newSection: SectionOptions = {
937              itemsCount: 6,
938              crossCount: 3,
939              columnsGap: 5,
940              rowsGap: 10,
941              margin: this.sectionMargin,
942              onGetItemMainSizeByIndex: (index: number) => {
943                return this.itemHeightArray[index % 100];
944              }
945            }
946            this.dataSource.addItem(this.oneColumnSection.itemsCount);
947            this.dataSource.addItem(this.oneColumnSection.itemsCount + 1);
948            this.dataSource.addItem(this.oneColumnSection.itemsCount + 2);
949            this.dataSource.addItem(this.oneColumnSection.itemsCount + 3);
950            const result: boolean = this.sections.update(1, newSection);
951            console.info('update:' + result);
952          })
953          .margin({ top: 10, left: 20 })
954        Button('delete')
955          .height('5%')
956          .onClick(() => {
957            // Click Update and then Delete.
958            let newSection: SectionOptions = {
959              itemsCount: 2,
960              crossCount: 2,
961              columnsGap: 5,
962              rowsGap: 10,
963              margin: this.sectionMargin,
964              onGetItemMainSizeByIndex: (index: number) => {
965                return this.itemHeightArray[index % 100];
966              }
967            }
968            this.dataSource.deleteItem(this.oneColumnSection.itemsCount);
969            this.dataSource.deleteItem(this.oneColumnSection.itemsCount);
970            this.dataSource.deleteItem(this.oneColumnSection.itemsCount);
971            this.dataSource.deleteItem(this.oneColumnSection.itemsCount);
972            this.sections.update(1, newSection);
973          })
974          .margin({ top: 10, left: 20 })
975        Button('values')
976          .height('5%')
977          .onClick(() => {
978            const sections: Array<SectionOptions> = this.sections.values();
979            for (const value of sections) {
980              console.log(JSON.stringify(value));
981            }
982            console.info('count:' + this.sections.length());
983          })
984          .margin({ top: 10, left: 20 })
985      }.margin({ bottom: 20 })
986
987      WaterFlow({ scroller: this.scroller, sections: this.sections }) {
988        LazyForEach(this.dataSource, (item: number) => {
989          FlowItem() {
990            ReusableFlowItem({ item: item })
991          }
992          .width('100%')
993          // The value of onGetItemMainSizeByIndex is used.
994          // .height(this.itemHeightArray[item % 100])
995          .backgroundColor(this.colors[item % 5])
996        }, (item: string) => item)
997      }
998      .columnsTemplate('1fr 1fr') // This attribute is ineffective when the sections parameter is used.
999      .columnsGap(10)
1000      .rowsGap(5)
1001      .backgroundColor(0xFAEEE0)
1002      .width('100%')
1003      .height('100%')
1004      .layoutWeight(1)
1005      .onScrollIndex((first: number, last: number) => {
1006        // Add data in advance when scrolling is about to end.
1007        if (last + 20 >= this.dataSource.totalCount()) {
1008          for (let i = 0; i < 100; i++) {
1009            this.dataSource.addLastItem();
1010          }
1011          // After the data source is updated, update sections synchronously and change the number of FlowItems in the last section.
1012          const sections: Array<SectionOptions> = this.sections.values();
1013          let newSection: SectionOptions = sections[this.sections.length() - 1];
1014          newSection.itemsCount += 100;
1015          this.sections.update(-1, newSection);
1016        }
1017      })
1018    }
1019  }
1020}
1021```
1022
1023![waterflowSections.png](figures/waterflowSections.png)
1024
1025### Example 4: Using the Pinch Gesture to Change the Column Count
1026This 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.
1027
1028<!--code_no_check-->
1029```ts
1030// Index.ets
1031import { WaterFlowDataSource } from './WaterFlowDataSource';
1032
1033@Reusable
1034@Component
1035struct ReusableFlowItem {
1036  @State item: number = 0;
1037
1038  // 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.
1039  aboutToReuse(params: Record<string, number>) {
1040    this.item = params.item;
1041    console.info('Reuse item:' + this.item);
1042  }
1043
1044  aboutToAppear() {
1045    console.info('item:' + this.item);
1046  }
1047
1048  build() {
1049    Column() {
1050      Text("N" + this.item).fontSize(12).height('16')
1051      Image('res/waterFlow (' + this.item % 5 + ').JPG')
1052        .objectFit(ImageFit.Fill)
1053        .width('100%')
1054        .layoutWeight(1)
1055    }
1056  }
1057}
1058
1059@Entry
1060@Component
1061struct WaterFlowDemo {
1062  minSize: number = 80;
1063  maxSize: number = 180;
1064  colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F];
1065  @State columns: number = 2;
1066  dataSource: WaterFlowDataSource = new WaterFlowDataSource();
1067  private itemWidthArray: number[] = [];
1068  private itemHeightArray: number[] = [];
1069
1070  // Calculate the width and height of a water flow item.
1071  getSize() {
1072    let ret = Math.floor(Math.random() * this.maxSize);
1073    return (ret > this.minSize ? ret : this.minSize);
1074  }
1075
1076  // Set the width and height array of the water flow item.
1077  setItemSizeArray() {
1078    for (let i = 0; i < 100; i++) {
1079      this.itemWidthArray.push(this.getSize());
1080      this.itemHeightArray.push(this.getSize());
1081    }
1082  }
1083
1084  aboutToAppear() {
1085    let lastCount = AppStorage.get<number>('columnsCount');
1086    if (typeof lastCount != 'undefined') {
1087      this.columns = lastCount;
1088    }
1089    this.setItemSizeArray();
1090  }
1091
1092  build() {
1093    Column({ space: 2 }) {
1094      Row() {
1095        Text('Pinch to change the number of columns')
1096          .height('5%')
1097          .margin({ top: 10, left: 20 })
1098      }
1099
1100      WaterFlow() {
1101        LazyForEach(this.dataSource, (item: number) => {
1102          FlowItem() {
1103            ReusableFlowItem({ item: item })
1104          }
1105          .width('100%')
1106          .height(this.itemHeightArray[item % 100])
1107          .backgroundColor(this.colors[item % 5])
1108        }, (item: string) => item)
1109      }
1110      .columnsTemplate('1fr '.repeat(this.columns))
1111      .columnsGap(10)
1112      .rowsGap(5)
1113      .backgroundColor(0xFAEEE0)
1114      .width('100%')
1115      .height('100%')
1116      .layoutWeight(1)
1117      // Switching the number of columns triggers a reordering animation for the item positions.
1118      .animation({
1119        duration: 300,
1120        curve: Curve.Smooth
1121      })
1122      .priorityGesture(
1123        PinchGesture()
1124          .onActionEnd((event: GestureEvent) => {
1125            console.info('end scale:' + event.scale);
1126            // When a user performs a pinch-to-zoom gesture by moving their fingers apart, and the number of columns decreases to a certain threshold (in this case, 2), it will cause the items to enlarge.
1127            if (event.scale > 2) {
1128              this.columns--;
1129            } else if (event.scale < 0.6) {
1130              this.columns++;
1131            }
1132            // You can set the maximum and minimum number of columns based on the device screen width. Here, the minimum number of columns is 1, and the maximum number of columns is 4.
1133            this.columns = Math.min(4, Math.max(1, this.columns));
1134            AppStorage.setOrCreate<number>('columnsCount', this.columns);
1135          })
1136      )
1137    }
1138  }
1139}
1140```
1141
1142![pinch](figures/waterflow-pinch.gif)
1143
1144### Example 5: Setting the Edge Fading Effect
1145This 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.
1146
1147<!--code_no_check-->
1148```ts
1149// Index.ets
1150import { LengthMetrics } from '@kit.ArkUI';
1151import { WaterFlowDataSource } from './WaterFlowDataSource';
1152@Entry
1153@Component
1154struct WaterFlowDemo {
1155  @State minSize: number = 80;
1156  @State maxSize: number = 180;
1157  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F];
1158  dataSource: WaterFlowDataSource = new WaterFlowDataSource();
1159  scroller: Scroller = new Scroller();
1160  private itemWidthArray: number[] = [];
1161  private itemHeightArray: number[] = [];
1162
1163  // Calculate the width and height of a water flow item.
1164  getSize() {
1165    let ret = Math.floor(Math.random() * this.maxSize);
1166    return (ret > this.minSize ? ret : this.minSize);
1167  }
1168
1169  // Set the width and height array of the water flow item.
1170  setItemSizeArray() {
1171    for (let i = 0; i < 100; i++) {
1172      this.itemWidthArray.push(this.getSize());
1173      this.itemHeightArray.push(this.getSize());
1174    }
1175  }
1176
1177  aboutToAppear() {
1178    this.setItemSizeArray();
1179  }
1180
1181  build() {
1182    Column({ space: 2 }) {
1183
1184      WaterFlow({ scroller:this.scroller }) {
1185        LazyForEach(this.dataSource, (item: number) => {
1186          FlowItem() {
1187            Column() {
1188              Text("N" + item).fontSize(12).height('16')
1189            }
1190          }
1191          .width('100%')
1192          .height(this.itemHeightArray[item % 100])
1193          .backgroundColor(this.colors[item % 5])
1194        }, (item: string) => item)
1195      }
1196      .columnsTemplate('repeat(auto-fill,80)')
1197      .columnsGap(10)
1198      .rowsGap(5)
1199      .height('90%')
1200      .scrollBar(BarState.On)
1201      .fadingEdge(true,{fadingEdgeLength:LengthMetrics.vp(80)})
1202
1203    }
1204  }
1205}
1206```
1207
1208![fadingEdge_waterFlow](figures/fadingEdge_waterFlow.gif)
1209