Jetpack Compose Swipe To Refresh

A short guide on how to implement swipe-to-refresh functionality along with placeholders when loading items

Igor Stevanovic
Better Programming

--

The Jetpack Compose logo used in this image is the official logo created by Google

Today, a lot of apps have data that need to be refreshed at some point. You can refresh data after some time or use sockets to have always up-to-date data, but what if you want functionality that allows users to start refreshing data?

That can be done with a button, but in some cases, a better UX would be swipe-to-refresh. Today, we are gonna implement that using Accompanist library.

Swipe-to-Refresh

First, let’s add a dependency:

implementation "com.google.accompanist:accompanist-swiperefresh:0.25.1"

Note: Check if there is a newer version of this dependency.

Next is to create a simpleViewModel which will hold our data and logic around refreshing. Here, items will contain random images and numbers. This is what it looks like:

isRefreshing is a boolean that we will use in our swipeRefreshState which we will explain later. items is just a list of 20 items with random images and numbers.

Now, let’s create our screen:

We are collecting our states and creating swipeRefreshState with the value of isRefreshing. This state we are passing to SwipeRefresh but also we can access its properties isRefreshing and isSwipeInProgress if needed. Inside SwipeRefresh we have a title, current time, and list of items. Every row of items is just showing an image and number.

SwipeRefresh has three mandatory parameters:

  • state: SwipeRefreshState — A state object that can be hoisted to control and observe changes for SwipeRefresh
  • onRefresh: () -> Unit — Lambda which is invoked when a swipe-to-refresh gesture is completed
  • content: @Composable () -> Unit — The content containing a scroll composable

Some of the interesting optional parameters are:

  • swipeEnabled: Boolean — Whether the layout should react to swipe gestures or not
  • refreshTriggerDistance: Dp — The minimum swipe distance which would trigger a refresh
  • indicatorAlignment: Alignment — The alignment of the indicator. Defaults to Alignment.TopCenter
  • indicatorPadding: PaddingValues — Content padding for the indicator, to inset the indicator in if required
  • indicator: @Composable (state: SwipeRefreshState, refreshTrigger: Dp) — the indicator that represents the current state. By default, this will use a SwipeRefreshIndicator

For indicator parameter, you can create your own composable, but the library provides us with SwipeRefreshIndicator which is a pretty nice composable that we can use.

It requires two parameters:

  • state: SwipeRefreshState — The SwipeRefreshState passed into the SwipeRefresh indicator block
  • refreshTriggerDistance: Dp — The minimum swipe distance which would trigger a refresh

Some optional parameters are:

  • fade: Boolean — Whether the arrow should fade in/out as it is scrolled in, defaults to true
  • scale: Boolean — Whether the indicator should scale up/down as it is scrolled in, defaults to false
  • arrowEnabled: Boolean — Whether an arrow should be drawn on the indicator, defaults to true
  • backgroundColor: Color — The color of the indicator background surface

There are more parameters, but no need to go through all of them. Make sure to check it out in the official documentation, if you want to learn more.

That is all for SwipeRefresh, now let’s implement placeholders, which is another nice library from the Accompanist.

Placeholder

Usually, the loading of the items is shown by some sort of loading spinner. Another way of showing that items are loading is with placeholders.

Accompanist created a library that provides us with a modifier for displaying a placeholder. There are actually two placeholder libraries. One is Foundation and other is Material. It is recommended that we use Material but feel free to use whatever you need. There isn’t much difference, APIs are mostly equivalent. In this blog, we are using Material. So, let’s import it with this command:

implementation "com.google.accompanist:accompanist-placeholder-material:0.25.1"

Note: Check if there is a newer version of this dependency.

Let’s quickly edit MainViewModel before proceeding with the MainScreen. Add init and isLoading StateFlow. Also, initialize items with 20 default RowItems.

Our ViewModel looks like this now:

Next is to collect isLoading in our screen, then to use it for our placeholder. We are adding a new parameter to the Item composable, childModifier: Modifier.

As you can see, the library provided placeholder a modifier. The required parameter is visible: Boolean which determines if the placeholder should be shown or the content. If visible is true, instead of the content there would be a placeholder filling the size of the composable on which it is applied.

Optional parameters are:

  • color: Color — the color used to draw the placeholder UI. If Color.Unspecified is provided, the placeholder will use PlaceholderDefaults.color
  • shape: Shape — the desired shape of the placeholder. If null is provided the placeholder will use the small shape set in MaterialTheme.shapes
  • highlight: PlaceholderHighlight — the optional highlight animation. There are two pre-created animations for placeholder, fade and shimmer
  • placeholderFadeTransitionSpec: @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<Float> — The transition spec to use when fading the placeholder on/off the screen. The boolean parameter defined for the transition is visible
  • contentFadeTransitionSpec: @Composable Transition.Segment<Boolean>.() -> FiniteAnimationSpec<Float> — The transition spec to use when fading the content on/off the screen. The boolean parameter defined for the transition is visible

Our MainScreen now looks like this:

That’s all. I hope you liked it.

You can find all of the source code in my GitHub repo.

Want to Connect?GitHub
LinkedIn
Twitter

Portfolio website

--

--