Home > Blockchain >  Flutter Async: Future.wait() constantly returning null
Flutter Async: Future.wait() constantly returning null

Time:04-15

For some reason, Future.wait() is constantly returning null. I'm not completely certain I am using it correctly.

For context, I have a collection of posts in Firebase. For each post, I can extract the userID assigned to it, then for each post individually I use the userID of the poster to grab the username for display purposes. I grab the Post from a snapshot:

static Future<Post> fromSnapshot(QueryDocumentSnapshot<Object?> doc) async {
    final _documentId = doc.id;
    final _title = doc.get('title');
    final _text = doc.get('text');
    final _createdOn = doc.get('createdOn');
    final _userID = doc.get('userID');

    final userDoc = await FirebaseFirestore.instance.collection('users').doc(_userID).get();
    final username = userDoc.get("username");

    return Post(documentId: _documentId, title: _title, text: _text, createdOn: _createdOn, username: username);
  }

and the extraction of posts occurs in a getPosts() function elsewhere:

Future<List<Post>> getPosts() async {
    QuerySnapshot posts = await FirebaseFirestore.instance.collection('posts').get();

    final allData = posts.docs.map(
            (doc) async => await Post.fromSnapshot(doc)
    ).toList();

    print(allData);                             // [Instance of 'Future<Post>', Instance of 'Future<Post>', Instance of 'Future<Post>']

    final futurePosts = Future.wait(allData);
    print(futurePosts);                         // Instance of 'Future<List<Post>>'

    // why does this always return null?
    return futurePosts;
  }

the problem is it has to be async to extract the posts but also to get the username, meaning it returns a future list of future posts. I want to pass the result of getPosts() to a FutureBuilder, so I need a Future List of posts, and to not make all the posts Future I use Future.wait - but that always seems to return null. Essentially, I am mapping each post in the snapshot to its own Post item, where in the constructor it needs to run a further async call to extract the username. Am I missing something?

Note: even making the Future.wait() await returns null, it just also doesn't return a List of type Future so I can't use it in the FutureBuilder either.

Edit 1: It turns out that futurePosts is actually an Instance of 'Future<List<Post>>', but when accessing the data within the FutureBuilder, snapshot.data is null:

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: const Text('Feed'),
    ),
    body: FutureBuilder(
        future: getPosts(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            print(snapshot.data);
            return postsToColumn(context, snapshot.data as List<Post>);
          }
          return const Center(
            child: CircularProgressIndicator(),
          );
        }
    ),
  );
}

CodePudding user response:

Ok, lots of thanks to @IvoBeckers for helping me pin this down. It turns out, the snapshot actually did have an error, as they said, but this doesn't get printed unless you print it explicitly:

if (snapshot.hasError) {
  print(snapshot.error.toString());
}

And the error is

Bad state: cannot get a field on a DocumentSnapshotPlatform which does not exist

So it turns out that not every User has a corresponding entry in the users collection with its username, which sounds like something I should have checked before, but I thought such an error would be printed out in the console. Once I updated the users collection, it worked perfectly.

  • Related