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 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 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 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 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 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 606