Flutter: How can I make a stream produce objects in periodic manner?

Advertisements

I have this function that aims at producing a Quote object after every 5 seconds.

Stream<Quote> quotesStream(Realm populatedRealm) async* {
  List<Quote> quotes = populatedRealm.all<Quote>().toList();

  for (Quote aQuote in quotes) {
    // Wait for 5 seconds before yielding the next quote.
    Future<void>.delayed(const Duration(seconds: 5));
    print(aQuote.quote); // Just to see what is going on.
    yield aQuote;
  }
}

It is then called in a StreamBuilder within a StatelessWidget like this.

class HomePage extends StatelessWidget {
  final Quote initialQuote = Quote('Nothing', 'none', '0', 'nothing');
  final String title;

  HomePage({super.key, required this.title});

  // The stream
  final Stream<Quote> newQuotesStream = quotesStream(RealmProvider.realm);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(title)),
      body: Center(
        child: BlocBuilder<QuotesBloc, QuotesState>(
          builder: (context, state) {
            if (state.quotesFetchingStatus == QuotesFetchingStatus.initial) {
              context.read<QuotesBloc>().add(const FetchingQuotes());
              return const CircularProgressIndicator();
            } else if (state.quotesFetchingStatus == QuotesFetchingStatus.success) {
              return StreamBuilder<Quote>(
                stream: newQuotesStream,
                builder: (context, snapshot) {
                  if (snapshot.hasData) {
                    Quote q = snapshot.data!;
                    return QuoteWidget(quote: q);
                  } else {
                    return QuoteWidget(quote: initialQuote);
                  }
                },
              );
            } else {
              return QuoteWidget(quote: initialQuote);
            }
          },
        ),
      ),
    );
  }
}

Apparently, all the quotes are streamed simultaneously; therefore, the QuoteWidget shows only the last quote.

What am I missing or doing wrong?

>Solution :

Adding await before Future<void>.delayed in quotesStream will fix your issue.

Stream<Quote> quotesStream(Realm populatedRealm) async* {
  List<Quote> quotes = populatedRealm.all<Quote>().toList();

  for (Quote aQuote in quotes) {
    await Future<void>.delayed(const Duration(seconds: 5));
    print(aQuote.quote);
    yield aQuote;
  }
}

Also, as @pskink said you can alternatively use Stream.periodic instead of Future.delayed like this:

Stream<Quote?> quotesStream(Realm populatedRealm) {
  List<Quote> quotes = populatedRealm.all<Quote>().toList();

  return Stream.periodic(const Duration(seconds: 5), (int index) {
    if (index < quotes.length) {
      Quote aQuote = quotes[index];

      print(aQuote.quote);
      return aQuote;
    } else {
      return null;
    }
  }).takeWhile((quote) => quote != null);
}

Leave a ReplyCancel reply