• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# WaterFlow
2
3
4The **\<WaterFlow>** component is a 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
15The [\<FlowItem>](ts-container-flowitem.md) child component is supported.
16
17
18## APIs
19
20
21WaterFlow(options?: {footer?: CustomBuilder, scroller?: Scroller})
22
23**Parameters**
24
25| Name    | Type                                       | Mandatory| Description                                    |
26| ---------- | ----------------------------------------------- | ------ | -------------------------------------------- |
27| footer |  [CustomBuilder](ts-types.md#custombuilder8) | No  | Footer of the **\<WaterFlow>** component. |
28| scroller | [Scroller](ts-container-scroll.md#scroller) | No  | Controller, which can be bound to scrollable components.<br>The **\<WaterFlow>** component supports only the **scrollToIndex** API of the **\<Scroller>** component.|
29
30
31## Attributes
32
33
34In addition to the [universal attributes](ts-universal-attributes-size.md), the following attributes are supported.
35
36| Name| Type| Description|
37| -------- | -------- | -------- |
38| columnsTemplate | string | Number of columns in the layout. If this attribute is not set, one column is used by default.<br>For 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. This attribute supports [auto-fill](#auto-fill).<br>Default value: **'1fr'**|
39| rowsTemplate | string | Number of rows in the layout. If this attribute is not set, one row is used by default.<br>For 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. This attribute supports [auto-fill](#auto-fill).<br>Default value: **'1fr'**|
40| itemConstraintSize | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions) | Size constraints of the child components during layout.              |
41| columnsGap | Length |Gap between columns.<br>Default value: **0**|
42| rowsGap | Length |Gap between rows.<br> Default value: **0**|
43| layoutDirection | [FlexDirection](ts-appendix-enums.md#flexdirection) |Main axis direction of the layout.<br>Default value: **FlexDirection.Column**|
44
45The priority of **layoutDirection** is higher than that of **rowsTemplate** and **columnsTemplate**. Depending on the **layoutDirection** settings, there are three layout modes:
46
47- **layoutDirection** is set to **FlexDirection.Column** or **FlexDirection.ColumnReverse**
48
49	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.
50
51- **layoutDirection** set to **FlexDirection.Row** or **FlexDirection.RowReverse**
52
53	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.
54
55- **layoutDirection** is not set
56
57	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.
58
59## Events
60
61
62In addition to the [universal events](ts-universal-events-click.md), the following events are supported.
63
64
65| Name| Description|
66| -------- | -------- |
67| onReachStart(event: () => void) | Triggered when the component reaches the start.|
68| onReachEnd(event: () => void)   | Triggered when the component reaches the end.|
69
70
71## auto-fill
72
73The **columnsTemplate** and **rowsTemplate** attributes supports **auto-fill** in the following format:
74
75```css
76repeat(auto-fill, track-size)
77```
78
79Where, **repeat** and **auto-fill** are keywords, and **track-size** indicates the row height or column width. The supported units include px, vp, %, and digits. The value of **track-size** must contain at least one valid row height or column width.
80
81
82## Example
83
84
85```ts
86// WaterFlowDataSource.ets
87
88// Object that implements the IDataSource API, which is used by the <WaterFlow> component to load data.
89export class WaterFlowDataSource implements IDataSource {
90
91  private dataArray: number[] = []
92  private listeners: DataChangeListener[] = []
93
94  constructor() {
95      for (let i = 0; i < 100; i++) {
96          this.dataArray.push(i)
97      }
98  }
99
100  // Obtain the data corresponding to the specified index.
101  public getData(index: number): any {
102      return this.dataArray[index]
103  }
104
105  // Notify the controller of data reloading.
106  notifyDataReload(): void {
107      this.listeners.forEach(listener => {
108          listener.onDataReloaded()
109      })
110  }
111
112  // Notify the controller of data addition.
113  notifyDataAdd(index: number): void {
114      this.listeners.forEach(listener => {
115          listener.onDataAdded(index)
116      })
117  }
118
119  // Notify the controller of data changes.
120  notifyDataChange(index: number): void {
121      this.listeners.forEach(listener => {
122          listener.onDataChanged(index)
123      })
124  }
125
126  // Notify the controller of data deletion.
127  notifyDataDelete(index: number): void {
128      this.listeners.forEach(listener => {
129          listener.onDataDeleted(index)
130      })
131  }
132
133  // Notify the controller of the data location change.
134  notifyDataMove(from: number, to: number): void {
135      this.listeners.forEach(listener => {
136          listener.onDataMoved(from, to)
137      })
138  }
139
140  // Obtain the total number of data records.
141  public totalCount(): number {
142      return this.dataArray.length
143  }
144
145  // Register the data change listener.
146  registerDataChangeListener(listener: DataChangeListener): void {
147      if (this.listeners.indexOf(listener) < 0) {
148          this.listeners.push(listener)
149      }
150  }
151
152  // Unregister the data change listener.
153  unregisterDataChangeListener(listener: DataChangeListener): void {
154      const pos = this.listeners.indexOf(listener)
155      if (pos >= 0) {
156          this.listeners.splice(pos, 1)
157      }
158  }
159
160  // Add data.
161  public Add1stItem(): void {
162      this.dataArray.splice(0, 0, this.dataArray.length)
163      this.notifyDataAdd(0)
164  }
165
166  // Add an item to the end of the data.
167  public AddLastItem(): void {
168      this.dataArray.splice(this.dataArray.length, 0, this.dataArray.length)
169      this.notifyDataAdd(this.dataArray.length-1)
170  }
171
172  // Add an item to the position corresponding to the specified index.
173  public AddItem(index: number): void {
174      this.dataArray.splice(index, 0, this.dataArray.length)
175      this.notifyDataAdd(index)
176  }
177
178  // Delete the first item.
179  public Delete1stItem(): void {
180      this.dataArray.splice(0, 1)
181      this.notifyDataDelete(0)
182  }
183
184  // Delete the second item.
185  public Delete2ndItem(): void {
186      this.dataArray.splice(1, 1)
187      this.notifyDataDelete(1)
188  }
189
190  // Delete the last item.
191  public DeleteLastItem(): void {
192      this.dataArray.splice(-1, 1)
193      this.notifyDataDelete(this.dataArray.length)
194  }
195
196  // Reload data.
197  public Reload(): void {
198      this.dataArray.splice(1, 1)
199      this.dataArray.splice(3, 2)
200      this.notifyDataReload()
201  }
202}
203```
204
205```ts
206// WaterflowDemo.ets
207import { WaterFlowDataSource } from './WaterFlowDataSource'
208
209@Entry
210@Component
211struct WaterflowDemo {
212  @State minSize: number = 50
213  @State maxSize: number = 100
214  @State fontSize: number = 24
215  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]
216  scroller: Scroller = new Scroller()
217  datasource: WaterFlowDataSource = new WaterFlowDataSource()
218  private itemWidthArray: number[] = []
219  private itemHeightArray: number[] = []
220
221  // Calculate the width and height of a flow item.
222  getSize() {
223    let ret = Math.floor(Math.random() * this.maxSize)
224    return (ret > this.minSize ? ret : this.minSize)
225  }
226
227  // Save the width and height of the flow item.
228  getItemSizeArray() {
229    for (let i = 0; i < 100; i++) {
230      this.itemWidthArray.push(this.getSize())
231      this.itemHeightArray.push(this.getSize())
232    }
233  }
234
235  aboutToAppear() {
236    this.getItemSizeArray()
237  }
238
239  @Builder itemFoot() {
240    Column() {
241      Text(`Footer`)
242        .fontSize(10)
243        .backgroundColor(Color.Red)
244        .width(50)
245        .height(50)
246        .align(Alignment.Center)
247        .margin({ top: 2 })
248    }
249  }
250
251  build() {
252    Column({ space: 2 }) {
253      WaterFlow({ footer: this.itemFoot, scroller: this.scroller }) {
254        LazyForEach(this.datasource, (item: number) => {
255          FlowItem() {
256            Column() {
257              Text("N" + item).fontSize(12).height('16')
258              Image('res/waterFlowTest(' + item % 5 + ').jpg')
259                .objectFit(ImageFit.Fill)
260            }
261          }
262          .width(this.itemWidthArray[item])
263          .height(this.itemHeightArray[item])
264          .backgroundColor(this.colors[item % 5])
265        }, item => item)
266      }
267      .columnsTemplate("1fr 1fr 1fr 1fr")
268      .itemConstraintSize({
269        minWidth: 0,
270        maxWidth: '100%',
271        minHeight: 0,
272        maxHeight: '100%'
273      })
274      .columnsGap(10)
275      .rowsGap(5)
276      .onReachStart(() => {
277        console.info("onReachStart")
278      })
279      .onReachEnd(() => {
280        console.info("onReachEnd")
281      })
282      .backgroundColor(0xFAEEE0)
283      .width('100%')
284      .height('80%')
285      .layoutDirection(FlexDirection.Column)
286    }
287  }
288}
289```
290
291![en-us_image_WaterFlow.gif](figures/waterflow.gif)
292