How to Use ListView Builder in Flutter

In this article I am going to show you how to use ListView and ListTiles in your app development projects. We are also going to use the HTTP package and JSON Decode for our data manipulation. This time we will be using a real data example from our request to display it on the screen. Let’s get started!

Here is my To-Do List app widget source code. We will expand this project into a real To-do App.

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

  @override
  State<TodoList> createState() => _TodoListState();
}

class _TodoListState extends State<TodoList> {
  void getData() async {
    Response response = await get(
      Uri.parse('https://jsonplaceholder.typicode.com/todos/1'),
    );

    Map data = jsonDecode(response.body);

    print(data);
  }

  @override
  Widget build(BuildContext context) {
    getData();
    return Container(
      child: Center(
          child: Text(
        'Todo List Here',
        style: TextStyle(
          color: Colors.amber,
          fontSize: 20,
          fontWeight: FontWeight.bold,
        ),
      )),
    );
  }
}

As you can see, the To-do List app doesn’t even have any lists at all to have a list first we will utilize the JSON response that we have and assign it on a List data type rather than a Map data type for our ListView Builder later.

JSON to List

First of all, all the data processing is happening inside of this getData() function. That is why we need a variable that is accessible on the outside of this function but limited to the widget. That is why I am going to create an initial List variable just outside of the function.

After we have access outside of the function, we shouldn’t forget to assign the JSON response data to the List variable that we initialized outside.

Also, a List must receive a List of data rather than a specific row, which is why I am going to edit my JSON url source to get a List of data rather than a single specific row.

Refer to the code below.

class _TodoListState extends State<TodoList> {
  List<dynamic> data = <dynamic>[];
  void getData() async {
    Response response = await get(
      Uri.parse('https://jsonplaceholder.typicode.com/todos'),
    );

    data = jsonDecode(response.body);

    //print(data); // will print a list of datasets
  }

  @override
  Widget build(BuildContext context) {
    getData(); // called the function
    return Container(
      child: Center(
          child: Text(
        'Todo List Here',
        style: TextStyle(
          color: Colors.amber,
          fontSize: 20,
          fontWeight: FontWeight.bold,
        ),
      )),
    );
  }
}

ListView Builder Usage

Now that we have all of the data prepared, let’s start writing the UI with the ListView Builder that we have been waiting for!

First, I am going to remove the “Todo List Here” widget and replace it with ListView Builder. Now the ListView Builder needs to keep count of the items we want to insert into it, which is why I am going to feed the argument with the length of our data variable.

The ListView Builder has another required argument, which is the item builder argument. It wants a function that returns something, which in my case is going to be a card widget that it can return, and this card widget has a ListTile widget as a child.

Refer to the code below.

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

  @override
  State<TodoList> createState() => _TodoListState();
}

class _TodoListState extends State<TodoList> {
  List<dynamic> data = <dynamic>[];
  void getData() async {
    Response response = await get(
      Uri.parse('https://jsonplaceholder.typicode.com/todos'),
    );

    data = jsonDecode(response.body);

    //print(data); // will print a list of datasets
  }

  @override
  Widget build(BuildContext context) {
    getData();
    return Container(
      child: Center(
        child: ListView.builder(
            itemCount: data.length, // assign length of the data here
            itemBuilder: (context, index) { // returns a Card widget
              return Card(
                child: ListView(),
              );
            }),
      ),
    );
  }
}

ListTile Usage

Now at this point, the code above will cause an error if you run it because it doesn’t have any content yet. So, continuing with our project, I am going to need the title argument first. In this argument, it needs a widget, so I am going to write a Text widget with a value of the current index of our data’s title.

Nicely done. Now I am going to add another argument, the “leading” argument, and assign a Text widget again with the value of the current index’s data id, which is an int datatype, but we could just easily parse it to a string by simply calling the “toString()” function. In my case, I also added a few styles.

Finally, the “trailing” argument I am going to assign a cool cupertino switch that indicates if the to-do task is completed or not. I am not going to go in-depth with the cupertino switch widget right now, but right now it just needs a boolean value that will indicate if the switch is on or off. Our data’s “completed” field was a perfect fit for this argument as it returns a boolean data type.

Refer to the source code below.

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

  @override
  State<TodoList> createState() => _TodoListState();
}

class _TodoListState extends State<TodoList> {
  List<dynamic> data = <dynamic>[];
  void getData() async {
    Response response = await get(
      Uri.parse('https://jsonplaceholder.typicode.com/todos'),
    );

    data = jsonDecode(response.body);

    //print(data); // will print a list of datasets
  }

  @override
  Widget build(BuildContext context) {
    getData();
    return Container(
      child: Center(
        child: ListView.builder(
            itemCount: data.length, // assign length of the data here
            itemBuilder: (context, index) { // returns a Card widget
              return Card(
                child: ListTile(
                leading: Text(
                  data[index]['id'].toString(), // assigned data's id here
                  style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
                ),
                title: Text(
                  data[index]['title'], // assigned data's title here
                ),
                trailing: CupertinoSwitch(
                  value: data[index]['completed'], // assigned data's completed status here
                  onChanged: (bool status) {},
                  activeColor: Colors.amber,
                ),
              ),
              );
            }),
      ),
    );
  }
}

To get a full in-depth reference to all of the used widgets here, refer to the sources below.

https://api.flutter.dev/flutter/material/ListTile-class.html
https://stackoverflow.com/questions/54031546/how-to-create-an-empty-list-in-dart
https://api.flutter.dev/flutter/cupertino/CupertinoSwitch-class.html
https://docs.flutter.dev/cookbook/lists/mixed-list

Conclusion

We covered a lot of topics in this article, including JSON decode, List, ListView Builder, Card, ListTile, and CupertinoSwitch, and utilized the data we have by writing its UI for display. Thank you again for reading. Keep following me on this flutter journey!

Leave a Comment

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