2

The idea is to display a string from a random document within a collection in Firebase. A simple function getRandom() retrieves the total number of documents and generates a random integer r that is fed into the Firebase instance.

The output in the app is always null.

StreamBuilder(
        initialData: Words(),
          stream: getWords(),
          builder: (context, snapshot){

            if(!snapshot.hasData){
              return Center(child: Text("NO DATA"));
            }else {
              var r = snapshot.data;
              return Center(child: Text("${r.english}"));
            }
      })
Stream<Words> getWords() async* {
  int r = await getRandom();

  print("RANDOM NO: " + "$r");

  Firestore.instance.document("vocabs/foods/words/$r")
      .get()
      .then((snapshot){
    try {
      return Words().english;

    } catch(e){
      print("ERROR");
      return null;
    }
  });
}

class Words{

  Words(): super();

  String english;

  Words.fromSnapshot(DocumentSnapshot snapshot)
  : english = snapshot.data["english"];
}

Firestore DB part 1

Firestore DB part 2

2
  • For async generators, isn't the keyword yield or yield* instead of return ? Also, seems like the return type should be Stream<String>, since you are yielding the english string of your Word. Commented May 14, 2019 at 13:21
  • @DanielV. Stream<String indeed makes more sense. yield is not possible within the then - callback. It has to be return So far it still returns null, though. Commented May 14, 2019 at 13:48

1 Answer 1

1

I've constructed a this piece of sample code for you to give you some options to achieve what you'd like to do:

import 'dart:async';

class Word {    
  final String english;
  const Word(this.english);
}

Future<Iterable<Word>> get firebaseSnapshot async => [ Word('aWord'), Word('bWord'), Word('cWord') ];

Stream<String> getEnglishWords() async* {
  yield* await firebaseSnapshot.then((words) => Stream.fromIterable(words.map((w) => w.english)));
}

Stream<String> getEnglishWords2() async* {    
  final words = await firebaseSnapshot.then((words) => words.map((w) => w.english));
  yield* Stream.fromIterable(words);
}

Stream<String> getEnglishWords3() async* {    
  final snapshot = await firebaseSnapshot;
  for(final word in snapshot) {
    yield word.english;
  }  
}

main() async {
  await for(final englishWord in getEnglishWords()) {
    print(englishWord);
  }

  await for(final englishWord in getEnglishWords2()) {
    print(englishWord);
  }

  await for(final englishWord in getEnglishWords3()) {
    print(englishWord);
  }
}

Option No. 2 is the one I'd use. There is some significant performance consideration around it. I am scraping the back of my mind for the lecture around it... Nope, can't recall... If I find it, I'll update ya.

Sign up to request clarification or add additional context in comments.

4 Comments

First of all, thank you very much for taking the time. I am not as experienced, so I struggle a little bit with understanding how to implement your sample code. As I can see, there is no Firestore.instance that determines the path within my Firestore database. How do I add it to the getEnglishWords function? Also, I am unable to add the async tag to the main function. I assume, in the build Widget, there still needs to be a StreamBuilder but there is nowhere for me to add async. Again, thanks very much!
My sample code was to mimic how Firestore works. There is no instance in it. I recommend skipping the async generator and simplify your code for now. Another starter: gist.github.com/daniel-v/1fbe7697ae970cb98ab12eb1bed999b8 FutureBuilder class docs is really good. docs.flutter.io/flutter/widgets/FutureBuilder-class.html
I had to add my Words.fromSnapshot line in the Words class, so that I was able to use .fromSnapshot in the getWords function. However, it still returns a Words instead of a Future<String> hence why I'm getting an error in that function.
I checked it again, and I realised I just needed to return word.toString(). My mistake, it finally works now. I have been working on this for days! Thank you, Daniel. Appreciate your help!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.