1# Refresh
2
3The **Refresh** component is a container that provides the pull-to-refresh feature.
4
5>  **NOTE**
6>
7>  - This component is supported since API version 8. Updates will be marked with a superscript to indicate their earliest API version.
8>
9>  - This component provides linkage with a vertical scrolling **Swiper** and **Web** component since API version 12. The linkage does not work if the **loop** attribute of **Swiper** is set to **true**.
10
11## Child Components
12
13This component supports only one child component.
14
15Since API version 11, this component's child component moves down with the pull-down gesture.
16
17## APIs
18
19Refresh(value: RefreshOptions)
20
21**Atomic service API**: This API can be used in atomic services since API version 11.
22
23**System capability**: SystemCapability.ArkUI.ArkUI.Full
24
25**Parameters**
26
27| Name| Type| Mandatory| Description|
28| -------- | -------- | -------- | -------- |
29| value |  [RefreshOptions](#refreshoptions)| Yes| Parameters of the **Refresh** component.|
30
31## RefreshOptions
32
33**System capability**: SystemCapability.ArkUI.ArkUI.Full
34
35| Name        | Type                                     | Mandatory  | Description                                    |
36| ---------- | ---------------------------------------- | ---- | ---------------------------------------- |
37| refreshing | boolean                                  | Yes   | Whether the component is being refreshed. The value **true** means that the component is being refreshed, and **false** means the opposite.<br>Default value: **false**<br>This parameter supports [$$](../../../quick-start/arkts-two-way-sync.md) for two-way binding of variables.<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
38| offset<sup>(deprecated)</sup>    | string \| number               | No   | Distance from the pull-down starting point to the top of the component.<br>Default value: **16**, in vp<br>This API is deprecated since API version 11. No substitute API is provided.<br>**NOTE**<br>The value range of **offset** is [0vp, 64vp]. If the value is greater than 64 vp, the value 64 vp will be used. The value cannot be a percentage or a negative number.|
39| friction<sup>(deprecated)</sup>   | number \| string               | No   | Coefficient of friction, which indicates the **<Refresh\>** component's sensitivity to the pull-down gesture. The value ranges from 0 to 100.<br>Default value: **62**<br>- **0** indicates that the **Refresh** component is not responsive to the pull-down gesture.<br>- **100** indicates that the **Refresh** component is highly responsive to the pull-down gesture.<br>- A larger value indicates higher responsiveness of the **Refresh** component to the pull-down gesture.<br>This API is deprecated since API version 11. You can use [pullDownRatio](#pulldownratio12) instead since API version 12.|
40| builder<sup>10+</sup>    | [CustomBuilder](ts-types.md#custombuilder8) | No   | Custom content in the refreshing area.<br>**NOTE**<br>In API version 10 and earlier versions, there is a height limit of 64 vp on custom components. This restriction is removed since API version 11.<br>When a custom component is set with a fixed height, it will be displayed below the refreshing area at that fixed height; when the custom component does not have a height set, its height will adapt to the height of the refreshing area, which may result in the height of the custom component changing to 0 along with the refreshing area. To prevent its height from being less than expected, you are advised to set a minimum height constraint for the custom component. For reference, see [Example 2](#example-2).<br>Since API version 12, use **refreshingContent** instead of **builder** for customizing the content of the refreshing area, to avoid animation interruptions caused by the destruction and re-creation of the custom component during the refreshing process.<br>**Atomic service API**: This API can be used in atomic services since API version 11.|
41| promptText<sup>12+</sup> | [ResourceStr](ts-types.md#resourcestr) | No| Custom text displayed at the bottom of the refreshing area.<br>**NOTE**<br>When setting the text, follow the constraints on the **Text** components. If you are using **builder** or **refreshingContent** to customize the content displayed in the refreshing area, the text set with **promptText** will not be displayed.<br>When **promptText** is set and effective, the [refreshOffset](#refreshoffset12) attribute defaults to 96 vp.<br>**Atomic service API**: This API can be used in atomic services since API version 12.|
42| refreshingContent<sup>12+</sup>    | [ComponentContent](../js-apis-arkui-ComponentContent.md) | No   | Custom content in the refreshing area.<br>**NOTE**<br>If this parameter and the **builder** parameter are set at the same time, the **builder** parameter does not take effect.<br>When a custom component is set with a fixed height, it will be displayed below the refreshing area at that fixed height; when the custom component does not have a height set, its height will adapt to the height of the refreshing area, which may result in the height of the custom component changing to 0 along with the refreshing area. To prevent its height from being less than expected, you are advised to set a minimum height constraint for the custom component. For reference, see [Example 5](#example-5).<br>**Atomic service API**: This API can be used in atomic services since API version 12.|
43
44## Attributes
45
46In addition to the [universal attributes](ts-universal-attributes-size.md), the following attributes are supported.
47
48### refreshOffset<sup>12+</sup>
49
50refreshOffset(value: number)
51
52Sets the pull-down offset that initiates a refresh.
53
54**Atomic service API**: This API can be used in atomic services since API version 12.
55
56**System capability**: SystemCapability.ArkUI.ArkUI.Full
57
58**Parameters**
59
60| Name| Type                                       | Mandatory| Description                                                      |
61| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------- |
62| value  | number |  Yes| Pull-down offset, in vp.<br>Default value: 96 vp when [promptText](#refreshoptions) is set and 64 vp when [promptText](#refreshoptions) is not<br>If the value specified is 0 or less than 0, the default value is used.|
63
64### pullToRefresh<sup>12+</sup>
65
66pullToRefresh(value: boolean)
67
68Sets whether to initiate a refresh when the pull-down distance exceeds the value of [refreshOffset](#refreshoffset12).
69
70**Atomic service API**: This API can be used in atomic services since API version 12.
71
72**System capability**: SystemCapability.ArkUI.ArkUI.Full
73
74**Parameters**
75
76| Name| Type                                       | Mandatory| Description                                                      |
77| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------- |
78| value  | boolean |  Yes| Whether to initiate a refresh when the pull-down distance exceeds the value of [refreshOffset](#refreshoffset12). The value **true** means to initiate a refresh, and **false** means the opposite.<br>Default value: **true**|
79
80### pullDownRatio<sup>12+</sup>
81
82pullDownRatio(ratio: [Optional](ts-universal-attributes-custom-property.md#optional12)\<number>)
83
84Sets the pull-down ratio.
85
86**Atomic service API**: This API can be used in atomic services since API version 12.
87
88**System capability**: SystemCapability.ArkUI.ArkUI.Full
89
90**Parameters**
91
92| Name| Type                                       | Mandatory| Description                                                      |
93| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------- |
94| ratio  | [Optional](ts-universal-attributes-custom-property.md#optional12)\<number> |  Yes| Pull-down ratio. A larger value indicates higher responsiveness to the pull-down gesture. The value **0** indicates that the pull-down does not follow the gesture, and **1** indicates that the pull-down follows the gesture proportionally.<br>If this parameter is not set or is set to **undefined**, a dynamic pull-down ratio is used. That is, the larger the pull-down distance, the smaller the ratio.<br>The value ranges from 0 to 1. A value less than 0 is handled as **0**, and a value greater than 1 is handled as **1**.
95
96## Events
97
98In addition to the [universal events](ts-universal-events-click.md), the following events are supported.
99
100### onStateChange
101
102onStateChange(callback: (state: RefreshStatus) => void)
103
104Called when the refresh status changes.
105
106**Atomic service API**: This API can be used in atomic services since API version 11.
107
108**System capability**: SystemCapability.ArkUI.ArkUI.Full
109
110**Parameters**
111
112| Name| Type                                   | Mandatory| Description      |
113| ------ | --------------------------------------- | ---- | ---------- |
114| state  | [RefreshStatus](#refreshstatus) | Yes  | Refresh status.|
115
116### onRefreshing
117
118onRefreshing(callback: () => void)
119
120Called when the component starts refreshing.
121
122**Atomic service API**: This API can be used in atomic services since API version 11.
123
124**System capability**: SystemCapability.ArkUI.ArkUI.Full
125
126### onOffsetChange<sup>12+</sup>
127
128onOffsetChange(callback: Callback\<number>)
129
130Called when the pull-down distance changes.
131
132**Atomic service API**: This API can be used in atomic services since API version 12.
133
134**System capability**: SystemCapability.ArkUI.ArkUI.Full
135
136**Parameters**
137
138| Name| Type                                   | Mandatory| Description      |
139| ------ | --------------------------------------- | ---- | ---------- |
140| value  | number | Yes  | Pull-down distance.<br>Unit: vp|
141
142
143## RefreshStatus
144
145**Atomic service API**: This API can be used in atomic services since API version 11.
146
147**System capability**: SystemCapability.ArkUI.ArkUI.Full
148
149| Name      | Value      | Description                  |
150| -------- | -------- | -------------------- |
151| Inactive | 0 | The component is not pulled down. This is the default value.            |
152| Drag     | 1 | The component is being pulled down, but the pull-down distance is shorter than the minimum length required to trigger the refresh.     |
153| OverDrag | 2 | The component is being pulled down, and the pull-down distance exceeds the minimum length required to trigger the refresh.     |
154| Refresh  | 3 | The pull-down ends, and the component rebounds to the minimum length required to trigger the refresh and enters the refreshing state.|
155| Done     | 4 | The refresh is complete, and the component returns to the initial state (at the top).    |
156
157
158## Example
159
160### Example 1
161
162This example implements a **Refresh** with its refreshing area in the default style.
163
164```ts
165// xxx.ets
166@Entry
167@Component
168struct RefreshExample {
169  @State isRefreshing: boolean = false
170  @State arr: String[] = ['0', '1', '2', '3', '4','5','6','7','8','9','10']
171
172  build() {
173    Column() {
174      Refresh({ refreshing: $$this.isRefreshing}) {
175        List() {
176          ForEach(this.arr, (item: string) => {
177            ListItem() {
178              Text('' + item)
179                .width('70%').height(80).fontSize(16).margin(10)
180                .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF)
181            }
182          }, (item: string) => item)
183        }
184        .onScrollIndex((first: number) => {
185          console.info(first.toString())
186        })
187        .width('100%')
188        .height('100%')
189        .alignListItem(ListItemAlign.Center)
190        .scrollBar(BarState.Off)
191      }
192      .onStateChange((refreshStatus: RefreshStatus) => {
193        console.info('Refresh onStatueChange state is ' + refreshStatus)
194      })
195      .onOffsetChange((value: number) => {
196        console.info('Refresh onOffsetChange offset:' + value)
197      })
198      .onRefreshing(() => {
199        setTimeout(() => {
200          this.isRefreshing = false
201        }, 2000)
202        console.log('onRefreshing test')
203      })
204      .backgroundColor(0x89CFF0)
205      .refreshOffset(64)
206      .pullToRefresh(true)
207    }
208  }
209}
210```
211
212![en-us_image_refresh_example1](figures/en-us_image_refresh_example1.gif)
213
214### Example 2
215
216This example implements a **Refresh** with its refreshing area displaying the custom content defined with **builder**.
217
218```ts
219// xxx.ets
220@Entry
221@Component
222struct RefreshExample {
223  @State isRefreshing: boolean = false
224  @State arr: String[] = ['0', '1', '2', '3', '4','5','6','7','8','9','10']
225  @Builder
226  customRefreshComponent()
227  {
228    Stack()
229    {
230      Row()
231      {
232        LoadingProgress().height(32)
233        Text("Refreshing...").fontSize(16).margin({left:20})
234      }
235      .alignItems(VerticalAlign.Center)
236    }
237    .align(Alignment.Center)
238    .clip(true)
239    .constraintSize({minHeight:32}) // Setting a minimum height constraint ensures that the height of the custom component does not fall below the specified minHeight when the height of the refreshing area changes.
240    .width("100%")
241  }
242
243  build() {
244    Column() {
245      Refresh({ refreshing: $$this.isRefreshing,builder:this.customRefreshComponent()}) {
246        List() {
247          ForEach(this.arr, (item: string) => {
248            ListItem() {
249              Text('' + item)
250                .width('70%').height(80).fontSize(16).margin(10)
251                .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF)
252            }
253          }, (item: string) => item)
254        }
255        .onScrollIndex((first: number) => {
256          console.info(first.toString())
257        })
258        .width('100%')
259        .height('100%')
260        .alignListItem(ListItemAlign.Center)
261        .scrollBar(BarState.Off)
262      }
263      .backgroundColor(0x89CFF0)
264      .pullToRefresh(true)
265      .refreshOffset(64)
266      .onStateChange((refreshStatus: RefreshStatus) => {
267        console.info('Refresh onStatueChange state is ' + refreshStatus)
268      })
269      .onRefreshing(() => {
270        setTimeout(() => {
271          this.isRefreshing = false
272        }, 2000)
273        console.log('onRefreshing test')
274      })
275    }
276  }
277}
278```
279
280![en-us_image_refresh_example2](figures/en-us_image_refresh_example2.gif)
281
282### Example 3
283
284This example implements a **Refresh** component that produces a bounce effect when the boundary is reached.
285
286```ts
287// Index.ets
288@Entry
289@Component
290struct ListRefreshLoad {
291  @State arr: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
292  @State refreshing: boolean = false;
293  @State refreshOffset: number = 0;
294  @State refreshState: RefreshStatus = RefreshStatus.Inactive;
295  @State canLoad: boolean = false;
296  @State isLoading: boolean = false;
297
298  @Builder
299  refreshBuilder() {
300    Stack({ alignContent: Alignment.Bottom }) {
301      // can use the refresh state to decide whether the progress component is exist or not.
302      // in this case, the component is not exist otherwise in the pull down or refresh state
303      if (this.refreshState != RefreshStatus.Inactive && this.refreshState != RefreshStatus.Done) {
304        Progress({ value: this.refreshOffset, total: 64, type: ProgressType.Ring })
305          .width(32).height(32)
306          .style({ status: this.refreshing ? ProgressStatus.LOADING : ProgressStatus.PROGRESSING })
307          .margin(10)
308      }
309    }
310    .clip(true)
311    .height("100%")
312    .width("100%")
313  }
314
315  @Builder
316  footer() {
317    Row() {
318      LoadingProgress().height(32).width(48)
319      Text("Loading")
320    }.width("100%")
321    .height(64)
322    .justifyContent(FlexAlign.Center)
323    // hidden this component when don't need to load
324    .visibility(this.isLoading ? Visibility.Visible : Visibility.Hidden)
325  }
326
327  build() {
328    Refresh({ refreshing: $$this.refreshing, builder: this.refreshBuilder() }) {
329      List() {
330        ForEach(this.arr, (item: number) => {
331          ListItem() {
332            Text('' + item)
333              .width('100%')
334              .height(80)
335              .fontSize(16)
336              .textAlign(TextAlign.Center)
337              .backgroundColor(0xFFFFFF)
338          }.borderWidth(1)
339        }, (item: string) => item)
340
341        ListItem() {
342          this.footer();
343        }
344      }
345      .onScrollIndex((start: number, end: number) => {
346        // when reach the end of list, trigger data load
347        if (this.canLoad && end >= this.arr.length - 1) {
348          this.canLoad = false;
349          this.isLoading = true;
350          // simulate trigger data load
351          setTimeout(() => {
352            for (let i = 0; i < 10; i++) {
353              this.arr.push(this.arr.length);
354              this.isLoading = false;
355            }
356          }, 700)
357        }
358      })
359      .onScrollFrameBegin((offset: number, state: ScrollState) => {
360        // loading can be triggered only when swipe up
361        if (offset > 5 && !this.isLoading) {
362          this.canLoad = true;
363        }
364        return { offsetRemain: offset };
365      })
366      .scrollBar(BarState.Off)
367      // open the spring back of edge
368      .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true })
369    }
370    .width('100%')
371    .height('100%')
372    .backgroundColor(0xDCDCDC)
373    .onOffsetChange((offset: number) => {
374      this.refreshOffset = offset;
375    })
376    .onStateChange((state: RefreshStatus) => {
377      this.refreshState = state;
378    })
379    .onRefreshing(() => {
380      // simulate refresh the data
381      setTimeout(() => {
382        this.refreshing = false;
383      }, 2000)
384    })
385  }
386}
387```
388
389![refresh_boundary_resilience](figures/refresh_boundary_resilience.gif)
390
391### Example 4
392
393This example demonstrates how to use **promptText** to set the text displayed in the refreshing area.
394
395```ts
396// xxx.ets
397@Entry
398@Component
399struct RefreshExample {
400  @State isRefreshing: boolean = false
401  @State promptText: string = "Refreshing..."
402  @State arr: String[] = ['0', '1', '2', '3', '4','5','6','7','8','9','10']
403
404  build() {
405    Column() {
406      Refresh({ refreshing: $$this.isRefreshing, promptText: this.promptText}) {
407        List() {
408          ForEach(this.arr, (item: string) => {
409            ListItem() {
410              Text('' + item)
411                .width('70%').height(80).fontSize(16).margin(10)
412                .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF)
413            }
414          }, (item: string) => item)
415        }
416        .onScrollIndex((first: number) => {
417          console.info(first.toString())
418        })
419        .width('100%')
420        .height('100%')
421        .alignListItem(ListItemAlign.Center)
422        .scrollBar(BarState.Off)
423      }
424      .backgroundColor(0x89CFF0)
425      .pullToRefresh(true)
426      .refreshOffset(96)
427      .onStateChange((refreshStatus: RefreshStatus) => {
428        console.info('Refresh onStatueChange state is ' + refreshStatus)
429      })
430      .onOffsetChange((value: number) => {
431        console.info('Refresh onOffsetChange offset:' + value)
432      })
433      .onRefreshing(() => {
434        setTimeout(() => {
435          this.isRefreshing = false
436        }, 2000)
437        console.log('onRefreshing test')
438      })
439    }
440  }
441}
442```
443
444![en-us_image_refresh_example4](figures/en-us_image_refresh_example4.gif)
445
446### Example 5
447
448This example implements a **Refresh** with its refreshing area displaying the custom content defined with **refreshingContent**.
449
450```ts
451import { ComponentContent } from '@ohos.arkui.node';
452
453class Params {
454  refreshStatus: RefreshStatus = RefreshStatus.Inactive
455
456  constructor(refreshStatus: RefreshStatus) {
457    this.refreshStatus = refreshStatus;
458  }
459}
460
461@Builder
462function customRefreshingContent(params:Params) {
463  Stack() {
464    Row() {
465      LoadingProgress().height(32)
466      Text("refreshStatus: "+params.refreshStatus).fontSize(16).margin({left:20})
467    }
468    .alignItems(VerticalAlign.Center)
469  }
470  .align(Alignment.Center)
471  .clip(true)
472  .constraintSize({minHeight:32}) // Setting a minimum height constraint ensures that the height of the custom component does not fall below the specified minHeight when the height of the refreshing area changes.
473  .width("100%")
474}
475
476@Entry
477@Component
478struct RefreshExample {
479  @State isRefreshing: boolean = false
480  @State arr: String[] = ['0', '1', '2', '3', '4','5','6','7','8','9','10']
481  @State refreshStatus: RefreshStatus = RefreshStatus.Inactive
482  private contentNode?: ComponentContent<Object> = undefined
483
484  aboutToAppear():void {
485    let uiContext = this.getUIContext();
486    this.contentNode = new ComponentContent(uiContext, wrapBuilder(customRefreshingContent), new Params(this.refreshStatus))
487  }
488
489  build() {
490    Column() {
491      Refresh({ refreshing: $$this.isRefreshing, refreshingContent:this.contentNode}) {
492        List() {
493          ForEach(this.arr, (item: string) => {
494            ListItem() {
495              Text('' + item)
496                .width('70%').height(80).fontSize(16).margin(10)
497                .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF)
498            }
499          }, (item: string) => item)
500        }
501        .onScrollIndex((first: number) => {
502          console.info(first.toString())
503        })
504        .width('100%')
505        .height('100%')
506        .alignListItem(ListItemAlign.Center)
507        .scrollBar(BarState.Off)
508      }
509      .backgroundColor(0x89CFF0)
510      .pullToRefresh(true)
511      .refreshOffset(96)
512      .onStateChange((refreshStatus: RefreshStatus) => {
513        this.refreshStatus = refreshStatus
514        this.contentNode?.update(new Params(this.refreshStatus)) // Update the content of the custom component.
515        console.info('Refresh onStatueChange state is ' + refreshStatus)
516      })
517      .onRefreshing(() => {
518        setTimeout(() => {
519          this.isRefreshing = false
520        }, 2000)
521        console.log('onRefreshing test')
522      })
523    }
524  }
525}
526```
527![en-us_image_refresh_example5](figures/en-us_image_refresh_example5.gif)
528
529### Example 6
530
531This example shows how to use the [pullDownRatio](#pulldownratio12) attribute and the [onOffsetChange](#onoffsetchange12) event to implement the maximum pull-down distance.
532
533```ts
534import { ComponentContent } from '@ohos.arkui.node';
535
536@Builder
537function customRefreshingContent() {
538  Stack() {
539    Row() {
540      LoadingProgress().height(32)
541    }
542    .alignItems(VerticalAlign.Center)
543  }
544  .align(Alignment.Center)
545  .clip(true)
546  .constraintSize({minHeight:32}) // Setting a minimum height constraint ensures that the height of the custom component does not fall below the specified minHeight when the height of the refreshing area changes.
547  .width("100%")
548}
549
550@Entry
551@Component
552struct RefreshExample {
553  @State isRefreshing: boolean = false
554  @State arr: String[] = ['0', '1', '2', '3', '4','5','6','7','8','9','10']
555  @State maxRefreshingHeight: number = 100.0
556  @State ratio: number = 1
557  private contentNode?: ComponentContent<Object> = undefined
558
559  aboutToAppear():void {
560    let uiContext = this.getUIContext();
561    this.contentNode = new ComponentContent(uiContext, wrapBuilder(customRefreshingContent))
562  }
563
564  build() {
565    Column() {
566      Refresh({ refreshing: $$this.isRefreshing, refreshingContent:this.contentNode}) {
567        List() {
568          ForEach(this.arr, (item: string) => {
569            ListItem() {
570              Text('' + item)
571                .width('70%').height(80).fontSize(16).margin(10)
572                .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF)
573            }
574          }, (item: string) => item)
575        }
576        .onScrollIndex((first: number) => {
577          console.info(first.toString())
578        })
579        .width('100%')
580        .height('100%')
581        .alignListItem(ListItemAlign.Center)
582        .scrollBar(BarState.Off)
583      }
584      .backgroundColor(0x89CFF0)
585      .pullDownRatio(this.ratio)
586      .pullToRefresh(true)
587      .refreshOffset(64)
588      .onOffsetChange((offset: number)=>{
589          this.ratio = 1 - Math.pow((offset / this.maxRefreshingHeight), 3) // The closer to the maximum pull-down distance, the smaller the pull-down ratio.
590      })
591      .onStateChange((refreshStatus: RefreshStatus) => {
592        console.info('Refresh onStatueChange state is ' + refreshStatus)
593      })
594      .onRefreshing(() => {
595        setTimeout(() => {
596          this.isRefreshing = false
597        }, 2000)
598        console.log('onRefreshing test')
599      })
600    }
601  }
602}
603```
604
605![en-us_image_refresh_example6](figures/en-us_image_refresh_example6.gif)
606