offset_iterator

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

Example

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();
print(firstPage.toNullable());

// 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;

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

Libraries

offset_iterator