OffsetIterator is an offset-based async iterable for true pull-based data streaming. Inspired by Apache Kafka.

Great for:

  • IO based streaming
  • Paginated API calls


Here is an example straight from the tests, that fetches up to 5 pages from a fake API. You can view the test case here.

Note that it will only call fetchPage once, as we only request one page.

Future<String> fetchPage(int page) async {
  print('Fetching page $page!');
  await Future.delayed(const Duration(milliseconds: 50));
  return 'page content $page';

final i = OffsetIterator(
  init: () => 1, // Start from page 1
  process: (nextPage) async {
    final pageContent = await fetchPage(nextPage);
    return OffsetIteratorState(
      acc: nextPage + 1, // Set the next accumulator / cursor
      chunk: [pageContent], // Add the page content
      hasMore: nextPage < 5, // We only want 5 pages

final firstPage = await i.pull();

// In the logs:
// "Fetching page 1!"
// "page content 1"

Usage with Flutter

A offset_iterator_builder package is available.

This package can be used to build widgets from an OffsetIterator's value. The builder function is given the current BuildContext, the latest value from the iterator and a pull function for requesting more data.

The builder provides a withValue helper function to make using the state easier!

class MyList extends StatelessWidget {
    MyList({Key? key, required this.iterator}) : super(key: key);

    final OffsetIterator<List<int>> iterator;

    Widget build(BuildContext context) => OffsetIteratorBuilder(
        iterator: iterator,
        builder: (context, value, pull) => withValue(value)(
            data: (listOfNumbers, hasMore) => Text('I have ${listOfNumbers.length} numbers!'),
            loading: () => Text('Loading...'),
            error: (err) => Text('Error: $err'),