How to load data for ListView.builder to build from provider's data

Viewed 4059

I want my app to create a ListView.builder with user's data that are fetched from backend API with provider and http package.

Provider's Method For Fetching data (Works fine now):

  Future<List<User>> fetchUsersList() async {
   var resp = await UserService().getAllUsers();
    if (resp.statusCode == 200) {
     return userListFromJson(resp.body);
    } else {
     decodeErrorMessage(resp);
     return _usersList = [];
   }
 }

My Code for Creating the ListView.builder:

class _StaffViewState extends State<StaffView> {
    bool _searchaMode = false;
    bool _fetchingData = false;
    List<User> _userList = List<User>();

@override
Widget build(BuildContext context) {

    Provider.of<UserProvider>(context).fetchUsersList().then(
     (data) => this._userList = data);
   

return Consumer<UserProvider>(
  builder: (context, providerData, _) => Scaffold(
    appBar: AppBar(
      title: _searchaMode == false
          ? Text('Staff\s List')
          : TextField(
              autofocus: true,
              cursorColor: Colors.white,
              onSubmitted: (String value) async {
                await showDialog<void>(
                  context: context,
                  builder: (BuildContext context) {
                    return AlertDialog(
                      title: const Text('Thanks!'),
                      content: Text('You typed "$value".'),
                      actions: <Widget>[
                        FlatButton(
                          onPressed: () {
                            Navigator.pop(context);
                          },
                          child: const Text('OK'),
                        ),
                      ],
                    );
                  },
                );
              },
              style: TextStyle(
                color: Colors.white,
              ),
              decoration: InputDecoration(
                hintText: 'Search . . .',
                hintStyle: TextStyle(color: Colors.white),
                border: InputBorder.none,
              ),
            ),
      actions: [
        _searchaMode == false
            ? IconButton(
                onPressed: () {
                  setState(() {
                    _searchaMode = true;
                  });
                },
                icon: Icon(Icons.search),
              )
            : IconButton(
                onPressed: () {
                  setState(() {
                    _searchaMode = false;
                  });
                },
                icon: Icon(
                  Icons.cancel,
                  color: Colors.tealAccent,
                ),
              ),
        RaisedButton.icon(
          onPressed: () async {},
          color: Colors.yellow.shade900,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.only(
              bottomLeft: Radius.circular(100.0),
              topLeft: Radius.circular(100.0),
            ),
          ),
          icon: Icon(
            Icons.add,
            size: 25.0,
            color: Colors.white,
          ),
          label: Text(
            'Add User',
            style: TextStyle(
              fontSize: 15.0,
              fontWeight: FontWeight.bold,
              color: Colors.white,
            ),
          ),
        ),
      ],
    ),
    drawer: AppSideNav(),
    body: _fetchingData == true
        ? pr.show()
        : ListView.builder(
            itemCount: _userList.length,
            itemBuilder: (context, index) {
              return Container(
                decoration: BoxDecoration(
                  border: Border(
                    bottom: BorderSide(
                      width: .9,
                      color: Colors.yellow.shade700,
                    ),
                  ),
                ),
                child: ListTile(
                  leading: Image.network(
                      providerData.getUsersList()[index].image != null
                          ? providerData.getUsersList()[index].image
                          : 'https://via.placeholder.com/150'),
                  title: Center(
                      child:
                          Text(providerData.getUsersList()[index].email)),
                  subtitle: Center(
                      child: Text(providerData.getUsersList()[index].nid)),
                  trailing:
                      providerData.getUsersList()[index].isAuthority == true
                          ? Text('Authority')
                          : providerData.getUsersList()[index].isStaff ==
                                  true
                              ? Text('Staff')
                              : Text('General User'),
                  contentPadding: EdgeInsets.symmetric(horizontal: 20.0),
                  visualDensity: VisualDensity.adaptivePlatformDensity,
                  isThreeLine: false,
                  onTap: () {},
                ),
              );
            },
          ),
       ),
     );
   }
 }

My Data is loading to build the list. But the problem is an error occurs that says setState() or markNeedsBuild() called during build.

enter image description here

My question is what is the actual way of populating data with the provider package where the dataFetching method will be executed first and than the list will be build.

3 Answers
@override
   void initState() {
     super.initState();

    use api here
  }

You shouldn't call the API inside build as you will have a new call each time the framework rebuilds the screen, instead call it inside initState().

If you need to wait for the API data, then use a FutureBuilder in your build function so it will rebuild automatically once the data has been retrieved.

Instead of calling your fetch function at the start of the build function or in the initState function, you can simply use the FutureBuilder widget. This way you can specify exactly what to show when it is still loading and what when finished, without adding more and more member variables.

Your modified code could then look like this:

class _StaffViewState extends State<StaffView> {
  
  bool _searchaMode = false;

  @override
  Widget build(BuildContext context) {
    return Consumer<UserProvider>(
      builder: (context, providerData, _) => FutureBuilder(
        future: providerData.fetchUsersList(),
        builder: (context, snapshot) {
          if (snapshot.connectionState != ConnectionState.done) {
            return Text("Loading...");
          }

          List<User> userList = snapshot.data;

          return Scaffold(
            appBar: AppBar(
              title: _searchaMode == false
                  ? Text('Staff\s List')
                  : TextField(
                      autofocus: true,
                      cursorColor: Colors.white,
                      onSubmitted: (String value) async {
                        await showDialog<void>(
                          context: context,
                          builder: (BuildContext context) {
                            return AlertDialog(
                              title: const Text('Thanks!'),
                              content: Text('You typed "$value".'),
                              actions: <Widget>[
                                FlatButton(
                                  onPressed: () {
                                    Navigator.pop(context);
                                  },
                                  child: const Text('OK'),
                                ),
                              ],
                            );
                          },
                        );
                      },
                      style: TextStyle(
                        color: Colors.white,
                      ),
                      decoration: InputDecoration(
                        hintText: 'Search . . .',
                        hintStyle: TextStyle(color: Colors.white),
                        border: InputBorder.none,
                      ),
                    ),
              actions: [
                _searchaMode == false
                    ? IconButton(
                        onPressed: () {
                          setState(() {
                            _searchaMode = true;
                          });
                        },
                        icon: Icon(Icons.search),
                      )
                    : IconButton(
                        onPressed: () {
                          setState(() {
                            _searchaMode = false;
                          });
                        },
                        icon: Icon(
                          Icons.cancel,
                          color: Colors.tealAccent,
                        ),
                      ),
                RaisedButton.icon(
                  onPressed: () async {},
                  color: Colors.yellow.shade900,
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.only(
                      bottomLeft: Radius.circular(100.0),
                      topLeft: Radius.circular(100.0),
                    ),
                  ),
                  icon: Icon(
                    Icons.add,
                    size: 25.0,
                    color: Colors.white,
                  ),
                  label: Text(
                    'Add User',
                    style: TextStyle(
                      fontSize: 15.0,
                      fontWeight: FontWeight.bold,
                      color: Colors.white,
                    ),
                  ),
                ),
              ],
            ),
            drawer: AppSideNav(),
            body: ListView.builder(
                    itemCount: userList.length,
                    itemBuilder: (context, index) {
                      return Container(
                        decoration: BoxDecoration(
                          border: Border(
                            bottom: BorderSide(
                              width: .9,
                              color: Colors.yellow.shade700,
                            ),
                          ),
                        ),
                        child: ListTile(
                          leading: Image.network(
                              providerData.getUsersList()[index].image != null
                                  ? providerData.getUsersList()[index].image
                                  : 'https://via.placeholder.com/150'),
                          title: Center(
                              child: Text(
                                  providerData.getUsersList()[index].email)),
                          subtitle: Center(
                              child:
                                  Text(providerData.getUsersList()[index].nid)),
                          trailing: providerData
                                      .getUsersList()[index]
                                      .isAuthority ==
                                  true
                              ? Text('Authority')
                              : providerData.getUsersList()[index].isStaff ==
                                      true
                                  ? Text('Staff')
                                  : Text('General User'),
                          contentPadding:
                              EdgeInsets.symmetric(horizontal: 20.0),
                          visualDensity: VisualDensity.adaptivePlatformDensity,
                          isThreeLine: false,
                          onTap: () {},
                        ),
                      );
                    },
                  ),
          );
        },
      ),
    );
  }
}

Related