juice_paging

Paged / infinite-scroll list state as a generic Juice bloc, behind a cursor-based fetcher seam.

pub package License: MIT

What it owns

The list of loaded items + load status (first page, loading-more, end, error). It does not own the transport — you supply a PageFetcher.

Install

dependencies:
  juice_paging: ^0.1.0

Use

final feed = PagingBloc<Post>.withConfig(PagingConfig(
  fetcher: (cursor) async {
    final page = await api.posts(after: cursor as String?);
    return PageResult(page.items, nextCursor: page.next);  // null next => end
  },
));

feed.loadMore();   // append next page
feed.refresh();    // reload from the first page
feed.retry();      // retry the failed load

The cursor is opaque — return an offset, a token, a timestamp, whatever your backend uses. nextCursor: null means there are no more pages.

Infinite scroll

NotificationListener<ScrollNotification>(
  onNotification: (n) {
    if (n.metrics.pixels >= n.metrics.maxScrollExtent - 300) feed.loadMore();
    return false;
  },
  child: ListView.builder(...),
)

loadMore is guarded — it no-ops while a load is in flight or at the end, so firing it on every scroll frame is safe.

State

Field / getter Meaning
items loaded items (List<T>)
status initial / loadingFirst / loaded / loadingMore / end / error
nextCursor cursor for the next page
error last load error (items are kept)
hasMore / isLoadingMore / isEmpty derived

A failed loadMore keeps items + cursor; retry() resumes the next page. A failed first page → error with an empty list; retry() reloads it.

Rebuild groups: paging:items, paging:status.

License

MIT License — see LICENSE.

Libraries

juice_paging
Paged / infinite-scroll list state as a generic Juice bloc, behind a cursor-based fetcher seam.