Custom Pull To Refresh using Jetpack Compose
Inspired by ThreadsApp Pull To Refresh
End Result
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
- When a user pulls the screen, the loader is in
PULL
state. - The loader icon color animates from Black to Light Gray.
- The size of the loader icon increases till a threshold is reached.
- A part of the icon moves along the path with a fixed width.
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.
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 toDEFAULT
state.RELEASE
— If the user releases the screen below the point where the pull started but within the refresh threshold limit, the loader is set toRELEASE
state when the user releases.REFRESHING
— If the user releases the screen beyond the refresh threshold limit, the loader is set toREFRESHING
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
- The
icon
we want the loader to be. isRefreshing
state to denote if the layout is in the refreshing state or not.- In the
onRefresh
callback, we can perform the data fetching operation. We setisRefreshing
totrue
as we start to fetch the data and reset it tofalse
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
- To update the loader to
PULL
andRELEASE
states using the pull progress. This is done in the firstLaunchedEffect
in the below code. - To maintain
pullProgress
andrefreshProgress
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/