1. Code
  2. Coding Fundamentals

Google Flutter From Scratch: Grids, Lists, and Data Sources

Scroll to top
This post is part of a series called Google Flutter From Scratch.
Google Flutter From Scratch: Building Apps With Widgets

Almost every non-trivial mobile app today is likely to have lists in its layouts. That's because using a scrollable list is often the most straightforward way to display a large number of similar items on a small screen. 

The Flutter framework offers several widgets you can use to efficiently, and with minimal code, create and display such lists. In this tutorial, I'll show you how to use them with both local and remote data sources.

1. Displaying a Non-Scrollable List

If you need to display a small number of similar items, and you are sure that the user's screen will be able to accommodate all of them at the same time, using the Column widget is the most efficient thing to do.

To create a list in your Flutter app, you can use the List class that the Dart programming language offers. After creating the list, you can use its add() method to add any number of items to it. The following code shows you how to create a list containing three RaisedButton widgets:

1
// Create list

2
List<RaisedButton> myItems = new List();
3
4
// Add three button widgets to it

5
myItems.add(new RaisedButton(
6
      child: new Text("Twitter"),
7
  onPressed: (){}
8
));
9
myItems.add(new RaisedButton(
10
    child: new Text("Facebook"),
11
    onPressed: (){}
12
));
13
myItems.add(new RaisedButton(
14
    child: new Text("Reddit"),
15
    onPressed: (){}
16
));

Note that each item in the list has an empty onPressed event handler associated with it because, without that, the item would be disabled.

Now that the list is ready, you can directly assign it to the children property of the Column widget to be displayed. Usually, however, you'll also want to specify where the list items should be placed on the screen. Because the Column widget is a flex widget, you can control the positions of the items along its main axis and cross axis using the mainAxisAlignment and crossAxisAlignment properties. By default, for the Column widget, the main axis is the vertical axis, and the cross axis is the horizontal axis.

The following code shows you how to stretch the three buttons across the horizontal axis and place them in the center of the screen vertically:

1
Column column = new Column(
2
    mainAxisAlignment: MainAxisAlignment.center,
3
    crossAxisAlignment: CrossAxisAlignment.stretch,
4
    children: myItems
5
);

Here's what the column will look like now:

App displaying column with three buttonsApp displaying column with three buttonsApp displaying column with three buttons

It is important to note that you will encounter a runtime error if a Column widget is unable to accommodate all its children. For example, if you had over a dozen RaisedButton widgets in your list instead of three, you would see an error message that looks like this:

App displaying overflow errorApp displaying overflow errorApp displaying overflow error

2. Displaying a Simple Scrollable List

For slightly larger lists, lists whose contents are likely to overflow the screen, you must consider using a ListView widget because it supports scrolling.

You may have noticed that the code we wrote to create the list in the previous step was both lengthy and repetitive. Creating a larger list using the same approach can be very tedious. An easy alternative approach is to use two lists instead: one containing the data, and one containing the widgets.

Here's how you can use the [] operator to quickly create a data list, which, for now, only contains several strings:

1
List<String> data = <String>["Twitter", "Reddit", "YouTube", "Facebook",
2
          "Vimeo", "GitHub", "GitLab", "BitBucket", "LinkedIn", "Medium",
3
          "Tumblr", "Instagram", "Pinterest"];

To convert the above list of strings into a list of RaisedButton widgets, you can use the map() and toList() methods. With the map() method, you can you use each string to generate a new RaisedButton widget. And with the toList() method, you can convert the Iterable object returned by the map() method into an actual List object. The following code shows you how:

1
List<RaisedButton> myWidgets = data.map((item) {
2
  return new RaisedButton(
3
    child: new Text(item),
4
    onPressed: () async {
5
      String url = "https://${item}.com";
6
      if(await canLaunch(url))
7
        await launch(url);
8
    }
9
  );
10
}).toList();

For the sake of completeness, the above code also shows you how to create an onPressed event handler that uses the canLaunch() and launch() methods offered by the url_launcher package to open the website the user selected in the default browser.

Once your list is ready, you can pass it to the children property of the ListView widget to display it.

1
ListView myList = new ListView(
2
  children: myWidgets
3
);

At this point, if you run the app, you should be able to scroll through the list and press any button to launch the associated website.

App displaying a scrollable listApp displaying a scrollable listApp displaying a scrollable list

3. Creating a Grid

The ListView widget allows you to place only one item on its cross axis. The item will, by default, be stretched to occupy all the space available on that axis. If you want more flexibility, you should consider using a GridView widget instead, which lets you specify how many items you want on the cross axis.

The following code uses the GridView.count() constructor to create a GridView widget that displays two items per row:

1
GridView myGrid = GridView.count(
2
    crossAxisCount: 2,
3
    children: myWidgets
4
);

Here's what the grid looks like:

App displaying a grid of buttonsApp displaying a grid of buttonsApp displaying a grid of buttons

4. Displaying Large Lists

For data lists that contain more than a few dozen items, you should avoid generating widget lists manually, the way you did in an earlier step. Why? Because creating a widget is an expensive operation, and large lists of widgets can consume a lot of memory.

Instead, you should use the IndexedWidgetBuilder function, which lets you generate widgets only when the user needs to see them. With it, you can lazily generate widgets as the user scrolls through your ListView widget.

It is quite unlikely that you're going to have large amounts of data defined right inside your apps. Usually, you would be fetching such data from a remote server. Therefore, to give you a realistic example, let me now show you how to fetch 100 questions from Stack Overflow using the Stack Exchange API and display them on demand.

Start by creating a subclass of the StatefulWidget class, which will act as a container for your ListView widget and override its createState() method.

1
class VeryLargeListHolder extends StatefulWidget {
2
  @override
3
  State<StatefulWidget> createState() {
4
    return new MyState();
5
  }
6
}

The MyState class mentioned in the above code doesn't exist yet, so create it and override its build() method.

1
class MyState extends State<VeryLargeListHolder> {
2
  @override
3
  Widget build(BuildContext context) {
4
    // TODO

5
  }
6
}

Next, add a List object as one of the member variables of the class. You'll be using it to store the questions you downloaded from Stack Overflow. Additionally, add the API's endpoint as another variable.

1
List questions;
2
3
String endpoint = "https://api.stackexchange.com/2.2/questions?" +
4
  "pagesize=100&order=desc&sort=activity&site=stackoverflow";

Unless you want your user to press a button to download the questions, I suggest you download them automatically when the widget is initializing. Accordingly, override the initState() method and make a call to a new asynchronous method called loadData().

1
@override
2
void initState() {
3
    super.initState();
4
    loadData();
5
}
6
7
void loadData() async {
8
    // More code here

9
}

Inside the loadData() method, you can use the get() function of Dart's http package to download the questions. The API endpoint returns a JSON document, which you can parse by using the json.decode() function available in Dart's convert package. The following code shows you how:

1
String rawData = (await http.get(endpoint)).body;
2
Map jsonData = json.decode(rawData);

Once the JSON document has been converted into a Map object, you can use the value associated with its items key to initialize the questions variable. The variable, however, is a part of the widget's state. Therefore, you must make sure you update it inside the setState() method only. Here's how:

1
setState(() {
2
    questions = jsonData["items"];
3
});

At this point you can create a new ListView widget using the ListView.builder() constructor, which expects an IndexedWidgetBuilder function and an item count as its arguments. For now, the item count is nothing but the size of the questions list. Accordingly, add the following code inside the build() method of the MyState class:

1
ListView myList = ListView.builder(
2
    itemCount: questions == null ? 0 : questions.length,
3
    itemBuilder: (BuildContext context, int index) {
4
        // More code here

5
    }
6
);

Inside the builder function, all you need to do is create a small widget tree to display various details about each question you downloaded. Flutter's material package offers a very handy widget called ListTile, which allows you to quickly create such a tree while adhering to the Material Design guidelines.

The following code shows you how to display the question's title and author, using the title and subtitle properties of the ListTile widget:

1
return new ListTile(
2
    title: Text(questions[index]["title"]),
3
    subtitle: Text("Asked by ${questions[index]["owner"]["display_name"]}")
4
);

Lastly, create a new Scaffold widget, assign the ListView widget to its body property, and return it from the build() method so it can be used with a MaterialApp widget. Optionally, you can add an AppBar widget to the Scaffold widget.

1
return new Scaffold(
2
    appBar: new AppBar(
3
        title: new Text("LargeListDemo")
4
    ),
5
    body: myList
6
);

Here's what the app will look like after it has downloaded the questions:

App displaying Stack Overflow dataApp displaying Stack Overflow dataApp displaying Stack Overflow data

Conclusion

You now know how to work with lists in a Flutter application. In this tutorial, you learned not only how to create lists and grids that support large data sources, but also how to make each item inside them interactive. To learn more about lists in Flutter, you can refer to the official documentation.

Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.