How to create your own pull to refresh / custom refresh indicator widget in Flutter.

Kamil Klyta
Flutter Community
5 min readMay 4, 2020

--

TLDR; package, online example.

A few months ago, in a project that I was working on, there was a need to create a custom refresh indicator to replace the default indicator provided with a flutter material library. Back in those days, I found it really hard to find some simple solutions or packages that will provide basic functionality on which I will be able to implement it. The one and only thing that I was able to find was liquid_pull_to_refresh package which (back then) was just a modification of the default material design refresh indicator widget, and I was like…

To avoid another: copy, paste, refactor, and forgot why this file has over a thousand lines of code — a type of work that almost always ends in some production f… problems. I have created custom_refresh_indicator_package (available on pub.dev).

The main target of this package was to provide all data needed for pull-to-refresh functionality implementation in an as simple way as possible.

Below you can see an example indicator created on top of this package — Och wait! and there is also an ONLINE EXAMPLE?!

Example of the custom refresh indicator widget.
Example pull to refresh widget build on top of the custom_refresh_indicator package.

Okey… now we know that someone, sometimes for some reason, might like to implement its own unique pull-to-refresh indicator.

But how?

Let’s imagine that we have a simple screen that looks like this:

and which is written like that:

To add pull-to-refresh functionality, we must wrap the list widget with the CustomRefreshIndicator widget that is provided by custom_refresh_indicator package:

Let’s take a look at the changes.

Import

First of all, we have added an import statement to the custom_refresh_indicator package, because we would like to build our indicator on top of it.

onRefresh

In the same way, as we are using RefreshIndicator widget we assigned a function that will return Future to the onRefresh argument. In the above example, we returned Future.delayed() as we do not want to take any action, instead we just want to wait for a few seconds to simulate asynchronous function behavior, but basically, this is the place when you are doing your API call or other asynchronous functions that will take effect after the pull to refresh gesture is made on your list.

child

We assigned our scrollable widget to the child argument — still the same way as we would like to do this with theRefreshIndicator widget.

builder

The builder argument is new in comparison to RefreshIndicator. This function will be used to build our pull-to-refresh widget. It takes three arguments in turn: the context of type BuildContext, child of type Widget and controller of type IndicatorController.

  1. BuildContext — It’s a quite common argument, so I think that I can skip it 😉.
  2. The child argument is basically the scrollable widget we assigned to the child widget argument (quite similar behavior to that known from the AnimatedBuilder widget). So if we, for some reason, do not want to render our list, we can skip the use of it — but it almost always makes no sense 😄.
  3. The third and most important argument is the controller of type IndicatorController. This argument contains all information about the scroll state that we would like to use to build our custom indicator. The controller extends theChangeNotifier class, and thanks to that, we can listen to its changes.

I̶m̶p̶o̶r̶t̶a̶n̶t̶!̶ ̶
̶B̶u̶i̶l̶d̶e̶r̶ ̶f̶u̶n̶c̶t̶i̶o̶n̶ ̶w̶i̶l̶l̶ ̶n̶o̶t̶ ̶b̶e̶ ̶c̶a̶l̶l̶e̶d̶ ̶e̶v̶e̶r̶y̶ ̶c̶o̶n̶t̶r̶o̶l̶l̶e̶r̶ ̶d̶a̶t̶a̶ ̶c̶h̶a̶n̶g̶e̶ ̶s̶o̶ ̶t̶o̶ ̶a̶n̶i̶m̶a̶t̶e̶ ̶y̶o̶u̶r̶ ̶i̶n̶d̶i̶c̶a̶t̶o̶r̶ ̶w̶i̶d̶g̶e̶t̶ ̶y̶o̶u̶ ̶s̶h̶o̶u̶l̶d̶ ̶c̶o̶n̶s̶i̶d̶e̶r̶ ̶u̶s̶i̶n̶g̶ ̶A̶n̶i̶m̶a̶t̶e̶d̶B̶u̶i̶l̶d̶e̶r̶ ̶w̶i̶d̶g̶e̶t̶.̶
Hi, it’s me from the future 👋😅 As the custom_refresh_indicator 2.0.0 was released, this is no longer true. Since 2.0.0, the builder function rebuilds automatically. However! if your widget is complex and needs some optimization, it might still be a good choice to restore the old behavior and use AnimatedBuilder widget to decide which part of the widget tree to rebuild. To do so, simply set the autoRebuild argument to false — that was easy, right? 😄

So… the above example will provide a pull-to-refresh behavior — the user can drag the list to call the onRefresh function — BUT — without visual effect because the builder function always returns our list without any changes. Therefore, when talking about user experience…

But if for some reason we find that our users should suffer a little less, just a little 🤏. We would like to add some effect, so let’s do this!

As you can see, we have added a Stack widget with some Transforms in it, and that's the effect:

To better understand how controller data changes, let’s look at the example below.

Presentation of controller data changes

In Summary

As you can see, to implement your custom refresh indicator widget, you do not need to copy dozens of lines of code that you do not even understand from other libraries to your project codebase. Instead, add a custom refresh indicator package to your pubspec.yaml file and implement a few lines of builder function. That’s it, isn’t it simple 😏?

Do not wait! Create your own custom refresh indicator!

--

--