0

I am creating a List in Flutter and displaying it in a Column, When I Run it is just Empty and when I print the list it just prints an Array

I/flutter (24613): []

I am using this code to create the List:-

 myFunction() {
  return StreamBuilder(
  stream:
      users.orderBy('timestamp', descending: true).limit(30).snapshots(),
builder: (context, snapshot) {
 List<UserList> usersList = [];
    snapshot.data.documents.forEach((doc) {
     User user = User.fromDocument(doc);
       UserList userList = UserList(user);
       usersList.add(userList);
    });
    return Column (children: usersList);
   }
  ),
 }

This is My User Class:-

class User {

final String id;
final String username;
final String email;
final String photoUrl;


User({
  this.id,
  this.username,
  this.email,
  this.photoUrl,
});

factory User.fromDocument(DocumentSnapshot doc) {
   return User(
   id: doc.data()['id'],
   username: doc.data()['username'],
   email: doc.data()['email'],
   photoUrl: doc.data()['photoUrl'],

    );
  }
}
  

The Code Is Showing No Errors and the Column Is not Displaying, Also When I print The length of the List it Shows it is Zero:-

I/flutter (24613): 0
 

What Could be The problem ??

Sai Prashanth
  • 1,621
  • 2
  • 6
  • 21

7 Answers7

1

I guess we need to tweak some of your code little bit to make the logic working. :)

builder param should be specified with Type otherwise it will be of type dynamic. To be in safer side in this case it will be QuerySnapshot. So,

builder: (context, snapshot) in your code becomes

builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot).

Next, there is no need of looping through foreach and instead you can try something like below.

snapshot.data.docs.map((document) {     ....    }

snapshot.data.documents in your code is not valid way of getting the Firestore Documents. Please refer official doc

And you need to return a widget from builder which you have done correctly. But, by mistake you are passing the List<UserList> to Column which will be expecting List<Widget>

return Column (children: usersList);

Here I can see you are passing usersList which is of type List<UserList>. So you can replace Column with ListView or similar kind of other widget since, Column doesn't support scroll.

So combining all these bits and pieces you will get the below snippet.

return StreamBuilder(
  stream: FirebaseFirestore.instance
      .collection('users')
      .orderBy('timestamp', descending: true)
      .limit(30)
      .snapshots(),  // Just for simplicity. 
  builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
    //When there is no data returned from the firebase.
    if (!snapshot.hasData) {
      return Center(
        child: CircularProgressIndicator(),
      );
    }
    return ListView(
      children: snapshot.data.docs.map((document) {
        return Text("Title: " + document['username']);
      }).toList(),
    );
  },
);  

For simplicity, I have returned the Text widget. You can implement your own UI there.

NOTE : This is the basic working example and you need to fine tune accordingly like using model classes instead of directly accessing based on your requirements.

Your Code

 myFunction() {
  return StreamBuilder(
  stream:
      users.orderBy('timestamp', descending: true).limit(30).snapshots(),
builder: (context, snapshot) {
 List<UserList> usersList = [];
    snapshot.data.documents.forEach((doc) {
     User user = User.fromDocument(doc);
       UserList userList = UserList(user);
       usersList.add(userList);
    });
    return Column (children: usersList);
   }
  ),
 }
Swaminathan V
  • 4,663
  • 2
  • 22
  • 32
  • I Tried This and it still doesn't work, also when I Print snapshot I get the message "AsyncSnapshot(ConnectionState.active, Instance of 'QuerySnapshot', null, null)" – Sai Prashanth Jul 16 '21 at 15:44
  • You are supposed to print the `snapshot.data` to know the data coming in `Instance of 'QuerySnapshot'`. Even then if data is not returned, please check the collection properties of `Firebase` you are accessing to get the `userList`. – Swaminathan V Jul 17 '21 at 06:08
  • I tried Printing snapshot.data and I got the message "Instance of 'QuerySnapshot'." But the List is still not Showing..what am I doing wrong ?? – Sai Prashanth Jul 17 '21 at 20:28
  • 1
    Instead of UI. try to use `async`/`await` to get the data from firebase in a seperate function and see if that is working. You can refer this tutorial as well https://medium.com/quick-code/reading-lists-from-firestore-using-streambuilder-in-flutter-eda590f461ed – Swaminathan V Jul 18 '21 at 03:13
  • 1
    I think I solved the issue after reading the medium tutorial I think I had a problem with the orderBy method I removed it and it worked, Thanks for the Help... – Sai Prashanth Jul 18 '21 at 03:37
0

It is because you have to await for the json to actually get parse to the dart model. Second thing is forEach method is synchronous it doesn't wait for the async operation to complete, this is the reason why your list is empty.

This SO question has lot of different ways to make a list work asynchronously in flutter.

Ashutosh Patole
  • 926
  • 1
  • 7
  • 23
0

Column shows data before fetching data, so it shows empty list. For this use setstate according to your state management type ("notifylisteners" in provider) after getting data, so by this the screen will be updated and column also shows the updated list.

Dharman
  • 30,962
  • 25
  • 85
  • 135
BosS
  • 447
  • 2
  • 9
0

As 'Ashutosh patole' said, 'forEach' method does not wait iteration's complete.

I think that because of this reason, although you made a 'usersList',
there is no data when build widget in 'usersList'.

To fix this, you'd better change from 'forEach' to 'for'.

void main() async {
  List<String> data = [ 'a', 'b', 'c'];
  List<String> result = [];
  data.forEach((data) async {
    await Future.delayed(Duration(seconds: 1));
    result.add(data);
    });
  print(result);
  await Future.delayed(Duration(seconds: 3));
  print(result);
  
  print('-----------------');
  
  result = [];
  for (var item in data) {
    await Future.delayed(Duration(seconds: 1));
    result.add(item);
  }
  print(result);
  await Future.delayed(Duration(seconds: 3));
  print(result);
}

enter image description here

In your code, you can change like below.

List<UserList> usersList = [];
for (var doc in snapshot.data.documents) {
    User user = User.fromDocument(doc);
    UserList userList = UserList(user);
    usersList.add(userList);
}
KuKu
  • 6,654
  • 1
  • 13
  • 25
0

I'm not very sure how you're handling the scope of the variable. Here's my minimal reproducible code which can give you some idea on how to add the items to the list.

class MyPage extends StatefulWidget {
  @override
  _MyPageState createState() => _MyPageState();
}

class _MyPageState extends State<MyPage> {
  final List<Widget> _list = [FlutterLogo()];

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

    Timer.periodic(Duration(seconds: 1), (timer) {
      if (timer.tick >= 2) timer.cancel();
      setState(() => _list.add(FlutterLogo()));
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(children: _list),
    );
  }
}
iDecode
  • 22,623
  • 19
  • 99
  • 186
0

Before calling the data, check all fields:

Firestore Docs

Add a print() to see where the problem

FutureBuilder<DocumentSnapshot>(
      future: users.doc(documentId).get(),
      builder:
          (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {

        //This
        if (snapshot.hasError) {
          return Text("Something went wrong");
        }

        print(snapshot.data);

        //This
        if (snapshot.hasData && !snapshot.data!.exists) {
          return Text("Document does not exist");
        }

        print(snapshot.data);

        //This
        if (snapshot.connectionState == ConnectionState.done) {
          Map<String, dynamic> data = snapshot.data!.data() as Map<String, dynamic>;
          return Text("Full Name: ${data['full_name']} ${data['last_name']}");
        }

        return Text("loading");
      },
    );
Lucas Josino
  • 820
  • 1
  • 4
  • 14
0

This is what i typically use.Try out this! Please balance the brackets in the code

    FutureBuilder(
                    future: users.orderBy('timestamp', descending: true).limit(30),
                    builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
                      List<User>ulist=snapshot.data;
                        return ListView.builder(
                          shrinkWrap: true,
                          padding: EdgeInsets.only(top: 25,bottom: 35),
                          itemCount: evlist==null?0:evlist.length,
                          itemBuilder: (BuildContext context, int index) {
                            String evtime=evlist[index].fromdate.substring(11,16);
                            String ontime=evlist[index].fromdate.substring(0,16);
                            return Container(
                              decoration: BoxDecoration(
                                  border: Border.all(width: 1.8,color: Colors.indigo[900]),
                                  borderRadius: BorderRadius.circular(12.0),
                                  color: Colors.grey[200]
                              ),
                              margin:
                              const EdgeInsets.symmetric(horizontal: 18.0, vertical: 4.0),
                              child: ListTile(
                                leading: Icon(Icons.notifications),
                                title: Text(ulist[index].username.toString()),
                                subtitle:Text("next data"),

                              ),
                            );
                          },
                        );
                    
RusJaI
  • 666
  • 1
  • 7
  • 28