• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Refresh
2
3 The **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>    | number \| string   | 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 maintain the intended layout, configure a minimum height constraint for a custom component, which ensures that the component's height does not fall below a certain threshold. For details about how to apply this constraint, see [Example 3](#example-3-customizing-the-refreshing-area-content-with-builder).<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>The maximum font scale factor for the custom text, as specified by [maxFontScale](ts-basic-components-text.md#maxfontscale12), is 2.<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 maintain the intended layout, configure a minimum height constraint for a custom component, which ensures that the component's height does not fall below a certain threshold. For details about how to apply this constraint, see [Example 4](#example-4-customizing-the-refreshing-area-content-with-refreshingcontent).<br>**Atomic service API**: This API can be used in atomic services since API version 12.|
43
44>  **Supplementary Notes**
45>  - If neither **builder** nor **refreshingContent** is set, the pull-down displacement effect is implemented by adjusting the [translate](ts-universal-attributes-transformation.md#translate) attribute of the child component. During the pull-down process, the [onAreaChange](ts-universal-component-area-change-event.md#onareachange) event of the child component is not triggered, and any changes made to the [translate](ts-universal-attributes-transformation.md#translate) attribute of the child component do not take effect.
46>  - When **builder** or **refreshingContent** is set, the pull-down displacement effect is implemented by adjusting the position of the child component relative to the **Refresh** component. During the pull-down process, the [onAreaChange](ts-universal-component-area-change-event.md#onareachange) event of the child component can be triggered. However, if the [position](ts-universal-attributes-location.md#position) attribute is set for the child component, the position of the child component relative to the **Refresh** component is fixed, preventing the child component from moving down with the pull gesture.
47
48## Attributes
49
50In addition to the [universal attributes](ts-component-general-attributes.md), the following attributes are supported.
51
52### refreshOffset<sup>12+</sup>
53
54refreshOffset(value: number)
55
56Sets the minimum pull-down offset required to trigger a refresh. If the distance pulled down is less than the value specified by this attribute, releasing the gesture does not trigger a refresh.
57
58**Atomic service API**: This API can be used in atomic services since API version 12.
59
60**System capability**: SystemCapability.ArkUI.ArkUI.Full
61
62**Parameters**
63
64| Name| Type                                       | Mandatory| Description                                                      |
65| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------- |
66| 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.|
67
68### pullToRefresh<sup>12+</sup>
69
70pullToRefresh(value: boolean)
71
72Sets whether to initiate a refresh when the pull-down distance exceeds the value of [refreshOffset](#refreshoffset12).
73
74**Atomic service API**: This API can be used in atomic services since API version 12.
75
76**System capability**: SystemCapability.ArkUI.ArkUI.Full
77
78**Parameters**
79
80| Name| Type                                       | Mandatory| Description                                                      |
81| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------- |
82| 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**|
83
84### pullDownRatio<sup>12+</sup>
85
86pullDownRatio(ratio: [Optional](ts-universal-attributes-custom-property.md#optional12)\<number>)
87
88Sets the pull-down ratio.
89
90**Atomic service API**: This API can be used in atomic services since API version 12.
91
92**System capability**: SystemCapability.ArkUI.ArkUI.Full
93
94**Parameters**
95
96| Name| Type                                       | Mandatory| Description                                                      |
97| ------ | ------------------------------------------- | ---- | ---------------------------------------------------------- |
98| 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**.
99
100## Events
101
102In addition to the [universal events](ts-component-general-events.md), the following events are supported.
103
104### onStateChange
105
106onStateChange(callback: (state: RefreshStatus) => void)
107
108Called when the refresh status changes.
109
110**Atomic service API**: This API can be used in atomic services since API version 11.
111
112**System capability**: SystemCapability.ArkUI.ArkUI.Full
113
114**Parameters**
115
116| Name| Type                                   | Mandatory| Description      |
117| ------ | --------------------------------------- | ---- | ---------- |
118| state  | [RefreshStatus](#refreshstatus) | Yes  | Refresh status.|
119
120### onRefreshing
121
122onRefreshing(callback: () => void)
123
124Called when the component starts refreshing.
125
126**Atomic service API**: This API can be used in atomic services since API version 11.
127
128**System capability**: SystemCapability.ArkUI.ArkUI.Full
129
130### onOffsetChange<sup>12+</sup>
131
132onOffsetChange(callback: Callback\<number>)
133
134Called when the pull-down distance changes.
135
136**Atomic service API**: This API can be used in atomic services since API version 12.
137
138**System capability**: SystemCapability.ArkUI.ArkUI.Full
139
140**Parameters**
141
142| Name| Type                                   | Mandatory| Description      |
143| ------ | --------------------------------------- | ---- | ---------- |
144| callback  | Callback\<number> | Yes  | Pull-down distance.<br>Unit: vp|
145
146
147## RefreshStatus
148
149**Atomic service API**: This API can be used in atomic services since API version 11.
150
151**System capability**: SystemCapability.ArkUI.ArkUI.Full
152
153| Name      | Value      | Description                |
154| -------- | -------- | -------------------- |
155| Inactive | 0 | The component is not pulled down. This is the default value.            |
156| Drag     | 1 | The component is being pulled down, but the pull-down distance is shorter than the minimum length required to trigger the refresh.     |
157| OverDrag | 2 | The component is being pulled down, and the pull-down distance exceeds the minimum length required to trigger the refresh.     |
158| Refresh  | 3 | The pull-down ends, and the component rebounds to the minimum length required to trigger the refresh and enters the refreshing state.|
159| Done     | 4 | The refresh is complete, and the component returns to the initial state (at the top).    |
160
161
162## Example
163
164### Example 1: Using the Default Refreshing Style
165
166This example implements a **Refresh** component with its refreshing area in the default style.
167
168```ts
169// xxx.ets
170@Entry
171@Component
172struct RefreshExample {
173  @State isRefreshing: boolean = false
174  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
175
176  build() {
177    Column() {
178      Refresh({ refreshing: $$this.isRefreshing }) {
179        List() {
180          ForEach(this.arr, (item: string) => {
181            ListItem() {
182              Text('' + item)
183                .width('70%')
184                .height(80)
185                .fontSize(16)
186                .margin(10)
187                .textAlign(TextAlign.Center)
188                .borderRadius(10)
189                .backgroundColor(0xFFFFFF)
190            }
191          }, (item: string) => item)
192        }
193        .onScrollIndex((first: number) => {
194          console.info(first.toString())
195        })
196        .width('100%')
197        .height('100%')
198        .alignListItem(ListItemAlign.Center)
199        .scrollBar(BarState.Off)
200      }
201      .onStateChange((refreshStatus: RefreshStatus) => {
202        console.info('Refresh onStatueChange state is ' + refreshStatus)
203      })
204      .onOffsetChange((value: number) => {
205        console.info('Refresh onOffsetChange offset:' + value)
206      })
207      .onRefreshing(() => {
208        setTimeout(() => {
209          this.isRefreshing = false
210        }, 2000)
211        console.log('onRefreshing test')
212      })
213      .backgroundColor(0x89CFF0)
214      .refreshOffset(64)
215      .pullToRefresh(true)
216    }
217  }
218}
219```
220
221![en-us_image_refresh_default](figures/en-us_image_refresh_default.gif)
222
223### Example 2: Setting the Text Displayed in the Refreshing Area
224
225This example shows how to set the text displayed in the refreshing area using the [promptText](#refreshoptions) parameter.
226
227```ts
228// xxx.ets
229@Entry
230@Component
231struct RefreshExample {
232  @State isRefreshing: boolean = false
233  @State promptText: string = "Refreshing..."
234  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
235
236  build() {
237    Column() {
238      Refresh({ refreshing: $$this.isRefreshing, promptText: this.promptText }) {
239        List() {
240          ForEach(this.arr, (item: string) => {
241            ListItem() {
242              Text('' + item)
243                .width('70%')
244                .height(80)
245                .fontSize(16)
246                .margin(10)
247                .textAlign(TextAlign.Center)
248                .borderRadius(10)
249                .backgroundColor(0xFFFFFF)
250            }
251          }, (item: string) => item)
252        }
253        .onScrollIndex((first: number) => {
254          console.info(first.toString())
255        })
256        .width('100%')
257        .height('100%')
258        .alignListItem(ListItemAlign.Center)
259        .scrollBar(BarState.Off)
260      }
261      .backgroundColor(0x89CFF0)
262      .pullToRefresh(true)
263      .refreshOffset(96)
264      .onStateChange((refreshStatus: RefreshStatus) => {
265        console.info('Refresh onStatueChange state is ' + refreshStatus)
266      })
267      .onOffsetChange((value: number) => {
268        console.info('Refresh onOffsetChange offset:' + value)
269      })
270      .onRefreshing(() => {
271        setTimeout(() => {
272          this.isRefreshing = false
273        }, 2000)
274        console.log('onRefreshing test')
275      })
276    }
277  }
278}
279```
280
281![en-us_image_refresh_prompttext](figures/en-us_image_refresh_prompttext.gif)
282
283### Example 3: Customizing the Refreshing Area Content with builder
284
285This example shows how to customize the content displayed in the refreshing area using the [builder](#refreshoptions) parameter.
286
287```ts
288// xxx.ets
289@Entry
290@Component
291struct RefreshExample {
292  @State isRefreshing: boolean = false
293  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
294
295  @Builder
296  customRefreshComponent() {
297    Stack() {
298      Row() {
299        LoadingProgress().height(32)
300        Text("Refreshing...").fontSize(16).margin({ left: 20 })
301      }
302      .alignItems(VerticalAlign.Center)
303    }
304    .align(Alignment.Center)
305    .clip(true)
306    // Set a minimum height constraint to ensure that the height of the custom component does not fall below the specified minHeight when the height of the refreshing area changes.
307    .constraintSize({ minHeight: 32 })
308    .width("100%")
309  }
310
311  build() {
312    Column() {
313      Refresh({ refreshing: $$this.isRefreshing, builder: this.customRefreshComponent() }) {
314        List() {
315          ForEach(this.arr, (item: string) => {
316            ListItem() {
317              Text('' + item)
318                .width('70%')
319                .height(80)
320                .fontSize(16)
321                .margin(10)
322                .textAlign(TextAlign.Center)
323                .borderRadius(10)
324                .backgroundColor(0xFFFFFF)
325            }
326          }, (item: string) => item)
327        }
328        .onScrollIndex((first: number) => {
329          console.info(first.toString())
330        })
331        .width('100%')
332        .height('100%')
333        .alignListItem(ListItemAlign.Center)
334        .scrollBar(BarState.Off)
335      }
336      .backgroundColor(0x89CFF0)
337      .pullToRefresh(true)
338      .refreshOffset(64)
339      .onStateChange((refreshStatus: RefreshStatus) => {
340        console.info('Refresh onStatueChange state is ' + refreshStatus)
341      })
342      .onRefreshing(() => {
343        setTimeout(() => {
344          this.isRefreshing = false
345        }, 2000)
346        console.log('onRefreshing test')
347      })
348    }
349  }
350}
351```
352
353![en-us_image_refresh_builder](figures/en-us_image_refresh_builder.gif)
354
355### Example 4: Customizing the Refreshing Area Content with refreshingContent
356
357This example shows how to customize the content displayed in the refreshing area using the [refreshingContent](#refreshoptions) parameter.
358
359```ts
360// xxx.ets
361import { ComponentContent } from '@ohos.arkui.node';
362
363class Params {
364  refreshStatus: RefreshStatus = RefreshStatus.Inactive
365
366  constructor(refreshStatus: RefreshStatus) {
367    this.refreshStatus = refreshStatus;
368  }
369}
370
371@Builder
372function customRefreshingContent(params: Params) {
373  Stack() {
374    Row() {
375      LoadingProgress().height(32)
376      Text("refreshStatus: " + params.refreshStatus).fontSize(16).margin({ left: 20 })
377    }
378    .alignItems(VerticalAlign.Center)
379  }
380  .align(Alignment.Center)
381  .clip(true)
382  // Set a minimum height constraint to ensure that the height of the custom component does not fall below the specified minHeight when the height of the refreshing area changes.
383  .constraintSize({ minHeight: 32 })
384  .width("100%")
385}
386
387@Entry
388@Component
389struct RefreshExample {
390  @State isRefreshing: boolean = false
391  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
392  @State refreshStatus: RefreshStatus = RefreshStatus.Inactive
393  private contentNode?: ComponentContent<Object> = undefined
394  private params: Params = new Params(RefreshStatus.Inactive)
395
396  aboutToAppear(): void {
397    let uiContext = this.getUIContext()
398    this.contentNode = new ComponentContent(uiContext, wrapBuilder(customRefreshingContent), this.params)
399  }
400
401  build() {
402    Column() {
403      Refresh({ refreshing: $$this.isRefreshing, refreshingContent: this.contentNode }) {
404        List() {
405          ForEach(this.arr, (item: string) => {
406            ListItem() {
407              Text('' + item)
408                .width('70%')
409                .height(80)
410                .fontSize(16)
411                .margin(10)
412                .textAlign(TextAlign.Center)
413                .borderRadius(10)
414                .backgroundColor(0xFFFFFF)
415            }
416          }, (item: string) => item)
417        }
418        .onScrollIndex((first: number) => {
419          console.info(first.toString())
420        })
421        .width('100%')
422        .height('100%')
423        .alignListItem(ListItemAlign.Center)
424        .scrollBar(BarState.Off)
425      }
426      .backgroundColor(0x89CFF0)
427      .pullToRefresh(true)
428      .refreshOffset(96)
429      .onStateChange((refreshStatus: RefreshStatus) => {
430        this.refreshStatus = refreshStatus
431        this.params.refreshStatus = refreshStatus
432        // Update the content of the custom component.
433        this.contentNode?.update(this.params)
434        console.info('Refresh onStatueChange state is ' + refreshStatus)
435      })
436      .onRefreshing(() => {
437        setTimeout(() => {
438          this.isRefreshing = false
439        }, 2000)
440        console.log('onRefreshing test')
441      })
442    }
443  }
444}
445```
446![en-us_image_refresh_refreshingcontent](figures/en-us_image_refresh_refreshingcontent.gif)
447
448### Example 5: Implementing the Maximum Pull-down Distance
449
450This example shows how to use the [pullDownRatio](#pulldownratio12) attribute and the [onOffsetChange](#onoffsetchange12) event to implement the maximum pull-down distance.
451
452```ts
453// xxx.ets
454import { ComponentContent } from '@ohos.arkui.node';
455
456@Builder
457function customRefreshingContent() {
458  Stack() {
459    Row() {
460      LoadingProgress().height(32)
461    }
462    .alignItems(VerticalAlign.Center)
463  }
464  .align(Alignment.Center)
465  .clip(true)
466  // Set a minimum height constraint to ensure that the height of the custom component does not fall below the specified minHeight when the height of the refreshing area changes.
467  .constraintSize({ minHeight: 32 })
468  .width("100%")
469}
470
471@Entry
472@Component
473struct RefreshExample {
474  @State isRefreshing: boolean = false
475  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
476  @State maxRefreshingHeight: number = 100.0
477  @State ratio: number = 1
478  private contentNode?: ComponentContent<Object> = undefined
479
480  aboutToAppear(): void {
481    let uiContext = this.getUIContext();
482    this.contentNode = new ComponentContent(uiContext, wrapBuilder(customRefreshingContent))
483  }
484
485  build() {
486    Column() {
487      Refresh({ refreshing: $$this.isRefreshing, refreshingContent: this.contentNode }) {
488        List() {
489          ForEach(this.arr, (item: string) => {
490            ListItem() {
491              Text('' + item)
492                .width('70%')
493                .height(80)
494                .fontSize(16)
495                .margin(10)
496                .textAlign(TextAlign.Center)
497                .borderRadius(10)
498                .backgroundColor(0xFFFFFF)
499            }
500          }, (item: string) => item)
501        }
502        .onScrollIndex((first: number) => {
503          console.info(first.toString())
504        })
505        .width('100%')
506        .height('100%')
507        .alignListItem(ListItemAlign.Center)
508        .scrollBar(BarState.Off)
509      }
510      .backgroundColor(0x89CFF0)
511      .pullDownRatio(this.ratio)
512      .pullToRefresh(true)
513      .refreshOffset(64)
514      .onOffsetChange((offset: number) => {
515        // The closer to the maximum distance, the smaller the pull-down ratio.
516        this.ratio = 1 - Math.pow((offset / this.maxRefreshingHeight), 3)
517      })
518      .onStateChange((refreshStatus: RefreshStatus) => {
519        console.info('Refresh onStatueChange state is ' + refreshStatus)
520      })
521      .onRefreshing(() => {
522        setTimeout(() => {
523          this.isRefreshing = false
524        }, 2000)
525        console.log('onRefreshing test')
526      })
527    }
528  }
529}
530```
531
532![en-us_image_refresh_maxrefreshingheight](figures/en-us_image_refresh_maxrefreshingheight.gif)
533
534### Example 6: Implementing Pull-Down-to-Refresh and Pull-Up-to-Load-More
535
536This example demonstrates how to combine the [Refresh](#refresh) component with the [List](ts-container-list.md) component to implement pull-down-to-refresh and pull-up-to-load-more features.
537
538```ts
539// xxx.ets
540@Entry
541@Component
542struct ListRefreshLoad {
543  @State arr: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
544  @State refreshing: boolean = false;
545  @State refreshOffset: number = 0;
546  @State refreshState: RefreshStatus = RefreshStatus.Inactive;
547  @State isLoading: boolean = false;
548
549  @Builder
550  refreshBuilder() {
551    Stack({ alignContent: Alignment.Bottom }) {
552      //The Progress component can be controlled by the refresh state.
553      // The Progress component is present only when the refresh state is pulling or refreshing.
554      if (this.refreshState != RefreshStatus.Inactive && this.refreshState != RefreshStatus.Done) {
555        Progress({ value: this.refreshOffset, total: 64, type: ProgressType.Ring })
556          .width(32).height(32)
557          .style({ status: this.refreshing ? ProgressStatus.LOADING : ProgressStatus.PROGRESSING })
558          .margin(10)
559      }
560    }
561    .clip(true)
562    .height("100%")
563    .width("100%")
564  }
565
566  @Builder
567  footer() {
568    Row() {
569      LoadingProgress().height(32).width(48)
570      Text("Loading")
571    }.width("100%")
572    .height(64)
573    .justifyContent(FlexAlign.Center)
574    // Hide the component when it is not in the loading state.
575    .visibility(this.isLoading ? Visibility.Visible : Visibility.Hidden)
576  }
577
578  build() {
579    Refresh({ refreshing: $$this.refreshing, builder: this.refreshBuilder() }) {
580      List() {
581        ForEach(this.arr, (item: number) => {
582          ListItem() {
583            Text('' + item)
584              .width('100%')
585              .height(80)
586              .fontSize(16)
587              .textAlign(TextAlign.Center)
588              .backgroundColor(0xFFFFFF)
589          }.borderWidth(1)
590        }, (item: string) => item)
591
592        ListItem() {
593          this.footer();
594        }
595      }
596      .onScrollIndex((start: number, end: number) => {
597        // Trigger new data loading when the end of the list is reached.
598        if (end >= this.arr.length - 1) {
599          this.isLoading = true;
600          // Simulate new data loading.
601          setTimeout(() => {
602            for (let i = 0; i < 10; i++) {
603              this.arr.push(this.arr.length);
604              this.isLoading = false;
605            }
606          }, 700)
607        }
608      })
609      .scrollBar(BarState.Off)
610      // Enable the effect used when the scroll boundary is reached.
611      .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true })
612    }
613    .width('100%')
614    .height('100%')
615    .backgroundColor(0xDCDCDC)
616    .onOffsetChange((offset: number) => {
617      this.refreshOffset = offset;
618    })
619    .onStateChange((state: RefreshStatus) => {
620      this.refreshState = state;
621    })
622    .onRefreshing(() => {
623      // Simulate data refreshing.
624      setTimeout(() => {
625        this.refreshing = false;
626      }, 2000)
627    })
628  }
629}
630```
631
632![refresh_boundary_resilience](figures/refresh_boundary_resilience.gif)
633