Custom Pull To Refresh using Jetpack Compose

Abhimanyu
Make Apps Simple
Published in
4 min readJul 8, 2023

Inspired by ThreadsApp Pull To Refresh

End Result

UI

Animation Breakdown

The loader animation is separated into 5 states — DEFAULT, PULL, RELEASE , REFRESHING and REFRESH_COMPLETED .

1. Default

The rest state when there is no user interaction or animation as a result of user interaction.

2. Pull

  1. When a user pulls the screen, the loader is in PULL state.
  2. The loader icon color animates from Black to Light Gray.
  3. The size of the loader icon increases till a threshold is reached.
  4. A part of the icon moves along the path with a fixed width.
Moving part along the path

Once a user starts to pull the screen and the loader is in PULL state, it can change into possibly 3 other states depending on how much the user pulls before releasing the screen.

rememberPullRefreshState has the option to provide a refresh threshold, which indicates how much the user has to pull to refresh the data.

  1. DEFAULT — If the user releases the screen at the same position where the pull started or at any point above it, the loader is set to DEFAULT state.
  2. RELEASE — If the user releases the screen below the point where the pull started but within the refresh threshold limit, the loader is set to RELEASE state when the user releases.
  3. REFRESHING — If the user releases the screen beyond the refresh threshold limit, the loader is set to REFRESHING state when the user releases.

3. Release

The release state is the opposite of the pull state. The animating fixed width part of the icon moves in the reverse direction and the size animates back to the default state. Once the animating part is hidden, the color animates back to Black, and the loader is set to DEFAULT state.

4. Refreshing

Refreshing is the core part of the loader. Once the loader is set to REFRESHING state, the data fetching starts, and the animating fixed width part animate along the path back and forth till the data is being fetched. The size also animates while the data is being fetched.
Once the data is fetched, the loader is set to REFRESH_COMPLETED state.

5. Refresh Completed

In REFRESH_COMPLETED state the animating part covers the loader in the reverse direction from the end to the start and the size is animated back to the default state size. Once the animation is done, the state is set back to DEFAULT completing the data refresh and loader animations.

We have covered what to do for each state. Now we can dive into the code.

I have created a reusable MyPullToRefreshLayout Composable with some limited/required customization options. It can be expanded to support more customization by adding more parameters as required.

MyPullToRefreshLayout usage

The minimal required parameters to use it

  1. The icon we want the loader to be.
  2. isRefreshing state to denote if the layout is in the refreshing state or not.
  3. In the onRefresh callback, we can perform the data fetching operation. We set isRefreshing to true as we start to fetch the data and reset it to false once the data fetching is completed.

Icon

The icon Path will be used for the animations. The sizeFactor will be used for animating size.

Disclosure: This is generated by ChatGPT. We can use the “Path Data” of SVG files to generate this.

MyPullToRefreshLayout

The layout has the following responsibilities

  1. To update the loader to PULL and RELEASE states using the pull progress. This is done in the first LaunchedEffect in the below code.
  2. To maintain pullProgress and refreshProgress for animations.

The float util method used

I have used a util method to interpolate change between two float values over a given duration linearly.

MyPullToRefreshIndicator

This handles the rendering logic of the loader based on the state and other parameters.

pathMeasure.getSegment is used to get the path between two given progress values.

Everything else is conditionals based on the loader state.

Please comment with your valuable feedback.
It would help me to improvise.

Kindly 👏👏👏 if this was useful.

Please follow if you want more content related to Android and Jetpack Compose.

LinkedIn: https://www.linkedin.com/in/abhimanyu-n/

Credits

Icons from https://www.svgrepo.com/

--

--