Skip to content

Add pagination or load more or infinite scrolling functionality to your flutter list view with ease.

License

Notifications You must be signed in to change notification settings

xsahil03x/super_paging

Repository files navigation

Super Paging

Open Source Love License Dart CI Version

Simplify Flutter pagination with Super Paging — A super package providing efficient data management, in-memory caching, configurable widgets and built-in error handling for a seamless pagination experience.

Live Demo: https://xsahil03x.github.io/super_paging

Show some ❤️ and star the repo to support the project

An animated image of the SuperPaging

Features

  • In-memory Caching: Intelligently caches frequently accessed data, minimizing network requests and significantly improving performance.
  • Configurable Widgets: Customize the look and feel of your pagination with a variety of built-in widgets, like PagingListView and BidirectionalPagingListView.
  • Error Handling: Gracefully handles potential errors during data loading and retrieval, ensuring a smooth user experience even in unexpected situations.
  • Flexible Configuration: Tailor Pager's behavior to your specific needs by adjusting parameters like initial keys, page sizes, and caching strategies.
  • Extensive Documentation: Clear and comprehensive documentation, making it easy for developers of all levels to understand and implement effectively.

Installation

Add the following to your pubspec.yaml and replace [version] with the latest version:

dependencies:
  super_paging: ^[version]

Usage

To get started, import the package:

import 'package:super_paging/super_paging.dart';

Create a PagingSource:

class RickAndMortySource extends PagingSource<int, Character> {
  final RickAndMortyApi api;

  RickAndMortySource({required this.api});

  @override
  Future<LoadResult<int, Character>> load(LoadParams<int> params) async {
    try {
      final characters = await api.getCharacters(page: key);
      return LoadResult.page(nextKey: key + 1, items: characters);
    } catch (e) {
      return LoadResult.error(e);
    }
  }
}

Create a Pager Instance:

final pager = Pager(
  initialKey: 1,
  config: const PagingConfig(pageSize: 20, initialLoadSize: 60),
  pagingSourceFactory: () => RickAndMortySource(api: RickAndMortyApi()),
);

Integrate with UI:

PagingListView(
  pager: pager,
  itemBuilder: (BuildContext context, int index) {
    final item = pager.items.elementAt(index);
    return ListTile(
      title: Text(item.name),
      subtitle: Text(item.species),
      trailing: Text('# ${item.id}'),
      leading: CircleAvatar(backgroundImage: NetworkImage(item.image)),
    );
  },
  emptyBuilder: (BuildContext context) {
    return const Center(
      child: Text('No characters found'),
    );
  },
  errorBuilder: (BuildContext context, Object? error) {
    return Center(child: Text('$error'));
  },
  loadingBuilder: (BuildContext context) {
    return const Center(
      child: CircularProgressIndicator.adaptive(),
    );
  },
);

Handle Refresh and Retry:

// Refresh the data
await pager.refresh();

// Retry failed loads
await pager.retry();

Dispose Resources:

@override
void dispose() {
  pager.dispose();
  super.dispose();
}

Customization

Pager

Pager(
  // Defines the initial key to use for the first load.
  //
  // By default, the initial key is null.
  initialKey: null,

  // Configuration for pagination behavior.
  config: PagingConfig(
    // Defines the number of items to load in a single page.
    pageSize: 20,
    
    // Defines the number of items to load in the first page.
    //
    // By default, the initial load size is 3 times the page size.
    initialLoadSize: 60,
    
    // Defines how far from the edge of loaded content an access
    // must be to trigger further loading.
    //
    // By default, the pager will start loading when the user scrolls
    // within 3 items of the end of the loaded content.
    //
    // If set to null, the pager will not start loading more content until
    // they are specifically requested by the user.
    prefetchIndex: 3,
    
    // @Experimental
    //
    // Defines the maximum number of items to keep in memory before
    // pages should be dropped.
    //
    // If set to null (Default), pages will never be dropped.
    maxSize: null,
  ),
  
  // Defines the initial state to use for the first load.
  //
  // By default, [PagingState.initial()] state is used.
  initialState: PagingState.initial(),

  // Defines the source from which to load the paginated data.
  pagingSourceFactory: () => MyPagingSource(),
);

PagingListView

PagingListView<int, String>(
  // Defines the pager to use for loading data.
  pager: myPager,

  // Defines the builder that is called to build items in the ListView.
  //
  // The builder is called once for each item in the list.
  itemBuilder: (context, index) {
    final item = myPager.valueList.elementAt(index);
    return ListTile(
      title: Text(item),
    );
  },

  // Defines the builder that is called to build the empty state of the list.
  emptyBuilder: (context) {
    return const Center(
      child: Text('No items found'),
    );
  },

  // Defines the builder that is called to build the error state of the list.
  //
  // [error] is the error that caused the state to be built.
  errorBuilder: (context, error) {
    return Center(child: Text('Error: $error'));
  },

  // Defines the builder that is called to build the loading state of the list.
  loadingBuilder: (context) {
    return const Center(
      child: CircularProgressIndicator.adaptive(),
    );
  },

  // Defines the builder that is called to build the prepend state of the list.
  //
  // [state] is the current state of the pager and [pager] is the pager instance.
  //
  // Optional. If not provided, the list will show the default prepend loading state.
  prependStateBuilder: (context, state, pager) {
    // Return a widget based on the state. For example, a button to load the previous page.
    // Use the [pager] instance to call [pager.load()] or [pager.retry] based on the state.
  },

  // Defines the builder that is called to build the append state of the list.
  //
  // [state] is the current state of the pager and [pager] is the pager instance.
  //
  // Optional. If not provided, the list will show the default append loading state.
  appendStateBuilder: (context, state, pager) {
    // Return a widget based on the state. For example, a button to load the next page.
    // Use the [pager] instance to call [pager.load()] or [pager.retry] based on the state.
  },
);

License

MIT License