Refresh

The Refresh component is a container that provides the pull-to-refresh feature.

NOTE

  • This component is supported since API version 8. Updates will be marked with a superscript to indicate their earliest API version.

  • Since API version 12, this component provides linkage with a vertically scrolling Swiper and Web components. When the loop attribute of Swiper is set to true, the Refresh component cannot provide linkage with Swiper.

  • When the Refresh component is nested with a List component whose content size is smaller than the component itself, and there are other components in between, gestures may be intercepted by the intermediate components, preventing the pull-to-refresh effect. In such cases, set the alwaysEnabled parameter to true to allow List to respond to gestures and drive the Refresh component through nested scrolling for the pull-to-refresh effect. For details, see Example 9: Implementing Pull-to-Refresh in the Non-Full-Screen Scenario.

  • The component has been bound with gestures to implement functions such as follow-up scrolling. If you need to add custom gestures, refer to Gesture Blocking Enhancement.

  • Pull-to-refresh cannot be triggered by mouse click-and-drag operations.

Child Components

This component supports only one child component.

Since API version 11, this component's child component moves down with the pull-down gesture.

APIs

Refresh(value: RefreshOptions)

Creates a Refresh container.

Atomic service API: This API can be used in atomic services since API version 11.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
value RefreshOptions Yes Parameters of the Refresh component.

RefreshOptions

Defines the options of the Refresh component.

System capability: SystemCapability.ArkUI.ArkUI.Full

Name Type Read-Only Optional Description
refreshing boolean No No Whether the component is being refreshed. The value true means that the component is being refreshed, and false means the opposite.
Default value: false
This parameter supports two-way binding through $$.
Atomic service API: This API can be used in atomic services since API version 11.
offset(deprecated) number | string No Yes Distance from the pull-down starting point to the top of the component.
Default value: 16.
Unit: vp.
If the type is string, the pixel unit must be explicitly specified, for example, '10px'; if the unit is not specified, for example, '10', the default unit vp is used.
Note: This API is supported since API version 8 and deprecated since API version 11. No substitute is provided.
NOTE
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.
friction(deprecated) number | string No Yes Coefficient of friction, which indicates the <Refresh> component's sensitivity to the pull-down gesture. The value ranges from 0 to 100.
Default value: 62
- 0 indicates that the Refresh component is not responsive to the pull-down gesture.
- 100 indicates that the Refresh component is highly responsive to the pull-down gesture.
- A larger value indicates higher responsiveness of the Refresh component to the pull-down gesture.
NOTE
This API is supported since API version 8 and deprecated since API version 11. You are advised to use pullDownRatio instead.
builder10+ CustomBuilder No Yes Custom content in the refreshing area.
NOTE
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.
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.
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.
Atomic service API: This API can be used in atomic services since API version 11.
promptText12+ ResourceStr No Yes Custom text displayed at the bottom of the refreshing area.
NOTE
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.
When promptText is set and effective, the refreshOffset attribute defaults to 96 vp.
The maximum font scale factor for the custom text, as specified by maxFontScale, is 2.
Atomic service API: This API can be used in atomic services since API version 12.
refreshingContent12+ ComponentContent No Yes Custom content in the refreshing area.
NOTE
If this parameter and the builder parameter are set at the same time, the builder parameter does not take effect.
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.
Atomic service API: This API can be used in atomic services since API version 12.

Supplementary Notes

  • If neither builder nor refreshingContent is set, the pull-down displacement effect is implemented by adjusting the translate attribute of the child component. During the pull-down process, the onAreaChange event of the child component is not triggered, and any changes made to the translate attribute of the child component do not take effect.
  • 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 event of the child component can be triggered. However, if the 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.
  • If the width and height of a custom component set by builder are not specified, its dimensions will adapt to the child components. If the width is specified but the height is not, the height of the component is automatically adjusted according to the pull-down distance. If a custom component set by refreshingContent does not have a specified height, its height will also adapt to the pull-down distance. In such cases, as the pull-down distance increases, the height of the custom component will increase accordingly. When the custom component's height is set to a fixed value or reaches its maximum height limit, further increases in the pull-down distance will cause the spacing between the custom component and the top boundary of the Refresh component to widen.

Attributes

In addition to the universal attributes, the following attributes are supported.

refreshOffset12+

refreshOffset(value: number)

Sets 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.

Atomic service API: This API can be used in atomic services since API version 12.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
value number Yes Pull-down offset, in vp.
Default value: 96 vp when promptText is set and 64 vp when promptText is not set.
If the value specified is 0 or less than 0, the default value is used.

refreshOffset

refreshOffset(value: number | Resource)

Sets the pull-down offset that triggers the refresh. When the pull-down distance is less than the value of this attribute, releasing the pull-down gesture does not trigger the refresh. The resource type is supported.

If this API and promptText are not set, the default offset is 64 vp. If promptText is set, the default offset is 96 vp.

Since: 26.0.0

Model restriction: This API can be used only in the stage model.

Atomic service API: This API can be used in atomic services since API version 26.0.0.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
value number | Resource Yes Pull-down offset.
Unit: vp
Value range: (0, +∞). If the value is 0 or a negative number, the default value will be used.

pullToRefresh12+

pullToRefresh(value: boolean)

Sets whether to initiate a refresh when the pull-down distance exceeds the value of refreshOffset.

Atomic service API: This API can be used in atomic services since API version 12.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
value boolean Yes Whether to initiate a refresh when the pull-down distance exceeds the value of refreshOffset. The value true means to initiate a refresh, and false means the opposite.
Default value: true

pullUpToCancelRefresh23+

pullUpToCancelRefresh(enabled: boolean | undefined)

Sets whether to enable the pull-up-to-cancel gesture for refreshing operations.

Atomic service API: This API can be used in atomic services since API version 23.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
enabled boolean | undefined Yes Whether to enable the pull-up-to-cancel gesture for refreshing operations.
true: Enable the pull-up-to-cancel gesture. false: Disable the pull-up-to-cancel gesture.
undefined: Enable the pull-up-to-cancel gesture.

pullDownRatio12+

pullDownRatio(ratio: Optional<number>)

Sets the pull-down ratio.

Atomic service API: This API can be used in atomic services since API version 12.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
ratio Optional<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.
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.
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.

maxPullDownDistance20+

maxPullDownDistance(distance: Optional<number>)

Sets the maximum pull-down distance.

Atomic service API: This API can be used in atomic services since API version 20.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
distance Optional<number> Yes Maximum pull-down distance. The minimum value for the maximum pull-down distance is 0. Values less than 0 are treated as 0. If this value is less than the refresh offset (refreshOffset), the refresh action will not be triggered when the pull-down gesture is released.
If set to undefined or null, this parameter is considered not set.
Default value: undefined.
Unit: vp

maxPullDownDistance

maxPullDownDistance(distance: number | Resource | undefined)

Sets the maximum pull-down distance. The resource type is supported.

If this API is not set, the maximum pull-down distance is undefined.

Since: 26.0.0

Model restriction: This API can be used only in the stage model.

Atomic service API: This API can be used in atomic services since API version 26.0.0.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
distance number | Resource | undefined Yes Maximum pull-down distance.
Default value: undefined.
Unit: vp
Value range: [0, +∞). If the value is less than 0, 0 is used. If this value is less than the refreshOffset, the refresh action will not be triggered when the pull-down gesture is released.
If this parameter is set to undefined or null, it is considered that this attribute is not set, meaning there is no limit on the maximum pull-down distance.

Events

In addition to the universal events, the following events are supported.

onStateChange

onStateChange(callback: (state: RefreshStatus) => void)

Called when the refresh status changes.

Atomic service API: This API can be used in atomic services since API version 11.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
state RefreshStatus Yes Refresh status.

onRefreshing

onRefreshing(callback: () => void)

Called when the component starts refreshing.

Atomic service API: This API can be used in atomic services since API version 11.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
callback () => void Yes Callback triggered when the component enters the refresh state.

onOffsetChange12+

onOffsetChange(callback: Callback<number>)

Called when the pull-down distance changes.

NOTE

This API can be called within attributeModifier since API version 20.

Atomic service API: This API can be used in atomic services since API version 12.

System capability: SystemCapability.ArkUI.ArkUI.Full

Parameters

Name Type Mandatory Description
callback Callback<number> Yes Callback used to listen for the pull-down distance changes. It is triggered when the pull-down distance changes and returns the current pull-down distance.
Unit: vp

RefreshStatus

Enumerates the states of a refresh operation.

Atomic service API: This API can be used in atomic services since API version 11.

System capability: SystemCapability.ArkUI.ArkUI.Full

Name Value Description
Inactive 0 The component is not pulled down. This is the default value.
Drag 1 The component is being pulled down, but the pull-down distance is shorter than the refresh threshold.
If you release the component, it enters the Inactive state. If you continue to pull down the component and the pull-down distance exceeds the refresh threshold, the component enters the OverDrag state.
OverDrag 2 The component is being pulled down, and the pull-down distance exceeds the refresh threshold.
If you release the component, the component enters the Refresh state. If you swipe upward and the pull-down distance is less than the refresh threshold, the component enters the Drag state.
Refresh 3 The pull-down ends, and the component rebounds to the minimum length required to trigger the refresh and enters the refreshing state.
Done 4 The refresh is complete, and the component returns to the initial state (at the top).

Example

Example 1: Using the Default Refreshing Style

This example implements a Refresh component with its refreshing area in the default style.

// xxx.ets
@Entry
@Component
struct RefreshExample {
  @State isRefreshing: boolean = false;
  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];

  build() {
    Column() {
      Row() {
        Button('Refresh').onClick(() => {
          this.isRefreshing = true;
        })
        Button('Stop').onClick(() => {
          this.isRefreshing = false;
        })
      }

      Refresh({ refreshing: $$this.isRefreshing }) {
        List() {
          ForEach(this.arr, (item: string) => {
            ListItem() {
              Text('' + item)
                .width('70%')
                .height(80)
                .fontSize(16)
                .margin(10)
                .textAlign(TextAlign.Center)
                .borderRadius(10)
                .backgroundColor(0xFFFFFF)
            }
          }, (item: string) => item)
        }
        .onScrollIndex((first: number) => {
          console.info(first.toString());
        })
        .width('100%')
        .height('100%')
        .alignListItem(ListItemAlign.Center)
        .scrollBar(BarState.Off)
      }
      .onStateChange((refreshStatus: RefreshStatus) => {
        console.info('Refresh onStateChange state is ' + refreshStatus);
      })
      .onOffsetChange((value: number) => {
        console.info('Refresh onOffsetChange offset:' + value);
      })
      .onRefreshing(() => {
        setTimeout(() => {
          this.isRefreshing = false;
        }, 2000)
        console.info('onRefreshing test');
      })
      .backgroundColor(0x89CFF0)
      .refreshOffset(64)
      .pullToRefresh(true)
    }
  }
}

en-us_image_refresh_default

Example 2: Setting the Text Displayed in the Refreshing Area

This example shows how to set the text displayed in the refreshing area using the promptText parameter.

// xxx.ets
@Entry
@Component
struct RefreshExample {
  @State isRefreshing: boolean = false;
  @State promptText: string = "Refreshing...";
  @State arr: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];

  build() {
    Column() {
      Refresh({ refreshing: $$this.isRefreshing, promptText: this.promptText }) {
        List() {
          ForEach(this.arr, (item: string) => {
            ListItem() {
              Text(item)
                .width('70%')
                .height(80)
                .fontSize(16)
                .margin(10)
                .textAlign(TextAlign.Center)
                .borderRadius(10)
                .backgroundColor(0xFFFFFF)
            }
          }, (item: string) => item)
        }
        .onScrollIndex((first: number) => {
          console.info(first.toString());
        })
        .width('100%')
        .height('100%')
        .alignListItem(ListItemAlign.Center)
        .scrollBar(BarState.Off)
      }
      .backgroundColor(0x89CFF0)
      .pullToRefresh(true)
      .refreshOffset(96)
      .onStateChange((refreshStatus: RefreshStatus) => {
        console.info('Refresh onStateChange state is ' + refreshStatus);
      })
      .onOffsetChange((value: number) => {
        console.info('Refresh onOffsetChange offset:' + value);
      })
      .onRefreshing(() => {
        setTimeout(() => {
          this.isRefreshing = false;
        }, 2000)
        console.info('onRefreshing test');
      })
    }
  }
}

en-us_image_refresh_prompttext

Example 3: Customizing the Refreshing Area Content with builder

This example shows how to customize the content displayed in the refreshing area using the builder parameter.

// xxx.ets
@Entry
@Component
struct RefreshExample {
  @State isRefreshing: boolean = false;
  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];

  @Builder
  customRefreshComponent() {
    Stack() {
      Row() {
        LoadingProgress().height(32)
        Text("Refreshing...").fontSize(16).margin({ left: 20 })
      }
      .alignItems(VerticalAlign.Center)
    }
    .align(Alignment.Center)
    .clip(true)
    // 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.
    .constraintSize({ minHeight: 32 })
    .width('100%')
  }

  build() {
    Column() {
      Refresh({ refreshing: $$this.isRefreshing, builder: this.customRefreshComponent() }) {
        List() {
          ForEach(this.arr, (item: string) => {
            ListItem() {
              Text('' + item)
                .width('70%')
                .height(80)
                .fontSize(16)
                .margin(10)
                .textAlign(TextAlign.Center)
                .borderRadius(10)
                .backgroundColor(0xFFFFFF)
            }
          }, (item: string) => item)
        }
        .onScrollIndex((first: number) => {
          console.info(first.toString());
        })
        .width('100%')
        .height('100%')
        .alignListItem(ListItemAlign.Center)
        .scrollBar(BarState.Off)
      }
      .backgroundColor(0x89CFF0)
      .pullToRefresh(true)
      .refreshOffset(64)
      .onStateChange((refreshStatus: RefreshStatus) => {
        console.info('Refresh onStateChange state is ' + refreshStatus);
      })
      .onRefreshing(() => {
        setTimeout(() => {
          this.isRefreshing = false;
        }, 2000)
        console.info('onRefreshing test');
      })
    }
  }
}

en-us_image_refresh_builder

Example 4: Customizing the Refreshing Area Content with refreshingContent

This example shows how to customize the content displayed in the refreshing area using the refreshingContent parameter.

// xxx.ets
import { ComponentContent } from '@kit.ArkUI';

class Params {
  refreshStatus: RefreshStatus = RefreshStatus.Inactive;

  constructor(refreshStatus: RefreshStatus) {
    this.refreshStatus = refreshStatus;
  }
}

@Builder
function customRefreshingContent(params: Params) {
  Stack() {
    Row() {
      LoadingProgress().height(32)
      Text("refreshStatus: " + params.refreshStatus).fontSize(16).margin({ left: 20 })
    }
    .alignItems(VerticalAlign.Center)
  }
  .align(Alignment.Center)
  .clip(true)
  // 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.
  .constraintSize({ minHeight: 32 })
  .width('100%')
}

@Entry
@Component
struct RefreshExample {
  @State isRefreshing: boolean = false;
  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
  @State refreshStatus: RefreshStatus = RefreshStatus.Inactive;
  private contentNode?: ComponentContent<Object> = undefined;
  private params: Params = new Params(RefreshStatus.Inactive);

  aboutToAppear(): void {
    let uiContext = this.getUIContext();
    this.contentNode = new ComponentContent(uiContext, wrapBuilder(customRefreshingContent), this.params);
  }

  build() {
    Column() {
      Refresh({ refreshing: $$this.isRefreshing, refreshingContent: this.contentNode }) {
        List() {
          ForEach(this.arr, (item: string) => {
            ListItem() {
              Text('' + item)
                .width('70%')
                .height(80)
                .fontSize(16)
                .margin(10)
                .textAlign(TextAlign.Center)
                .borderRadius(10)
                .backgroundColor(0xFFFFFF)
            }
          }, (item: string) => item)
        }
        .onScrollIndex((first: number) => {
          console.info(first.toString());
        })
        .width('100%')
        .height('100%')
        .alignListItem(ListItemAlign.Center)
        .scrollBar(BarState.Off)
      }
      .backgroundColor(0x89CFF0)
      .pullToRefresh(true)
      .refreshOffset(96)
      .onStateChange((refreshStatus: RefreshStatus) => {
        this.refreshStatus = refreshStatus;
        this.params.refreshStatus = refreshStatus;
        // Update the content of the custom component.
        this.contentNode?.update(this.params);
        console.info('Refresh onStateChange state is ' + refreshStatus);
      })
      .onRefreshing(() => {
        setTimeout(() => {
          this.isRefreshing = false;
        }, 2000)
        console.info('onRefreshing test');
      })
    }
  }
}

en-us_image_refresh_refreshingcontent

Example 5: Implementing the Maximum Pull-down Distance

This example shows how to use the pullDownRatio attribute and the onOffsetChange event to implement the maximum pull-down distance.

// xxx.ets
import { ComponentContent } from '@kit.ArkUI';

@Builder
function customRefreshingContent() {
  Stack() {
    Row() {
      LoadingProgress().height(32)
    }
    .alignItems(VerticalAlign.Center)
  }
  .align(Alignment.Center)
  .clip(true)
  // 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.
  .constraintSize({ minHeight: 32 })
  .width('100%')
}

@Entry
@Component
struct RefreshExample {
  @State isRefreshing: boolean = false;
  @State arr: String[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
  @State maxRefreshingHeight: number = 100.0;
  @State ratio: number = 1;
  private contentNode?: ComponentContent<Object> = undefined;

  aboutToAppear(): void {
    let uiContext = this.getUIContext();
    this.contentNode = new ComponentContent(uiContext, wrapBuilder(customRefreshingContent));
  }

  build() {
    Column() {
      Refresh({ refreshing: $$this.isRefreshing, refreshingContent: this.contentNode }) {
        List() {
          ForEach(this.arr, (item: string) => {
            ListItem() {
              Text('' + item)
                .width('70%')
                .height(80)
                .fontSize(16)
                .margin(10)
                .textAlign(TextAlign.Center)
                .borderRadius(10)
                .backgroundColor(0xFFFFFF)
            }
          }, (item: string) => item)
        }
        .onScrollIndex((first: number) => {
          console.info(first.toString());
        })
        .width('100%')
        .height('100%')
        .alignListItem(ListItemAlign.Center)
        .scrollBar(BarState.Off)
      }
      .backgroundColor(0x89CFF0)
      .pullDownRatio(this.ratio)
      .pullToRefresh(true)
      .refreshOffset(64)
      .onOffsetChange((offset: number) => {
        // The closer to the maximum distance, the smaller the pull-down ratio.
        this.ratio = 1 - Math.pow((offset / this.maxRefreshingHeight), 3);
      })
      .onStateChange((refreshStatus: RefreshStatus) => {
        console.info('Refresh onStateChange state is ' + refreshStatus);
      })
      .onRefreshing(() => {
        setTimeout(() => {
          this.isRefreshing = false;
        }, 2000)
        console.info('onRefreshing test');
      })
    }
  }
}

en-us_image_refresh_maxrefreshingheight

Example 6: Implementing Pull-Down-to-Refresh and Pull-Up-to-Load-More

This example demonstrates how to combine the Refresh component with the List component to implement pull-down-to-refresh and pull-up-to-load-more features.

// xxx.ets
@Entry
@Component
struct ListRefreshLoad {
  @State arr: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  @State refreshing: boolean = false;
  @State refreshOffset: number = 0;
  @State refreshState: RefreshStatus = RefreshStatus.Inactive;
  @State isLoading: boolean = false;

  @Builder
  refreshBuilder() {
    Stack({ alignContent: Alignment.Bottom }) {
      // The Progress component is displayed based on the refresh state.
      // It is only shown when the refresh state is Drag or Refresh.
      if (this.refreshState != RefreshStatus.Inactive && this.refreshState != RefreshStatus.Done) {
        Progress({ value: this.refreshOffset, total: 64, type: ProgressType.Ring })
          .width(32).height(32)
          .style({ status: this.refreshing ? ProgressStatus.LOADING : ProgressStatus.PROGRESSING })
          .margin(10)
      }
    }
    .clip(true)
    .height('100%')
    .width('100%')
  }

  @Builder
  footer() {
    Row() {
      LoadingProgress().height(32).width(48)
      Text("Loading")
    }.width('100%')
    .height(64)
    .justifyContent(FlexAlign.Center)
    // The component is hidden when not in the loading state.
    .visibility(this.isLoading ? Visibility.Visible : Visibility.Hidden)
  }

  build() {
    Refresh({ refreshing: $$this.refreshing, builder: this.refreshBuilder() }) {
      List() {
        ForEach(this.arr, (item: number) => {
          ListItem() {
            Text('' + item)
              .width('100%')
              .height(80)
              .fontSize(16)
              .textAlign(TextAlign.Center)
              .backgroundColor(0xFFFFFF)
          }.borderWidth(1)
        }, (item: string) => item)

        ListItem() {
          this.footer();
        }
      }
      .onScrollIndex((start: number, end: number) => {
        // Trigger new data loading when the end of the list is reached.
        if (end >= this.arr.length - 1) {
          this.isLoading = true;
          // Simulate new data loading.
          setTimeout(() => {
            for (let i = 0; i < 10; i++) {
              this.arr.push(this.arr.length);
            }
            this.isLoading = false;
          }, 700)
        }
      })
      .scrollBar(BarState.Off)
      // Enable the effect used when the scroll boundary is reached.
      .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: true })
    }
    .width('100%')
    .height('100%')
    .backgroundColor(0xDCDCDC)
    .onOffsetChange((offset: number) => {
      this.refreshOffset = offset;
    })
    .onStateChange((state: RefreshStatus) => {
      this.refreshState = state;
    })
    .onRefreshing(() => {
      // Simulate data refreshing.
      setTimeout(() => {
        this.refreshing = false;
      }, 2000)
    })
  }
}

refresh_boundary_resilience

Example 7: Setting the Maximum Pull-Down Distance

This example demonstrates how to set the maximum pull-down distance using the maxPullDownDistance attribute, supported since API version 20.

// xxx.ets
@Entry
@Component
struct RefreshExample {
  @State isRefreshing: boolean = false
  @State arr: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']

  build() {
    Column() {
      Refresh({ refreshing: $$this.isRefreshing }) {
        List() {
          ForEach(this.arr, (item: string) => {
            ListItem() {
              Text('' + item)
                .width('70%')
                .height(80)
                .fontSize(16)
                .margin(10)
                .textAlign(TextAlign.Center)
                .borderRadius(10)
                .backgroundColor(0xFFFFFF)
            }
          }, (item: string) => item)
        }
        .onScrollIndex((first: number) => {
          console.info(first.toString())
        })
        .width('100%')
        .height('100%')
        .alignListItem(ListItemAlign.Center)
        .scrollBar(BarState.Off)
      }
      .maxPullDownDistance(150)
      .onStateChange((refreshStatus: RefreshStatus) => {
        console.info('Refresh onStateChange state is ' + refreshStatus)
      })
      .onOffsetChange((value: number) => {
        console.info('Refresh onOffsetChange offset:' + value)
      })
      .onRefreshing(() => {
        setTimeout(() => {
          this.isRefreshing = false
        }, 2000)
        console.info('onRefreshing test')
      })
      .backgroundColor(0x89CFF0)
      .refreshOffset(64)
      .pullToRefresh(true)
    }
  }
}

refresh_maxpulldowndistance

Example 8: Disabling Pull-to-Refresh

This example demonstrates how to disable pull-to-refresh using the pullDownRatio attribute.

// xxx.ets
@Entry
@Component
struct RefreshExample {
  @State isRefreshing: boolean = false;
  @State ratio: number | undefined = undefined;
  @State arr: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];

  build() {
    Column() {
      Row() {
        Button('Disable Pull-to-Refresh').onClick(() => {
          this.ratio = 0
        })
        Button('Enable Pull-to-Refresh').onClick(() => {
          this.ratio = undefined
        })
      }
      Refresh({ refreshing: $$this.isRefreshing }) {
          List() {
            ForEach(this.arr, (item: string) => {
              ListItem() {
                Text('' + item)
                  .width('70%')
                  .height(80)
                  .fontSize(16)
                  .margin(10)
                  .textAlign(TextAlign.Center)
                  .borderRadius(10)
                  .backgroundColor(0xFFFFFF)
              }
            }, (item: string) => item)
          }
          .onScrollIndex((first: number) => {
            console.info(first.toString());
          })
          .width('100%')
          .height('100%')
          .alignListItem(ListItemAlign.Center)
          .scrollBar(BarState.Off)
      }
      .backgroundColor(0x89CFF0)
      .refreshOffset(64)
      .pullToRefresh(true)
      .pullDownRatio(this.ratio)
      .onStateChange((refreshStatus: RefreshStatus) => {
        console.info('Refresh onStateChange state is ' + refreshStatus);
      })
      .onOffsetChange((value: number) => {
        console.info('Refresh onOffsetChange offset:' + value);
      })
      .onRefreshing(() => {
        setTimeout(() => {
          this.isRefreshing = false;
        }, 2000)
        console.info('onRefreshing test');
      })
    }
  }
}

refresh_pulldownratio

Example 9: Implementing Pull-to-Refresh in the Non-Full-Screen Scenario

When calling edgeEffect, set alwaysEnabled of the options parameter to true to implement the pull-to-refresh effect of the Refresh component in the non-full-screen scenario.

// xxx.ets
@Entry
@Component
struct RefreshExample {
  @State isRefreshing: boolean = false;
  @State alwaysEnabled: boolean = false;

  build() {
    Column() {
      Refresh({ refreshing: $$this.isRefreshing }) {
        Column() {
          List() {
            ListItem() {
              Text('alwaysEnabled:' + this.alwaysEnabled)
                .width('70%')
                .height(80)
                .fontSize(16)
                .margin(10)
                .textAlign(TextAlign.Center)
                .borderRadius(10)
                .backgroundColor(0xFFFFFF)
                .onClick(() => {
                  this.alwaysEnabled = !this.alwaysEnabled;
                })
            }
          }
          .width('100%')
          .height('100%')
          .alignListItem(ListItemAlign.Center)
          .scrollBar(BarState.Auto)
          // If the content size of the List component is smaller than the component size and alwaysEnabled is set to false, the List component does not respond to gestures. In this case, the gestures are responded by the Column component, and the pull-to-refresh effect is not generated.
          // If alwaysEnabled is set to true, the List component responds to gestures and drives the Refresh component to trigger pull-down refresh through nested scrolling.
          .edgeEffect(EdgeEffect.Spring, { alwaysEnabled: this.alwaysEnabled })
        }
        .gesture(
          PanGesture({ direction: PanDirection.Vertical })
        )
      }
      .onStateChange((refreshStatus: RefreshStatus) => {
        console.info('Refresh onStateChange state is ' + refreshStatus);
      })
      .onOffsetChange((value: number) => {
        console.info('Refresh onOffsetChange offset:' + value);
      })
      .onRefreshing(() => {
        setTimeout(() => {
          this.isRefreshing = false;
        }, 2000)
      })
      .backgroundColor(0x89CFF0)
      .refreshOffset(64)
      .pullToRefresh(true)
    }
  }
}

refresh_list_edgeEffect

Example 10: Disabling Pull-Up-to-Cancel

This example shows how to disable the pull-up-to-cancel gesture for refreshing operations using the pullUpToCancelRefresh API.

The pullUpToCancelRefresh API is supported since API version 23.

// xxx.ets
import { ComponentContent } from '@kit.ArkUI';

class Params {
  refreshStatus: RefreshStatus = RefreshStatus.Inactive;

  constructor(refreshStatus: RefreshStatus) {
    this.refreshStatus = refreshStatus;
  }
}

@Builder
function customRefreshingContent(params: Params) {
  Stack() {
    Row() {
      LoadingProgress().height(32)
      Text('refreshStatus: ' + params.refreshStatus).fontSize(16).margin({ left: 20 })
    }
    .alignItems(VerticalAlign.Center)
  }
  .align(Alignment.Center)
  .clip(true)
  .constraintSize({ minHeight: 32 })
  .width('100%')
}

@Entry
@Component
struct RefreshExample {
  @State isRefreshing: boolean = false;
  @State arr: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']; // The data type is changed to string[].
  @State refreshStatus: RefreshStatus = RefreshStatus.Inactive;
  private contentNode?: ComponentContent<Object> = undefined;
  private params: Params = new Params(RefreshStatus.Inactive);

  aboutToAppear(): void {
    let uiContext = this.getUIContext();
    this.contentNode = new ComponentContent(uiContext, wrapBuilder(customRefreshingContent), this.params);
  }

  build() {
    Column() {
      Refresh({ refreshing: $$this.isRefreshing, refreshingContent: this.contentNode }) {
        List() {
          ForEach(this.arr, (item: string) => {
            ListItem() {
              Text('' + item)
                .width('70%')
                .height(80)
                .fontSize(16)
                .margin(10)
                .textAlign(TextAlign.Center)
                .borderRadius(10)
                .backgroundColor(0xFFFFFF)
            }
          }, (item: string) => item)
        }
        .onScrollIndex((first: number) => {
          console.info(first.toString());
        })
        .width('100%')
        .height('100%')
        .alignListItem(ListItemAlign.Center)
        .scrollBar(BarState.Off)
      }
      .backgroundColor(0x89CFF0)
      .pullToRefresh(true)
      .pullUpToCancelRefresh(false)
      .refreshOffset(96)
      .onStateChange((refreshStatus: RefreshStatus) => {
        this.refreshStatus = refreshStatus;
        this.params.refreshStatus = refreshStatus;
        this.contentNode?.update(this.params);
        console.info('Refresh onStateChange state is ' + refreshStatus);
      })
      .onRefreshing(() => {
        setTimeout(() => {
          const newArr: string[] = [];
          const lastNum = parseInt(this.arr[this.arr.length - 1]);
          for (let i = 0; i < 11; i++) {
            newArr.push((lastNum + 1 + i).toString());
          }
          this.arr = newArr;

          this.isRefreshing = false;
        }, 6000)
        console.info('onRefreshing test');
      })
    }
  }
}

refresh_pullUpToCancelRefresh