FadyFayezYounan / easy_infinite_pagination

A simple and customizable infinite pagination package for Flutter applications. The package provides a simple and customizable way to implement infinite pagination in your Flutter applications.
MIT License
3 stars 2 forks source link

Easy Infinite Pagination

Pub Pub likes Pub popularity Pub points

A simple and customizable infinite pagination package for Flutter applications. The Easy Infinite Pagination package makes it easy to add infinite pagination to your apps. With just a few lines of code, you can create beautiful and efficient paginated lists, grids, and page views.

Features

Getting started

Import the following package in your dart file

import 'package:easy_infinite_pagination/easy_infinite_pagination.dart';

Usage

class SimpleExample extends StatefulWidget {
  const SimpleExample({super.key});

  @override
  State<SimpleExample> createState() => _SimpleExampleState();
}

class _SimpleExampleState extends State<SimpleExample> {
  List<String> _items = [];
  bool _isLoading = true;

  void _fetchData() async {
    setState(() {
      _isLoading = true;
    });
    await Future.delayed(const Duration(seconds: 3));
    setState(() {
      _isLoading = false;
      // Add 20 more items to the list. This could be a network request, a database query, etc.
      _items = List.generate(_items.length + 20, (i) => 'Item ${i + 1}');
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Simple Example'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: InfiniteListView(
        delegate: PaginationDelegate(
          itemCount: _items.length,
          itemBuilder: (_, index) => ListTile(
            title: Text(_items[index]),
          ),
          isLoading: _isLoading,
          onFetchData: _fetchData,
        ),
      ),
    );
  }
}

Layouts Supported by easy_infinite_pagination

Layout Easy Infinite Pagination
ListView.builder InfiniteListView
ListView.separated InfiniteListView.separated
SliverList.builder SliverInfiniteListView
SliverList.separated SliverInfiniteListView.separated
SliverFixedExtentList SliverInfiniteListView (just provide itemExtent parameter)
SliverPrototypeExtentList SliverInfiniteListView (just provide prototypeItem parameter)
GridView.builder InfiniteGridView
SliverGrid.builder SliverInfiniteGridView
PageView.builder InfinitePageView

Customizable Indicators

InfiniteListView(
  delegate: PaginationDelegate(
    itemCount: _items.length,
    itemBuilder: (context, index) => ListTile(
      title: DefaultListTile(index: index),
    ),
    isLoading: _isLoading,
    onFetchData: _fetchData,
    firstPageLoadingBuilder: (context) =>
        const FirstPageLoadingIndicator(),
    firstPageErrorBuilder: (context, onRetry) => FirstPageErrorIndicator(
      onRetry: onRetry,
    ),
    firstPageNoItemsBuilder: (context) =>
        const FirstPageNoItemsFoundedIndicator(),
    loadMoreLoadingBuilder: (context) => const LoadMoreLoadingIndicator(),
    loadMoreErrorBuilder: (context, onRetry) => LoadMoreErrorIndicator(
      onRetry: onRetry,
    ),
    loadMoreNoMoreItemsBuilder: (context) =>
        const LoadMoreNoMoreItemsIndicator(),
    ),
  ),

Control when to fetch data

InfiniteListView(
  delegate: PaginationDelegate(
    // The number of remaining invisible items that should trigger a new fetch.
    // The default value is 3.
    invisibleItemsThreshold: 5,
    // If true, it will fetch data on start.
    fetchDataOnStart: true,
    ....
    ),
  )

How to use easy_infinite_pagination with Bloc

InfiniteListView.separated(
  delegate: PaginationDelegate(
    isLoading: state is PostsFetchLoading,
    hasError: state is PostsFetchError,
    hasReachedMax: context.read<PostsListCubit>().hasReachedMax,
    // The number of remaining invisible items that should trigger a new fetch.
    // The default value is 3.
    invisibleItemsThreshold: 5,
    itemCount: posts.length,
    itemBuilder: (context, index) {
    final post = posts[index];
    return PostWidget(post: post);
    },
    // here we add a custom error screen if the state is an error state.
    // and this screen will be shown if an error occurs while fetching data for the first page.
    firstPageErrorBuilder: state is PostsFetchError
      ? (context, onRetry) =>
      CustomErrorScreen(errorMessage: state.message, onRetry: onRetry)
      : null,
    // this method will be called when the user reaches the end of the list or for the first page.
    onFetchData: () async {
      await context.read<PostsListCubit>().fetchPosts();
      },
    ),
    separatorBuilder: (context, index) => const Divider(),
  )

How to use easy_infinite_pagination with Sliver

SliverPadding(
  padding: const EdgeInsets.all(16.0),
  sliver: SliverInfiniteGridView(
    gridDelegate:
      const SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 2,
    crossAxisSpacing: 10.0,
    mainAxisSpacing: 10.0,
  ),
  delegate: PaginationDelegate(
    isLoading: state.isLoading,
    hasError: state.hasError,
    hasReachedMax: state.hasReachedMax,
    // The number of remaining invisible items that should trigger a new fetch.
    // The default value is 3.
    invisibleItemsThreshold: 5,
    itemCount: state.posts.length,
    itemBuilder: (context, index) {
      final post = state.posts[index];
      return PostGridWidget(post: post);
    },
    // this method will be called when the user reaches the end of the list or for the first page.
    onFetchData: () async {
      await context.read<PostsGridCubit>().fetchPosts();
      },
    ),
  ),
)

How to use InfinitePageView

InfinitePageView(
  scrollDirection: Axis.vertical,
  delegate: PaginationDelegate(
  itemCount: _items.length,
  itemBuilder: (_, index) => ListTile(
    title: PageViewItem(index: index),
  ),
  isLoading: _isLoading,
  invisibleItemsThreshold: 1,
  onFetchData: _fetchData,
 ),
),

Support Searching, Sorting and Filtering with easy_infinite_pagination

Support Custom Pagination Layouts

return PaginationLayoutBuilder(
  // Provider the layout strategy (box or sliver).
  // In this case we used the box strategy.
  // because the child not sliver widget.
  layoutStrategy: LayoutStrategy.box,
  delegate: paginationDelegate,
  useShrinkWrapForFirstPageIndicators: _useShrinkWrapForFirstPageIndicators,
  layoutChildBuilder: (
    context,
    itemBuilder,
    itemCount,
    bottomLoaderBuilder,
    ) =>
        MasonryGridView.custom(
      gridDelegate: gridDelegate,
      mainAxisSpacing: mainAxisSpacing,
      crossAxisSpacing: crossAxisSpacing,
      padding: padding,
      // Here we create a predefined sliver delegate by the package
      // this delegate is used to handle the bottom loader widget while loading, success and error state.
      childrenDelegate: PaginationLayoutBuilder.createSliverChildDelegate(
        builder: itemBuilder,
        childCount: itemCount,
        bottomLoaderBuilder: bottomLoaderBuilder,
        addAutomaticKeepAlives: addAutomaticKeepAlives,
        addRepaintBoundaries: addRepaintBoundaries,
        addSemanticIndexes: addSemanticIndexes,
      ),
    ),
  )

Additional information

We hope you find the Easy Infinite Pagination package helpful!