Sai Prashanth
Sai Prashanth

Reputation: 1915

List is Empty (Flutter)

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 ??

Upvotes: 0

Views: 2772

Answers (7)

Swaminathan V
Swaminathan V

Reputation: 4781

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);
   }
  ),
 }

Upvotes: 1

RusJaI
RusJaI

Reputation: 794

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"),

                              ),
                            );
                          },
                        );
                    

Upvotes: 0

Lucas Josino
Lucas Josino

Reputation: 835

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");
      },
    );

Upvotes: 0

KuKu
KuKu

Reputation: 7492

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);
}

Upvotes: 0

iDecode
iDecode

Reputation: 28876

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),
    );
  }
}

Upvotes: 0

BosS
BosS

Reputation: 500

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.

Upvotes: 0

Ashutosh Patole
Ashutosh Patole

Reputation: 980

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.

Upvotes: 0

Related Questions