Pretty much all of us working with network calls have experienced some delay in response. The delay could be because of slow WiFi, or slow server, or some black-box reason we don’t need to worry about right now. It won’t be a great user experience to freeze the UI or show a white screen while waiting for the API response. We want to show a progress indicator to our users indicating some network call is happening right now. Now what we usually do is have a bool variable such as _load and do something like the following.

_load ? return new Container(...) : return CircularProgressIndicator()

Or even conquer this problem with FutureBuilder, but we still have a lot of if-else statements.

FutureBuilder<ItemsList>(

future: getRepositories(),

builder: (context, snapshot) {



if (snapshot.hasData) {

return new Container(...),

}

if (snapshot.hasError) {...} return new Center(child: new CircularProgressIndicator());

},

)

AsyncLoader solves this problem for us.

But first, what are we building?

For this tutorial, we would be creating a part of a GitHub App that uses the Github Search Repositories API to return a list of repos for a given keyword.

Here is the blank project that you can develop on. We have multiple todos lined up in repo_screen.dart that we are going to conquer one by one.

Setup AsyncLoader

AsyncLoader is a pretty cool plugin by Ryan Edge. Had I known this earlier, I would have had a cleaner codebase.

To set up a new project with AsyncLoader, first we must add it in our pubspec.yaml file. Something like this.

dev_dependencies:

flutter_test:

sdk: flutter

async_loader: ^0.1.1

It’s already there in the sample project mentioned above.

For the next part, we move on to repo_screen.dart and declare a GlobalKey for AsyncLoaderState so that we can access its object from anywhere.

final GlobalKey<AsyncLoaderState> asyncLoaderState =

new GlobalKey<AsyncLoaderState>();

Now under the build function, we create a AsyncLoader and it takes a few parameters there

key — the GlobalKey you just created

— the GlobalKey you just created initState — what do you want to load? In this case, we would call our getRepositories() method that would return the response from the API call.

— what do you want to load? In this case, we would call our method that would return the response from the API call. renderLoad — what do you want to show when you are loading data? Here I want to show a CircularProgressIndicator while it’s fetching the response.

— what do you want to show when you are loading data? Here I want to show a while it’s fetching the response. renderSuccess — what do you want to show after the loading is completed? Here I want to show the list of cards having Repo name and description.

— what do you want to show after the loading is completed? Here I want to show the list of cards having Repo name and description. renderError — what do you want to show if the loading was abruptly stopped due to some reason. In this case, network calls can be affected by internet connection. So I would like to show a message that says “Sorry, no internet connection” and ask them to retry again.

var _asyncLoader = new AsyncLoader(

key: asyncLoaderState,

initState: () async => await getRepositories(),

renderLoad: () => Center(child: new CircularProgressIndicator()),

renderError: ([error]) => getNoConnectionWidget(),

renderSuccess: ({data}) => getListView(data),

);

Notes:

getRepositories() does the network call and returns a Future<ItemsList> and can be found in api_services.dart .

does the network call and returns a and can be found in . renderSuccess receives the ItemsList object as data and sends it to the getListView method. The method returns a ListView.

Widget getListView(ItemsList items){

return new ListView.builder(

itemCount: items.items.length,

itemBuilder: (context, index) =>

new RepoListItem(items.items[index])

);

} //already declared in the blank project

renderError displays the getNoConnectionWidget to show a view with an image and a text that says “No Internet Connection” along with a retry button that retries the loading process all over again.

The FlatButton has an onPressed that calls the following line of code

asyncLoaderState.currentState.reloadState()

This is responsible for reloading the loading process which is what Retry button is supposed to do.

Final code looks like this

Adding a Refresh Indicator

You might want to add a pull to refresh gesture for your list, so that you can keep refreshing the content whenever you want to. Like how its done in Gmail app where you can pull to refresh and update the content. RefreshIndicator is what we need for this purpose.

return Scaffold(

appBar: new AppBar(title: buildAppBarTitle('Github Repositories')),

body: Scrollbar(

child: RefreshIndicator(

onRefresh: () => asyncLoaderState.currentState.reloadState(),

child: _asyncLoader

),

),

);

However this would throw an error when you try to pull to refresh.

The RefreshIndicator onRefresh callback must return a Future.

The onRefresh method will require a Future , so we create a Future<Null> _handleRefresh() method and add the reloadState() function call in this method body .

Future<Null> _handleRefresh() async {

asyncLoaderState.currentState.reloadState();

return null;

}

Try the final code with RefreshIndicator

Now you can do all of this using FutureBuilder too, but that still doesn’t solve the problem of having too many if-else statements. Sure FutureBuilder is useful in a lot of use-cases, and I still use it. You can treat this plugin as an alternative to FutureBuilder which makes the code a little bit neater.

I’m just exploring different Flutter plugins that can make your development work easier. Hope this helps!

Read my other articles on Flutter