mo_infinite_scroll 1.0.1
mo_infinite_scroll: ^1.0.1 copied to clipboard
A simple and easy-to-use infinite scroll package for Flutter. Supports pull-to-refresh, pre-fetch before reaching the end, a Sliver variant for custom scroll views, and optional placeholder widgets fo [...]
mo_infinite_scroll #
A simple, easy-to-use infinite scroll package for Flutter with minimal required parameters, pull-to-refresh, smart pre-fetching, and full placeholder support.
Features #
- ✅ Minimal required params — just
fetcheranditemBuilder - 🔄 Pull-to-refresh built in (standalone variant)
- ⚡ Pre-fetch the next page before the user reaches the end
- 🛑 Loop prevention — skips fetch when list is too short or already loading
- 📦 Sliver variant — drop into any
CustomScrollView, let the parent control refresh - 🎨 Placeholder widgets — loading, empty, and error states, all overridable
- 🎛️ External controller — trigger
refresh()from outside the widget
Getting started #
Add to your pubspec.yaml:
dependencies:
mo_infinite_scroll: ^1.0.1
Usage #
Standalone list (with pull-to-refresh) #
import 'package:mo_infinite_scroll/mo_infinite_scroll.dart';
MoInfiniteScroll<Post>(
fetcher: (page, limit) => myApi.getPosts(page: page, limit: limit),
itemBuilder: (context, post) => PostCard(post: post),
)
With all options #
MoInfiniteScroll<Post>(
fetcher: (page, limit) => myApi.getPosts(page: page, limit: limit),
itemBuilder: (context, post) => PostCard(post: post),
limit: 20, // items per page, passed to your API
prefetchOffset: 3, // pre-fetch when 3 items from the end
loadingPlaceholder: MyShimmer(), // first-load skeleton
emptyPlaceholder: MyEmptyState(), // zero items
errorPlaceholder: MyErrorState(), // fetch error
loadingMoreIndicator: MySpinner(), // bottom-of-list spinner
separatorBuilder: (_, __) => const Divider(),
padding: const EdgeInsets.all(16),
)
External controller (refresh from an AppBar button) #
final _controller = MoInfiniteScrollController<Post>();
// In build():
Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () => _controller.refresh(fetcher, limit),
),
],
),
body: MoInfiniteScroll<Post>(
controller: _controller,
fetcher: fetcher,
itemBuilder: (context, post) => PostCard(post: post),
),
)
Sliver variant (inside a CustomScrollView) #
final _controller = MoInfiniteScrollController<Post>();
RefreshIndicator(
onRefresh: () => _controller.refresh(
(page, limit) => myApi.getPosts(page: page, limit: limit),
20,
),
child: CustomScrollView(
slivers: [
const SliverAppBar(title: Text('Posts'), floating: true),
MoInfiniteScrollSliver<Post>(
controller: _controller,
fetcher: (page, limit) => myApi.getPosts(page: page, limit: limit),
itemBuilder: (context, post) => PostCard(post: post),
),
],
),
)
Parameters #
Shared by both widgets #
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
fetcher |
PageFetcher<T> |
✅ | — | Called for each page. Receives (page, limit). Return [] or fewer items than limit to signal end. |
itemBuilder |
Widget Function(context, T) |
✅ | — | Builds a single list item. |
limit |
int |
❌ | 20 |
Items per page, forwarded to fetcher. |
prefetchOffset |
int |
❌ | 3 |
Start pre-fetching when this many items remain. |
controller |
MoInfiniteScrollController<T> |
❌ | internal | External controller to call refresh() from outside. |
loadingPlaceholder |
Widget |
❌ | spinner | Shown during the first fetch. |
emptyPlaceholder |
Widget |
❌ | inbox icon | Shown when the list is empty. |
errorPlaceholder |
Widget |
❌ | error icon + retry | Shown on fetch error. |
loadingMoreIndicator |
Widget |
❌ | spinner | Shown at the bottom while loading more. |
separatorBuilder |
Widget Function(context, index) |
❌ | none | Optional separator between items. |
MoInfiniteScroll only #
| Parameter | Type | Default | Description |
|---|---|---|---|
padding |
EdgeInsetsGeometry |
none | Padding for the internal ListView. |
physics |
ScrollPhysics |
default | Physics for the internal ListView. |
How pre-fetching & loop prevention work #
- The next page is fetched when the user scrolls within
prefetchOffsetitems of the end. - If the current page returned fewer items than
limit,hasReachedEndis set totrueand no further fetches are made. - Concurrent fetches are blocked — if a fetch is already in progress, new calls to
fetchNextPageare ignored. - On error, further fetches are blocked until the user retries.
License #
MIT