How to use FutureBuilder Widget to create widgets based on the latest snapshot?

How to use FutureBuilder Widget to create widgets based on the latest snapshot?

#Flutter: FutureBuilder and ListView

So you want to build a list of items in your application using some API efficiently
By Efficiently, I mean

When dealing with APIs, Your app or program can handle the latency or errors for you.

Dealing with situations like latency in fetching JSON string or No Internet Connection in user's device, may require you to write more lines of codes to handle such types of exceptions.

Like for the latency issue, one can think of using Future delay to make your application wait for some time interval until your application is fetching the JSON string or whatsoever from the API.

In case of a network connection issue, one can think of first making your application check that if Network connection can be established or not?

If you find this interesting, then one can easily accomplish this by some lines of dart code.
Umm, will share this in my future articles.


So coming back to point, how can we use handle these kinds of exceptions?
Here comes the Future Builder Widget into play.

We will be building an application that looks like this,

Without having any issue, ListView

When having some issue, issue

When having a null response, Null response

Let us say we are dealing with i/o operation, So what it suppose to do in that time interval can be defined by using FutureBuilder or StreamBuilder.

Let's see how Future Builder works and How to use it to display our list using the ListView Widget.

In our previous article, I have articulated the concept behind parsing a JSON string using Random Person API.
Here, I will be using random User API for this Article.

You can also have a look on it

Starting with the main.dart file, I have the following code,

import 'package:flutter/material.dart';
import 'package:jsonstuff/screens/homepage.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      themeMode: ThemeMode.system,
      theme: ThemeData(
        // primarySwatch: Colors.purple,
        brightness: Brightness.dark,
      ),
      home: const MyHomePage(),
    );
  }
}

My homepage.dart file,

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:jsonstuff/models/person_model.dart';
import 'package:jsonstuff/services/network_calls.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final NetworkService personData = NetworkService();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Json Testing"),
        backgroundColor: const Color(0xFF6002ee),
      ),
      body: SafeArea(
        child: FutureBuilder(
          future: personData.fetchPerson(),
          builder: (BuildContext context,
              AsyncSnapshot<List<PersonModel>> snapshot) {
            if (snapshot.hasData) {
              return ListView.builder(
                itemCount: snapshot.data!.length,  // 
                itemBuilder: (BuildContext context, int index) {
                  var currentPerson = snapshot.data![index];
                  return InkWell(
                    onTap: () {
                      // Whatever you want to accomplish.
                    },
                    child: Padding(
                      padding: const EdgeInsets.all(4.0),
                      child: ListTile(
                        leading: CircleAvatar(
                          backgroundImage:
                              NetworkImage(currentPerson.picture.thumbnail),
                        ),
                        title: Text(currentPerson.name.toString()),
                        subtitle: Text(currentPerson.email),
                      ),
                    ),
                  );
                },
              );
            }
            if (snapshot.hasError) {
              return AlertDialog(
                title: const Text("Something Gone wrong :("),
                actions: [
                  TextButton(
                    onPressed: () {
                      SystemNavigator.pop();
                    },
                    child: const Text("Exit"),
                  )
                ],
              );
            } else {
              return const LinearProgressIndicator();
            }
          },
        ),
      ),
    );
  }
}

Now we have a rough idea what is the use case of FutureBuilder,
How it works?

In FutureBuilder we have a required argument builder, that we need to implement with a Function having a Context and Async Snapshot of your defined class.

Do this means that our code will work without having the Future argument?

Yes, It will work but now we have to provide the data using initstate. Which we will discuss in our future articles.

What Future argument is doing in the code? What's its role?

It is performing the asynchronous computation to which this builder is connected.

What is asynchronous?

Async Simply means occurring at the same time. And Await means to wait. i.e. to wait for a certain operation to be completed.

Just for a quick recap :)

After reading all this you may have a rough idea that Future is creating the snapshots and Builder argument is passing it as an argument in some function.

As we saw here,

builder: ((BuildContext context, AsyncSnapshot<List<PersonModel>> snapshot) {
        // Body
}

So now here comes an important part, If you did the flutter installation recently, that means you are probably using Flutter V2.x, which has been migrated to null safety.

What is null safety?

As we are using this API, there could be cases when the server which serves this API would be down due to some technicality.

Or maybe the link provided by the API is broken, or you may have some typo in the link.

So for handling these kinds of issues, Dart Provides a mechanism known as null safety. But you can disable it if you are annoyed by those errors related to null safety in your code.

So we just need to define some cases like, If Something goes wrong, show this.
Show loading indicator until the list has been created for your user.

I think we are done with FutureBuilder, Let's move on to ListView Builder.

 if (snapshot.hasData) {
              return ListView.builder(
                itemCount: snapshot.data!.length,  // 
                itemBuilder: (BuildContext context, int index) {
                  var currentPerson = snapshot.data![index];
                  return InkWell(
                    onTap: () {
                      // Whatever you want to accomplish.
                    },
                    child: Padding(
                      padding: const EdgeInsets.all(4.0),
                      child: ListTile(
                        leading: CircleAvatar(
                          backgroundImage:
                              NetworkImage(currentPerson.picture.thumbnail),
                        ),
                        title: Text(currentPerson.name.toString()),
                        subtitle: Text(currentPerson.email),
                      ),
                    ),
                  );
                },
              );
            }

Now I feel that rest of the part will be very easy for you to understand.
But then also, leme give you a rough idea about what it is? and how it is working?

This part of our code is saying that,

if the snapshot has data and we haven't encountered any error, then return a listView.Builder.

ListView have an argument itemcount. It stores the information, how many lists are supposed to be created.
Therefore we passed snapshot.data!.length.

What's that exclamation mark doing?

It is just doing a null check. If it is null or not.

One can specify question mark ? too, but then you need to specify, what your program is supposed to do while having the value as null.

I don't wanna do it right now. It's your homework. try it, It's awesome and good practice.

Now I feel the rest of the code is self-explanatory, as we have discussed all the definitions above.

But I think you may have noticed that I have used SystemNavigator.pop(); inside snapshot.haserror case.
What is SystemNavigator.pop()?

Well, it is equivalent, You tapping the back button.


I think we are clear. If you have any queries or suggestions, then please don't hesitate to contact me through telegram or drop a comment below :)

I'll be happy to hear from you.

Thank you,
Stay home and keep learning
Cya :)

Did you find this article valuable?

Support Gulshan Yadav by becoming a sponsor. Any amount is appreciated!