Create a List in Flutter using FutureBuilder

In this post I explain how to load external data and displaying it in a list (ListView) in Flutter. Dart provides us some great ways for handling asynchronous operations which is what Futures is all about, and Flutter provides us with great widgets, such as the FutureBuilder which is what I am going to use in this example.

What exactly is a Future?

A Future can be completed in two ways: with a value (“the future succeeds”) or with an error (“the future fails”). Users can install callbacks for each case.

api.flutter.dev

Pretty straight forward. We either have a value or error for futures when we complete them. You can think futures of being similar to Promise in JavaScript if you are familiar with that.

Now let’s open up our favourite Source-code editor and make a new Flutter project and start coding a FutureBuilder.

Create a Stateful Widget

Type the shortkey stful and enter to create a Stateful Widget and call it something, I’m going for ShowFutureThings here.

class ShowFutureThings extends StatefulWidget {
  @override
  _ShowFutureThingsState createState() => _ShowFutureThingsState();
}

class _ShowFutureThingsState extends State<ShowFutureThings> {
  @override
  Widget build(BuildContext context) {
    return Container(
      
    );
  }
}

Create a FutureBuilder

Once done that let’s get into the builder. First though, let’s see read some of the FutureBuilder documentation. Notice the description in bold.

Widget that builds itself based on the latest snapshot of interaction with a Future.

The future must have been obtained earlier, e.g. during State.initState, State.didUpdateConfig, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder. If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder’s parent is rebuilt, the asynchronous task will be restarted.

api.flutter.dev

Okay, we got a nice base and nice understanding of what we are going to do.
Let’s add the FutureBuilder, so instead of returning a Container in the code of the Widget build() we will replace this and return a FutureBuilder.

return FutureBuilder<List<String>>(
        builder: (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
      return Container();
    });

This is what is the required properties (builder) are for the builder. We also need to return something for the builder, so I am going with an empty Container. Now before we add more code here, the interesting part is that what we specify here FutureBuilder<List<String>>. This means that we are going to have a List of strings for the data we are retrieving with for the Future property. The builder also have AsyncSnapshot of the same class, <List<String>> this is because we will fetch async data and have a AsyncSnapshot holding a object of the List String class.

We have yet to define any function for the FutureBuilder to get data from anywhere so let us prepare a function to do just that.

Adding a Future function for getting http data

To make things more fun let us use a http request to make things more real. Now we already know that we want a function to return a List of String based on the previous FutureBuilder. So let us get started prepare the http request dependency by adding this to our pubspec.yaml file.

http: ^0.12.2

Then add the package import in your main.dart file or wherever you’re doing this.

import 'package:http/http.dart' as http;

We are now ready to write our function. Take note that it is returning a Future<List<String>>. It is going to get json formatted data from https://jsonplaceholder.typicode.com/todos. When we got the response from the URL, which we wait for with the help of the await keyword. When it is done, we are checking if result is 200 (OK) and parse the json with jsonDecode, and then loop through it and adding each title of the todo object to our list. When this is all done we return the list which now should contain a bunch of todo titles.

Future<List<String>> getHttpData() async {
  final List<String> mylist = [];
  final response = await http.get('https://jsonplaceholder.typicode.com/todos');
  if (response.statusCode == 200) {
    var json = jsonDecode(response.body);
    for (var i = 0; i < json.length; i++) {
      mylist.add(json[i]['title']);
    }
  } else {
    throw Exception('Failed to get http');
  }
  return mylist;
}

Display the http data

Okay great, we got a future function which returns a list of strings! Let’s add it to our FutureBuilder. The Future property is what will call for our new getHttpData function.
set future: getHttpData() and while we are at it, add the initialData property to [] which will initialize the List so we avoid strange problems.

Since we have data to work with now, you can access the List of Strings like this snapshot.data. A List of elements means we can type [0] to get our first title.

return FutureBuilder<List<String>>(
      future: getHttpData(),
      initialData: [],
      builder: (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
        return Text(snapshot.data[0]);
      },

Let’s add the list. There is a few ways to do it but I am going to use the ListView.Builder which I believe is pretty straight forward. So instead of returning a Text as we do above, we will return a ListView.builder(). We want to define:

  • itemCount
  • itemBuilder

So let’s add this to your FutureBuilder’s builder. The highlighted code here is what builds our list based on the snapshot data!

return FutureBuilder<List<String>>(
      future: getHttpData(),
      initialData: [],
      builder: (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
        return ListView.builder(
          itemCount: snapshot.data.length,
          itemBuilder: (context, index) {
            return new ListTile(title: new Text(snapshot.data[index]));
          },
        );
      },
    );

My final main.dart file looks like this.

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to FileIdea',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to FileIdea'),
        ),
        // Display our list widget
        body: Center(child: ShowFutureThings()),
      ),
    );
  }
}

class ShowFutureThings extends StatefulWidget {
  @override
  _ShowFutureThingsState createState() => _ShowFutureThingsState();
}

Future<List<String>> getHttpData() async {
  final List<String> mylist = [];
  final response = await http.get('https://jsonplaceholder.typicode.com/todos');
  if (response.statusCode == 200) {
    var json = jsonDecode(response.body);
    for (var i = 0; i < json.length; i++) {
      mylist.add(json[i]['title']);
    }
  } else {
    throw Exception('Failed to get http');
  }
  return mylist;
}

class _ShowFutureThingsState extends State<ShowFutureThings> {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<String>>(
      future: getHttpData(),
      initialData: [],
      builder: (BuildContext context, AsyncSnapshot<List<String>> snapshot) {
        return ListView.builder(
          itemCount: snapshot.data.length,
          itemBuilder: (context, index) {
            return new ListTile(title: new Text(snapshot.data[index]));
          },
        );
      },
    );
  }
}

Leave a comment

Your email address will not be published. Required fields are marked *