riverpod_paging_utils 0.7.0 copy "riverpod_paging_utils: ^0.7.0" to clipboard
riverpod_paging_utils: ^0.7.0 copied to clipboard

Flutter/Riverpod pagination utilities. Easily build screens with loading/error states. Supports page, offset, and cursor-based pagination.

Riverpod Paging Utils #

A Flutter package that provides utilities for implementing pagination with Riverpod. It includes a generic PagingHelperView widget and mixins for page-based, offset-based, and cursor-based pagination.

Demo #

riverpod_paging_utils_sample riverpod_paging_utils_first_error riverpod_paging_utils_second_error

Features #

  • PagingHelperView: A generic widget that simplifies the implementation of pagination in Flutter apps.
  • PagePagingNotifierMixin: A mixin for implementing page-based pagination logic.
  • OffsetPagingNotifierMixin: A mixin for implementing offset-based pagination logic.
  • CursorPagingNotifierMixin: A mixin for implementing cursor-based pagination logic.

Getting Started #

Installation #

Add the following dependency to your pubspec.yaml file:

dependencies:
  riverpod_paging_utils: ^{latest}

Then, run flutter pub get to install the package.

Usage #

Here's a simple example of how to use the PagingHelperView widget with a Riverpod provider that uses the CursorPagingNotifierMixin:

/// A Riverpod provider that mixes in [CursorPagingNotifierMixin].
/// This provider handles the pagination logic for fetching [SampleItem] data using cursor-based pagination.
@riverpod
class SampleNotifier extends _$SampleNotifier
    with CursorPagingNotifierMixin<SampleItem> {
  /// Builds the initial state of the provider by fetching data with a null cursor.
  @override
  Future<CursorPagingData<SampleItem>> build() => fetch(cursor: null);

  /// Fetches paginated data from the [SampleRepository] based on the provided [cursor].
  /// Returns a [CursorPagingData] object containing the fetched items, a flag indicating whether more data is available,
  /// and the next cursor for fetching the next page.
  @override
  Future<CursorPagingData<SampleItem>> fetch({
    required String? cursor,
  }) async {
    final repository = ref.read(sampleRepositoryProvider);
    final (items, nextCursor) = await repository.getByCursor(cursor);
    final hasMore = nextCursor != null && nextCursor.isNotEmpty;

    return CursorPagingData(
      items: items,
      hasMore: hasMore,
      nextCursor: nextCursor,
    );
  }
}

/// A sample page that demonstrates the usage of [PagingHelperView] with the [SampleNotifier] provider.
class SampleScreen extends StatelessWidget {
  const SampleScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample Screen'),
      ),
      body: PagingHelperView(
        provider: sampleNotifierProvider,
        futureRefreshable: sampleNotifierProvider.future,
        notifierRefreshable: sampleNotifierProvider.notifier,
        contentBuilder: (data, widgetCount, endItemView) => ListView.builder(
          itemCount: widgetCount,
          itemBuilder: (context, index) {
            // if the index is last, then
            // return the end item view.
            if (index == widgetCount - 1) {
              return endItemView;
            }

            // Otherwise, build a list tile for each sample item.
            return ListTile(
              title: Text(data.items[index].name),
              subtitle: Text(data.items[index].id),
            );
          },
        ),
      ),
    );
  }
}

UI Customization #

Basic Customization #

You can easily customize the appearance of loading and error states using ThemeExtension.

riverpod_paging_utils_sample

For example, if you're using the loading_animation_widget package, you can set up your code like this:

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        extensions: [
          PagingHelperViewTheme(
            loadingViewBuilder: (context) => Padding(
              padding: const EdgeInsets.all(16),
              child: Align(
                alignment: Alignment.topCenter,
                child: LoadingAnimationWidget.horizontalRotatingDots(
                  color: Colors.red,
                  size: 100,
                ),
              ),
            ),
            endLoadingViewBuilder: (context) =>
                LoadingAnimationWidget.threeArchedCircle(
              color: Colors.red,
              size: 50,
            ),
          ),
        ],
      ),
      home: const SampleScreen(),
    );
  }
}

A complete sample implementation can be found in the example/lib/main2.dart file.

Advanced Customization #

Customizing the appearance of RefreshIndicators requires a bit more setup.

1. Theme Configuration

First, adjust your PagingHelperViewTheme like this:

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        extensions: [
          PagingHelperViewTheme(
            // disable pull-to-refresh
            enableRefreshIndicator: false,
          ),
        ],
      ),
      home: const SampleScreen(),
    );
  }
}

2. Integrating Custom Refresh (e.g., with easy_refresh)

If you're using a package like easy_refresh to provide a RefreshIndicator, modify your screen code as follows:

class SampleScreen extends ConsumerWidget {
  const SampleScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Advanced UI Customization'),
      ),
      body: PagingHelperView(
        provider: sampleNotifierProvider,
        futureRefreshable: sampleNotifierProvider.future,
        notifierRefreshable: sampleNotifierProvider.notifier,
        contentBuilder: (data, widgetCount, endItemView) {
          // Use EasyRefresh alternative to RefreshIndicator
          return EasyRefresh(
            onRefresh: () async => ref.refresh(sampleNotifierProvider.future),
            child: ListView.builder(
              itemCount: widgetCount,
              itemBuilder: (context, index) {
                // if the index is last, then
                // return the end item view.
                if (index == widgetCount - 1) {
                  return endItemView;
                }

                // Otherwise, build a list tile for each sample item.
                return ListTile(
                  title: Text(data.items[index].name),
                  subtitle: Text(data.items[index].id),
                );
              },
            ),
          );
        },
      ),
    );
  }
}

A complete sample implementation can be found in the example/lib/main3.dart file.

8
likes
150
points
262
downloads

Publisher

verified publisherk9i.app

Weekly Downloads

Flutter/Riverpod pagination utilities. Easily build screens with loading/error states. Supports page, offset, and cursor-based pagination.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_riverpod, freezed_annotation, visibility_detector

More

Packages that depend on riverpod_paging_utils