How to iterate through list of map and display Text widget in flutter listview?

Viewed 5631

I'm very new to flutter I'm trying to display listview through for loop from the list of contents but I was able to print one list row which is one iteration as return fires only once. How to iterate through the complete list and display all listview elements? below is the list of map content

 List<Map<String, String>> users = [
    {"name": "Staff 1", "status": "Online"},
    {"name": "Staff 2", "status": "Online"},
    {"name": "Staff 3", "status": "Offline"},
    {"name": "Vehicle 1", "status": "Available"},
    {"name": "Vehicle 2", "status": "Unavailable"},
    {"name": "Team 1", "status": "Active"},
    {"name": "Team 2", "status": "Not Active"},
  ];

below is the code for the listview widget which I'm trying to display

  Widget ListViewBuilder() {
        for (var i = 0; i < users.length; i++) {
          print(i);
          return Expanded(
            child: ListView(
              children: <Widget>[
                GestureDetector(
                  onTap: () {
                    Navigator.pushNamed(
                      context,
                      '/chatscreen',
                    );
                  },
                  child: ListTile(
                      leading: CircleAvatar(
                          radius: 20,
                          backgroundImage: AssetImage("assets/profileavatar.png")),
                      title: Text(users[i]['name']),
                      subtitle: Text("User -ID"),
                      trailing: Row(
                          mainAxisAlignment:
                              MainAxisAlignment.spaceBetween, // added line
                          mainAxisSize: MainAxisSize.min,
                          children: <Widget>[
                            Text(
                              users[i]['status'],
                              style: TextStyle(
                                color: Colors.green,
                                fontSize: 20,
                          ),
                 ),                          
              ],
            ),
          );
        }
      }

this will display only first element in the list, how can I achieve to display all listview elements present in list users? any advice will be helpful

3 Answers

The problem here is - you are returning the result in the first loop. This is why you only get first element in the list.

You need to iterate through your map within the ListView widget.

List<Map<String, String>> users = [
    {"name": "Staff 1", "status": "Online"},
    {"name": "Staff 2", "status": "Online"},
    {"name": "Staff 3", "status": "Offline"},
    {"name": "Vehicle 1", "status": "Available"},
    {"name": "Vehicle 2", "status": "Unavailable"},
    {"name": "Team 1", "status": "Active"},
    {"name": "Team 2", "status": "Not Active"},
  ];

Widget ListViewBuilder(BuildContext context) {
      return Expanded(
        child: ListView(
          children: <Widget>[
            ...users.map( (u) => 
            <Widget> [GestureDetector(
              onTap: () {
                Navigator.pushNamed(
                  context,
                  '/chatscreen',
                );
              },
              child: ListTile(
                  leading: CircleAvatar(
                      radius: 20,
                      backgroundImage: AssetImage("assets/profileavatar.png")),
                  title: Text(u['name']),
                  subtitle: Text("User -ID"),
                  trailing: Row(
                      mainAxisAlignment:
                          MainAxisAlignment.spaceBetween, // added line
                      mainAxisSize: MainAxisSize.min,
                      children: <Widget>[
                        Text(
                          u['status'],
                          style: TextStyle(
                            color: Colors.green,
                            fontSize: 20,
                          ),
                        ),
                        SizedBox(
                          width: 20,
                        ),
                        InkWell(
                          onTap: () {
                            Navigator.pushNamed(
                              context,
                              '/viewlocation',
                            );
                          },
                          child: Icon(
                            Icons.add_location,
                            color: Colors.green,
                            size: 40,
                          ),
                        ),
                        SizedBox(
                          width: 20,
                        ),
                        Container(
                          decoration: BoxDecoration(
                              color: Colors.green,
                              border: Border.all(
                                color: Colors.green,
                              ),
                              borderRadius:
                                  BorderRadius.all(Radius.circular(20))),
                          child: InkWell(
                            onTap: () async {
                              const number =
                                  '08592119XXXX'; //set the number here
                              bool res =
                                  await FlutterPhoneDirectCaller.callNumber(
                                      number);
                            },
                            child: Icon(
                              Icons.call,
                              color: Colors.white,
                              size: 40,
                            ),
                          ),
                        )
                      ])),
            ),
            Divider(
              color: Colors.black,
              endIndent: 10,
              indent: 10,
            )
            ]).expand((element) => element).toList()
            ,
          ],
        ),
      );
 
  }

Few things are happening here:

  • users.map() will run supplied function on each element of your list.

  • since you want to create mini-list of two elements - GestureDetector and Divider, the function will return List of Widgets

  • result of your map funciton will be [[GestureDetector, Divider], [GestureDetector, Divider],...]. This is why we run expand to flatten it (https://api.dart.dev/stable/2.10.5/dart-core/Iterable/expand.html)

  • finally, spread operator (...) is used (...users.map( (u) =>) to unload elements into encompassing list. so this:

child: ListView(
          children: <Widget>[
      [GestureDetector, Divider, GestureDetector, Divider]]

becomes

child: ListView(
          children: <Widget>[
      GestureDetector, Divider, GestureDetector, Divider]

EDIT: I spaced on that divider you had in there and I agree with @Sam Chan suggesting ListView.separated instead of my earlier recommendation of ListView.builder.

For starters, it'll be way cleaner to extract your ListTile to its own widget and pass in a couple strings. Otherwise it gets messy really fast. Here's an example of that.

class CustomListTile extends StatelessWidget {
  final String name, status;

  const CustomListTile({Key key, this.name, this.status}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        Navigator.pushNamed(
          context,
          '/chatscreen',
        );
      },
      child: ListTile(
        leading: CircleAvatar(
            radius: 20,
            backgroundImage: AssetImage("assets/profileavatar.png")),
        title: Text(name),
        subtitle: Text("User -ID"),
        trailing: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween, // added line
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Text(
              status,
              style: TextStyle(
                color: Colors.green,
                fontSize: 20,
              ),
            ),
            SizedBox(
              width: 20,
            ),
            InkWell(
              onTap: () {
                Navigator.pushNamed(
                  context,
                  '/viewlocation',
                );
              },
              child: Icon(
                Icons.add_location,
                color: Colors.green,
                size: 40,
              ),
            ),
            SizedBox(
              width: 20,
            ),
            Container(
              decoration: BoxDecoration(
                  color: Colors.green,
                  border: Border.all(
                    color: Colors.green,
                  ),
                  borderRadius: BorderRadius.all(Radius.circular(20))),
              child: InkWell(
                onTap: () async {
                  const number = '08592119XXXX'; //set the number here
                  // bool res = await FlutterPhoneDirectCaller.callNumber(number);
                },
                child: Icon(
                  Icons.call,
                  color: Colors.white,
                  size: 40,
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

Here's an example of a full page displaying your list of maps.

class DemoPage extends StatelessWidget {
  DemoPage({
    Key key,
  }) : super(key: key);

  final List<Map<String, String>> users = [
    {"name": "Staff 1", "status": "Online"},
    {"name": "Staff 2", "status": "Online"},
    {"name": "Staff 3", "status": "Offline"},
    {"name": "Vehicle 1", "status": "Available"},
    {"name": "Vehicle 2", "status": "Unavailable"},
    {"name": "Team 1", "status": "Active"},
    {"name": "Team 2", "status": "Not Active"},
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [   
          Expanded(
            child: ListView.separated(
              separatorBuilder: (BuildContext context, int index) => Divider(
                color: Colors.black,
                endIndent: 10,
                indent: 10,
              ),
              itemCount: users.length,
              itemBuilder: (context, index) {
                // parsing the maps in the list before passing them into the extracted widget
                // the itemBuilder iterates through the list
                final name = users[index]['name'];
                final status = users[index]['status'];
                return CustomListTile(name: name, status: status);
              },
            ),
          ),
        ],
      ),
    );
  }
}

enter image description here

The problem is that you build the ListView in every loop. You can use loop to build a ListTile list and pass to ListView

BUT I suggest you to use ListView.separated, it is easier. like

  Widget listViewBuilder() {
    return Expanded(
        child: ListView.separated(
            itemCount: users.length,
            itemBuilder: (context, i) {
              return GestureDetector(
                onTap: () {
                  Navigator.pushNamed(
                    context,
                    '/chatscreen',
                  );
                },
                child: ListTile(
                    leading: CircleAvatar(
                        radius: 20,
                        backgroundImage:
                            AssetImage("assets/profileavatar.png")),
                    title: Text(users[i]['name']),
                    subtitle: Text("User -ID"),
                    trailing: Row(
                        mainAxisAlignment:
                            MainAxisAlignment.spaceBetween, // added line
                        mainAxisSize: MainAxisSize.min,
                        children: <Widget>[
                          Text(
                            users[i]['status'],
                            style: TextStyle(
                              color: Colors.green,
                              fontSize: 20,
                            ),
                          ),
                          SizedBox(
                            width: 20,
                          ),
                          InkWell(
                            onTap: () {
                              Navigator.pushNamed(
                                context,
                                '/viewlocation',
                              );
                            },
                            child: Icon(
                              Icons.add_location,
                              color: Colors.green,
                              size: 40,
                            ),
                          ),
                          SizedBox(
                            width: 20,
                          ),
                          Container(
                            decoration: BoxDecoration(
                                color: Colors.green,
                                border: Border.all(
                                  color: Colors.green,
                                ),
                                borderRadius:
                                    BorderRadius.all(Radius.circular(20))),
                            child: InkWell(
                              onTap: () async {
                                const number =
                                    '08592119XXXX'; //set the number here
                                bool res =
                                    await FlutterPhoneDirectCaller.callNumber(
                                        number);
                              },
                              child: Icon(
                                Icons.call,
                                color: Colors.white,
                                size: 40,
                              ),
                            ),
                          )
                        ])),
              );
            },
            separatorBuilder: (BuildContext context, int index) {
              return Divider(
                color: Colors.black,
                endIndent: 10,
                indent: 10,
              );
            }));
  }
Related